题目地址:github
在线编辑器:TypeScript Playground
1. ReturnType
type MyReturnType<Fn extends Function> = Fn extends (...args: any) => infer R ? R : never;
2. Omit
type MyExclude<T, K> = T extends K ? never : T;
type MyOmit<T, K extends keyof T> = {
[P in MyExclude<keyof T, K>]: T[P];
}
3. Readonly2
允许选择指定的key进行readonly化
type MyReadonly2<T, K extends keyof T> = {
readonly [P in K]: T[P];
} & {
[P in MyExclude<keyof T, K>]: T[P];
}
4. DeepReadonly
对嵌套属性也进行readonly化
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends number | string | symbol ? T[P] : DeepReadonly<T[P]>;
}
类型也可以递归
5. TupleToUnion
type TupleToUnion<A extends any[]> = A[number]
6. Chainable
实现可以链式调用的类型结构,其中包含 option(key, value) get() 两个方法,option 的参数是对象的属性和值,get 用于得到最后的结果。
主要是需要实现可以达到这样效果的类型声明,不需要写逻辑
interface Chainable<T = {}> {
// `K extends string` 是让ts推导 `key` 类型的时候将类型收窄为字面量类型
// `[P in K]` 是将推导的 `K` 看做一个联合类型,然后使用 `in` 拿到具体的类型,用作
// 键名,这样最终的类型中才会出现明确的键名
option: <K extends string, V>(key: K, value: V) => Chainable<T & {
[P in K]: typeof value
}>,
get: () => T
}
可以链式调用就意味着这个
option方法返回了这个类型,所以需要提供一个泛型参数T,其作用为:1. 接收不断调用option传递的keyvalue,构造新的类型继续作为下一个T;2. 作为get方法返回值。
7. Last
获取数组最后一个值的类型
type Last<A extends any[]> = [undefined, ...A][A['length']];
这题我没做出来,没转过来弯。看到比较巧妙的思路就是在数组前面加一个元素,那原来的
length就是新数组的最后一个index
8. Pop
实现数组的pop方法,返回pop后的数组类型
type Pop<A extends any[]> = A extends [...infer R, infer L] ? R : undefined;
type Shift<A extends any[]> = A extends [infer F, ...infer R] ? R : undefined;
shift同理
上一题的Last也可以用类似的方式,比如直接返回
L。需要注意的是infer关键字必须用在条件类型中extends后面
9. PromiseAll
取到 Promise.all() 返回值的类型
type GetPromiseType<P> = P extends Promise<infer R> ? R : P;
type PromiseAll<P extends any[], R extends any[] = []> = P extends [infer F, ...infer Rest]
? PromiseAll<Rest, [GetPromiseType<F>, ...R]>
: Promise<R>;
还是递归,暂时没想到其他方法。为了能够拿到每一项的类型,就递归取第一个元素进行判断
10. LookUp
从联合类型(限定类型中存在 { type: string } )中查找符合要求的类型
type LookUp<T, P> = T extends { type: P } ? T : never;
'a' | never === 'a',任何其他类型和never进行|返回其本身
11. TrimLeft
对字符串进行trim left处理
type TrimLeft<S extends string> = S extends `${infer F}${infer R}`
? F extends ' '
? TrimLeft<R>
: S
: S;
字符串也可以使用条件类型进行解构,一次一个字符。字符串可以根据合理模式进行自由解构
12. Trim
type ReverseString<
S extends string,
R extends string = ''
> = S extends `${infer F}${infer Rest}`
? ReverseString<Rest, `${F}${R}`>
: R;
type Trim<S extends string> = ReverseString<TrimLeft<ReverseString<TrimLeft<S>>>>
想法就是先trim lefe,然后翻转再trim left,最后再翻转回来