Appearance
类型体操 01
INFO
以下的题目全部出自 Type Challenge,如果想刷题的话可以安装 VSCode 的插件。
获取函数返回值
ts
type MyReturnType<T extends Function> = T extends (...args: any) => infer R
? R
: never
实现 Omit
ts
type MyOmit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[K]
}
实现 Pick
ts
type MyPick<T, K extends keyof T> = {
[P in keyof T as P extends K ? P : never]: T[P]
}
实现 Readonly
ts
type MyReadonly<T> = {
readonly [P in keyof T]: T[P]
}
将 Tuple 转为对象
ts
type TupleToObject<T extends readonly (string | number | symbol)[]> = {
[P in T[number]]: P
}
获取数组第一个元素的类型
ts
type First<T extends any[]> = T['length'] extends 0 ? never : T[0]
注意空数组的判断就行。
获取元组的长度
ts
type Length<T extends readonly any[]> = T['length']
实现内置的 Exclude
ts
type MyExclude<T, U> = T extends U ? never : T
获取 Promise 的返回值
ts
type MyAwaited<T extends PromiseLike<any>> = T extends PromiseLike<infer R>
? R extends PromiseLike<any>
? MyAwaited<R>
: R
: never
注意上面用的是的 PromiseLike 而不是 Promise,主要是为了避免用户自己实现的 Promise 不完全符合标准,比如说以下的测试:
ts
type T = { then: (onfulfilled: (arg: number) => any) => any }
实现 IF
ts
type If<C extends boolean, T extends any, F extends any> = C extends true
? T
: F
实现 Concat
ts
type Tuple = readonly unknown[]
type Concat<T extends Tuple, U extends Tuple> = [...T, ...U]
实现 Include
ts
type IsEqual<T, U> = (<G>() => G extends T ? 1 : 2) extends <G>() => G extends U
? 1
: 2
? true
: false
type Includes<T extends readonly any[], U> = T extends [
infer First,
...infer Rest
]
? IsEqual<First, U> extends true
? true
: Includes<Rest, U>
: false
这道题难度估计是划错了,说是简单,实现起来挺多细节,具体的坑可以参考于此,主要就是null
和undefined
在 TS 中互相赋值的问题。
实现 Push
ts
type Push<T extends unknown[], U> = [...T, U]
实现 Unshift
ts
type Unshift<T extends unknown[], U> = [U, ...T]
实现 Parameter
ts
type MyParameters<T extends (...args: any[]) => any> = T extends (
...args: infer R
) => any
? R
: never
获取函数返回值
ts
type MyReturnType<T extends Function> = T extends (...args: any) => infer R
? R
: never
实现 Omit
ts
type MyOmit<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P]
}
对象部分属性只读
ts
type MyReadonly2<T, K extends keyof T = keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P]
} & { readonly [P in K]: T[P] }
对象属性只读(递归)
ts
type DeepReadonly<T> = {
readonly [K in keyof T]: keyof T[K] extends never ? T[K] : DeepReadonly<T[K]>
}
type DeepReadonly2<T> = keyof T extends never
? T
: { readonly [k in keyof T]: DeepReadonly<T[k]> }
这道题的难点在于 DeepReadonly2 为什么通过不了下面的测试:
ts
type X2 = { a: string } | { b: number }
type Expected2 = { readonly a: string } | { readonly b: number }
元组转合集
ts
type TupleToUnion<T> = T extends (infer P)[] ? P : never
可串联构造器
ts
type Chainable<T = {}> = {
option: <K extends string, V>(
key: K extends keyof T ? never : K,
value: V
) => Chainable<Omit<T, K> & Record<K, V>>
get: () => T
}
注意重复属性赋值的问题。
获取最后一个元素
ts
type Last<T extends any[]> = [any, ...T][T['length']]
这题奇葩就奇葩在第一个any
上,要不T['length']
会超过索引,但 TS 中又不能使用T['length'] - 1
这种表达式,有些时候 TS 的语法还是稍显奇怪的。