Type_Challenges
189. Easy Await
If we have a type which is wrapped type like Promise. How we can get a type which is inside the wrapped type? For example if we have Promise<ExampleType> how to get ExampleType?
type X = Promise<string>
type Y = Promise<{ field: number }>
type Z = Promise<Promise<string | number>>
type cases = [
Expect<Equal<MyAwaited<X>, string>>,
Expect<Equal<MyAwaited<Y>, { field: number }>>,
Expect<Equal<MyAwaited<Z>, string | number>>,
]
// @ts-expect-error
type error = MyAwaited<number>
直白的说,需要实现 MyAwaited<Promise<string>> 的结果为 string。
MyAwaited<Promise<ExampleType>> = ExampleType
解题思路:
- 判断 T 是否是 Promise 类型,如果是 Promise 类型,返回 Promise 类型推断 X ,如果不是 Promise 类型,则返回 T
type MyAwaited<T> = T extends Promise<infer X> ? X : T
这样只能通过 X,Y 的测试用例(题目描述中的测试用例), 无法处理嵌套 Promise 和 错误情况(T 不是 Promise 需抛出错误)
- 利用递归的思路,如果 T 是 Promise 类型,调用自身。
type MyAwaited<T> = T extends Promise<infer X> ? MyAwaited<T> : T
这样可以通过 X,Y,Z 的测试用例,虽然判断了 T 是否是 Promise,但无法处理 T 不是 Promise 的情况。
- 可以通过限定 T 的类型
type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer X> ? MyAwaited<X> : T //报错
这样实际上会报错,因为已经限定了 T 为 Promise, 但调用自身时,X 有可能不是 Promise
- 所以需要对 X 再进行判断
type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer X>
? X extends Promise<unknown>
? MyAwaited<X>
: X
: never
由于最外层的三元运算的否定逻辑并不会执行,所以可以将其改为 never(或者任何类型都可以)。
这样可以通过全部测试。
014. Easy First
Implement a generic First<T> that takes an Array T and returns it's first element's type.
For example
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type head1 = First<arr1> // expected to be 'a'
type head2 = First<arr2> // expected to be 3
type head2 = First<arr2> // expected to be never
直接获取第 1 个元素。
不过如果 T 是空数组,那么会返回 undefined, 所以这种方法并不满足题目要求。
type First<T extends any[]> = T[0]判断 T 是否为空数组
type First<T extends any[]> = T extends {} : never : T[0]判断数组长度是否为 0
type First<T extends any[]> = T['length'] extends 0 ? never : T[0]infer
type First<T extends any[]> = T entends [infer First,...infer Rest] ? First : never
015. Last of Array
Implement a generic Last<T> that takes an Array T and returns its last element.
For example
type arr1 = ['a', 'b', 'c']
type arr2 = [3, 2, 1]
type tail1 = Last<arr1> // expected to be 'c'
type tail2 = Last<arr2> // expected to be 1
与第 14 题类似,可以使用 infer。
type Last<T extends any[]> = T extends [...infer Rest, infer Last] ? Last : never
043. Easy Exclude
Implement the built-in Exclude<T, U>
Exclude from T those types that are assignable to U
type cases = [
Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a'>, Exclude<'a' | 'b' | 'c', 'a'>>>,
Expect<Equal<MyExclude<'a' | 'b' | 'c', 'a' | 'b'>, Exclude<'a' | 'b' | 'c', 'a' | 'b'>>>,
Expect<Equal<MyExclude<string | number | (() => void), Function>, Exclude<string | number | (() => void), Function>>>,
]
实现一个 Exclude,从 T 中排除掉可以分配给 U 的类型,也就是说,
如果 T 中出现 U 中的类型,则将 T 中的这个类型过滤掉。
如果 U 中的类型没有在 T 中出现,则忽略。
例如:
let a: Exclude<string | number | object, string | Function> // let a = number|object
let b: MyExclude<string | number | object, string | Function> // let b = number|object
答案:
type MyExclude<T, U> = T extends U ? never : T
TS 官方实现了 Exclue<UnionType,ExcludedMembers> Utility Types:
https://www.typescriptlang.org/docs/handbook/utility-types.html#excludeuniontype-excludedmembers
018. Tuple Length
For given a tuple, you need create a generic Length, pick the length of the tuple
For example
type tesla = ['tesla', 'model 3', 'model X', 'model Y']
type spaceX = ['FALCON 9', 'FALCON HEAVY', 'DRAGON', 'STARSHIP', 'HUMAN SPACEFLIGHT']
type teslaLength = Length<tesla> // expected 4
type spaceXLength = Length<spaceX> // expected 5
答案:
type Length<T extends readonly any[]> = T['length']
因为 tuple 类型,所以需要 readonly 。
004. Easy Pick
Implement the built-in Pick<T, K> generic without using it.
Constructs a type by picking the set of properties K from T
For example
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = MyPick<Todo, 'title' | 'completed'>
const todo: TodoPreview = {
title: 'Clean room',
completed: false,
}
答案:
type MyPick<T, U extends keyof T> = {
[P in U]: T[P]
}
TS 官方实现了 Pick<Type,Keys> 的 Utility Types
https://www.typescriptlang.org/docs/handbook/utility-types.html#picktype-keys
007.
008. Easy If
Implement a utils If which accepts condition C, a truthy return type T, and a falsy return type F. C is expected to be either true or false while T and F can be any type.
For example:
import type { Equal, Expect } from '@type-challenges/utils'
type cases = [Expect<Equal<If<true, 'a', 'b'>, 'a'>>, Expect<Equal<If<false, 'a', 2>, 2>>]
// @ts-expect-error
type error = If<null, 'a', 'b'>
答案:
type If<B extends boolean, T, U> = B extends true ? T : U
这道题比较简单,但是有一个知识点:null 的类型。
如果关闭了 strictNullChecks 选项,则 null 的会判定为 true
type t = If<null, true, false> // t 的类型是 true
关于 null 的类型,ts 文档中有明确说明:
When
strictNullChecksisfalse,nullandundefinedare effectively ignored by the language. This can lead to unexpected errors at runtime.When
strictNullChecksistrue,nullandundefinedhave their own distinct types and you’ll get a type error if you try to use them where a concrete value is expected.