同样也是做类型体操的时候遇到的,记录一下:
1. Awaited
现在有这些数据:
type X = Promise<string>
type Y = Promise<{ field: number }>
type Z = Promise<Promise<string | number>>
type Z1 = Promise<Promise<Promise<string | boolean>>>
type T = { then: (onfulfilled: (arg: number) => any) => any }
要求把其中的类型取出来,比如X里的string,Y里的等。
三个问题:
- 最基本的,如何取出来?
- 嵌套情况下如何递归
- 像type T这种类promise对象如何处理?
一个个解决,首先第一点就要用到infer,infer网上查了解释五花八门,其实就可以把它理解为一个占位符,来看具体的处理:
type MyAwaited<T> = T extends Promise<infer R> ? R : T;
很好理解,判断T是否属于Promise<>这种结构,是的话返回里面的R,否则直接返回T。
再加一层递归解决问题二:
type MyAwaited<T> = T extends Promise<infer R> ? MyAwaited<R> : T;
最后引入PromiseLike对象解决问题三:
type MyAwaited<T> = T extends PromiseLike<infer R> ? MyAwaited<R> : T;
当然这样还是存在小问题,就是判断不了一开始直接就传入基本类型值的情况,比如:
// @ts-expect-error
type error = MyAwaited<number>
因此最终答案会稍微复杂点,不过这题主要是为了熟悉infer的使用:
type MyAwaited<T extends PromiseLike<any | PromiseLike<any>>> =
T extends PromiseLike<infer V>
? V extends PromiseLike<any>
? MyAwaited<V>
: V
: never
2. Include
先上测试数据:
type cases = [
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Kars'>, true>>,
Expect<Equal<Includes<['Kars', 'Esidisi', 'Wamuu', 'Santana'], 'Dio'>, false>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 7>, true>>,
Expect<Equal<Includes<[1, 2, 3, 5, 6, 7], 4>, false>>,
Expect<Equal<Includes<[1, 2, 3], 2>, true>>,
Expect<Equal<Includes<[1, 2, 3], 1>, true>>,
...
]
实现Array.includes()方法,注意这里infer是如何占位的:
type Includes<T extends any[], U> =
T extends [infer First , ...infer Rest]
? Equal<First , U> extends true
? true
: Includes<Rest , U>
: false
首先是判断T是不是一个数组,然后用First占位数组中的第一个元素,剩下的用Rest占位。不是数组返回false。
如果T是一个数组,判断第一个元素和待检测元素是否相等,相当说明include,返回true。
不相等则递归检查剩余的数组元素,如果一直找不到Rest数组会变成空数组:
[] extends [infer First , ...infer Rest] // false
最终也会返回false。
这题主要就是学会数组的占位。
3. Parameters
需要实现的效果如图,也就是取到函数的参数类型:
const foo = (arg1: string, arg2: number): void => {}
const bar = (arg1: boolean, arg2: { a: 'A' }): void => {}
const baz = (): void => {}
type cases = [
Expect<Equal<MyParameters<typeof foo>, [string, number]>>,
Expect<Equal<MyParameters<typeof bar>, [boolean, { a: 'A' }]>>,
Expect<Equal<MyParameters<typeof baz>, []>>,
]
思路也是通过infer去占位需要取到的东西:
type MyParameters<T extends (...args: any[]) => any> =
T extends (...args: infer S) => any ? S : never;