1、OptionalKeys
获取对象类型中的可选属性的联合类型
type a1 = OptionalKeys<{ foo: number | undefined, bar?: string, flag: boolean }> // bartype a2 = OptionalKeys<{ foo: number, bar?: string }> // bartype a3 = OptionalKeys<{ foo: number, flag: boolean }> // nevertype a4 = OptionalKeys<{ foo?: number, flag?: boolean }> // foo|flagtype a5 = OptionalKeys<{}> // never
实现:
type Equals<X, Y> =(<T>() => T extends X ? 1 : 2) extends(<U>() => U extends Y ? 1 : 2) ? true : false//遍历T的K,如果原始的属性K与加可选`?`构造对象相同,说明此属性为可选属性type OptionalKeys<T> = {[K in keyof T]: Equals<{ [F in K]: T[F] },{ [F in K]?: T[F] }> extends true ? K : never}[keyof T]//另外一种思路:就是如果去除一个属性还能与原对象相同,说明就是可选属性type Omit<T, K> = Pick<T, Exclude<keyof T, K>>type OptionalKeys<T> = {[K in keyof T]: Omit<T, K> extends T ? K : never}[keyof T]
2、PickOptional
保留一个对象中的可选属性类型
type a1 = PickOptional<{ foo: number | undefined, bar?: string, flag: boolean }> // {bar?:string|undefined}type a2 = PickOptional<{ foo: number, bar?: string }> // {bar?:string}type a3 = PickOptional<{ foo: number, flag: boolean }> // {}type a4 = PickOptional<{ foo?: number, flag?: boolean }> // {foo?:number,flag?:boolean}type a5 = PickOptional<{}> // {}
实现:
type Equals<X, Y> =(<T>() => T extends X ? 1 : 2) extends(<U>() => U extends Y ? 1 : 2) ? true : falsetype OptionalKeys<T> = {[K in keyof T]: Equals<{ [F in K]: T[F] },{ [F in K]?: T[F] }> extends true ? K : never}[keyof T]//关键就是实现OptionalKeys类型,看上图有两种处理方式type PickOptional<T> = {[K in OptionalKeys<T>]?: T[K]}
3、RequiredKeys
保留一个对象中的必须属性
type a1 = PickRequired<{ foo: number | undefined, bar?: string, flag: boolean }> // {foo:number|undefined,flag:boolean}type a2 = PickRequired<{ foo: number, bar?: string }> // {foo:number}type a3 = PickRequired<{ foo: number, flag: boolean }> // {foo:number,flag:boolean}type a4 = PickRequired<{ foo?: number, flag?: boolean }> // {}type a5 = PickRequired<{}> // {}
实现:
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>type RequiredKeys<T> = {[K in keyof T]: Omit<T, K> extends T ? never : K}[keyof T]
4、PickRequired
保留一个对象中的必须属性
type a1 = PickRequired<{ foo: number | undefined, bar?: string, flag: boolean }> // {foo:number|undefined,flag:boolean}type a2 = PickRequired<{ foo: number, bar?: string }> // {foo:number}type a3 = PickRequired<{ foo: number, flag: boolean }> // {foo:number,flag:boolean}type a4 = PickRequired<{ foo?: number, flag?: boolean }> // {}type a5 = PickRequired<{}> // {}
实现:
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>type OptionalKeys<T> = {[K in keyof T]: Omit<T, K> extends T ? K : never}[keyof T]//需要采用提出可选属性的方式获得必须属性,否则联合类型的值,undefined会消失(foo: number | undefined)type PickRequired<T> = Omit<T, OptionalKeys<T>>
5、Merge
合并两个对象类型
T以及K,如果属性重复,则以K中属性类型为准
type obj1 = {el: string,age: number}type obj2 = {el: HTMLElement,flag: boolean}type obj3 = Merge<obj1, obj2> // {el:HtmlElement,age:number,flag:boolean}const a = {...{} as obj3}console.log(a.el.scrollTop, a.age.toFixed(0), a.flag.valueOf())// console.log(a.el.charAt(0)) // error
实现:
//优先返回U中的键值对方式type Merge<T extends object, U extends object> = {[key in (keyof T | keyof U)]: key extends keyof U ? U[key] : key extends keyof T ? T[key] : never}
6、IsNever
判断是否为never类型
type A = IsNever<never> // truetype B = IsNever<string> // falsetype C = IsNever<undefined> // falsetype D = IsNever<any> // false
实现:
type IsNever<T> = [T, never] extends [never, T] ? true : false
7、IsEmptyType
判断是否为没有属性的对象类型
{}
type A = IsEmptyType<string> // falsetype B = IsEmptyType<{ a: 3 }> // falsetype C = IsEmptyType<{}> // truetype D = IsEmptyType<any> // falsetype E = IsEmptyType<object> // falsetype F = IsEmptyType<Object> // falsetype G = IsEmptyType<unknown> // false
实现:
type IsEmptyType<T> = T extends object ? [keyof T] extends [] ? true : false : false
8、IsAny
判断是否为any类型
type A = IsAny<string> // falsetype B = IsAny<any> // truetype C = IsAny<unknown> // falsetype D = IsAny<never> // false
实现:
// unknown 只能赋值给 any或者unknown// any可以赋值给string,但是unknown不可以赋值给stringtype IsAny<T> = [unknown] extends [T] ? [T] extends [string] ? true : false : false//还有一种更加简洁的写法,通过any可以被赋值任意类型,而其它不行的特性type IsAny<T> = 0 extends (1 & T) ? true : false
9、Redux Connect
实现Connect类型,能够自动地转化Redux Module对象中的函数类型
interface Module {count: number;message: string;asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>;syncMethod<T, U>(action: Action<T>): Action<U>;}interface Action<T> {payload?: T;type: string;}// 这个要求的结果type Result = {asyncMethod<T, U>(input: T): Action<U>;syncMethod<T, U>(action: T): Action<U>;}// 实现类型Connect,要求 Connect<Module> 的结果为上面的 Result// 只要函数类型的属性;// 如果函数是异步函数,要求自动解析出来Promise中的类型;
实现:
//获取普通属性的keytype NormalKeys<T> = {[K in keyof T]: T[K] extends (...args: any) => any ? never : K}[keyof T]//剔除非函数属性type FunctionOptional<T> = Omit<T, NormalKeys<T>>type InferConnectFunctionParameterType<Func> =Func extends (<T, U>(input: Promise<T>) => Promise<Action<U>>) ? (<T, U>(input: T) => Action<U>) :Func extends (<T, U>(action: Action<T>) => Action<U>) ? (<T, U>(action: T) => Action<U>) : nevertype Connect<T extends object> = {[K in keyof FunctionOptional<T>]: InferConnectFunctionParameterType<T[K]>}
10、UnionToBooleanProps
有且只有一个属性
// 实现一个叫做 UnionToBooleanProps 的泛型,使得以下需求成立type MessageStringType = "info" | "success" | "warning" | "error";type OneMessageTypes = UnionToBooleanProps<MessageStringType>type Props = OneMessageTypes & { id: string; }function Component(props: Props) {return <></>}const a = <Component id="abc" info/> //correctconst b = <Component id="abc" success/> //correctconst c = <Component id="abc"/> //wrongconst d = <Component id="abc" info success/> //wrong// 组件Component所接收的属性,有且只有一个 "info" | "success" | "warning" | "error" 中的值;
实现:
//联合类型extends时会分发,利用此特性生成如下结构type UnionToBooleanProps<T extends string, U extends string = T> =T extends any ? { [k in Exclude<U, T>]?: void } & { [k in T]: boolean } : never/**{info: boolean;success?: void;warning?: void;error?: void} | {info?: void;success: boolean;warning?: void;error?: void} | {info?: void;success?: void;warning: boolean;error?: void} | {info?: void;success?: void;warning?: void;error: boolean}*/
11、UnionToIntersection
将联合类型转换为交叉类型
type A = UnionToIntersection<{a: string} | {b: string} | {c: string}>// {a: string} & {b: string} & {c: string}
实现:
type UnionToIntersection<T> = (T extends infer R ? (x: R) => any : never) extends (x: infer V) => any ? V : never/*** 1. T extends infer R ? (x: R) => any : never 将生成一个函数的联合类型,就像下面这样:* (x: {a: string}) => any | (x: {b: string}) => any | (x: {c: string}) => any** 2. extends (x: infer V) => any ? V : never 将会寻找联合函数的超集,而满足条件的就是下面这样的函数* (x: {a: string} & {b: string} & {c: string}) => any*/
这道题解答看的github:https://github.com/type-challenges/type-challenges/issues/775
12、UnionPop
得到联合类型中的最后一个类型
type a = 1 | 2 | 3type b = UnionPop<a>; // 3
实现:
//将联合类型转成函数类型的交叉 ((arg: "1") => void) & ((arg: "2") => void) & ((arg: "3") => void)type UnionToIntersectionFn<T> = (T extends any ? (k: (arg: T) => void) => void : never) extends ((k: infer I) => void) ? I : never//函数类型的 intersection 等价于 接口的 call signature 重载//这里有个TS特性,https://github.com/type-challenges/type-challenges/issues/737#issuecomment-791505468//如果有必要从重载中输出一种类型,TS会选择重载中的最后一个签名type UnionPop<U> = UnionToIntersectionFn<U> extends { (a: infer A): void; } ? A : never;
小结:
这个与11题都是利用了协变与逆变。针对于最后这三题的解答可以查看:https://zhuanlan.zhihu.com/p/58704376
13、UnionToTuple
联合类型转换为元组类型
type a = UnionToTuple<1 | 2 | 3> // [1,2,3]type b = UnionToTuple<1 | string | boolean> // [1,string,boolean]
实现:
type UnionToIntersectionFn<T> = (T extends any ? (k: (arg: T) => void) => void : never) extends ((k: infer I) => void) ? I : nevertype UnionPop<U> = UnionToIntersectionFn<U> extends { (a: infer A): void; } ? A : never;type UnionToTuple<T, Last = UnionPop<T>> = [T] extends [never] ? [] : [...UnionToTuple<Exclude<T, Last>>, Last]
