1、OptionalKeys

获取对象类型中的可选属性的联合类型

  1. type a1 = OptionalKeys<{ foo: number | undefined, bar?: string, flag: boolean }> // bar
  2. type a2 = OptionalKeys<{ foo: number, bar?: string }> // bar
  3. type a3 = OptionalKeys<{ foo: number, flag: boolean }> // never
  4. type a4 = OptionalKeys<{ foo?: number, flag?: boolean }> // foo|flag
  5. type a5 = OptionalKeys<{}> // never

实现:

  1. type Equals<X, Y> =
  2. (<T>() => T extends X ? 1 : 2) extends
  3. (<U>() => U extends Y ? 1 : 2) ? true : false
  4. //遍历T的K,如果原始的属性K与加可选`?`构造对象相同,说明此属性为可选属性
  5. type OptionalKeys<T> = {
  6. [K in keyof T]: Equals<{ [F in K]: T[F] },
  7. { [F in K]?: T[F] }> extends true ? K : never
  8. }[keyof T]
  9. //另外一种思路:就是如果去除一个属性还能与原对象相同,说明就是可选属性
  10. type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
  11. type OptionalKeys<T> = {
  12. [K in keyof T]: Omit<T, K> extends T ? K : never
  13. }[keyof T]

2、PickOptional

保留一个对象中的可选属性类型

  1. type a1 = PickOptional<{ foo: number | undefined, bar?: string, flag: boolean }> // {bar?:string|undefined}
  2. type a2 = PickOptional<{ foo: number, bar?: string }> // {bar?:string}
  3. type a3 = PickOptional<{ foo: number, flag: boolean }> // {}
  4. type a4 = PickOptional<{ foo?: number, flag?: boolean }> // {foo?:number,flag?:boolean}
  5. type a5 = PickOptional<{}> // {}

实现:

  1. type Equals<X, Y> =
  2. (<T>() => T extends X ? 1 : 2) extends
  3. (<U>() => U extends Y ? 1 : 2) ? true : false
  4. type OptionalKeys<T> = {
  5. [K in keyof T]: Equals<{ [F in K]: T[F] },
  6. { [F in K]?: T[F] }> extends true ? K : never
  7. }[keyof T]
  8. //关键就是实现OptionalKeys类型,看上图有两种处理方式
  9. type PickOptional<T> = {
  10. [K in OptionalKeys<T>]?: T[K]
  11. }

3、RequiredKeys

保留一个对象中的必须属性

  1. type a1 = PickRequired<{ foo: number | undefined, bar?: string, flag: boolean }> // {foo:number|undefined,flag:boolean}
  2. type a2 = PickRequired<{ foo: number, bar?: string }> // {foo:number}
  3. type a3 = PickRequired<{ foo: number, flag: boolean }> // {foo:number,flag:boolean}
  4. type a4 = PickRequired<{ foo?: number, flag?: boolean }> // {}
  5. type a5 = PickRequired<{}> // {}

实现:

  1. type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
  2. type RequiredKeys<T> = {
  3. [K in keyof T]: Omit<T, K> extends T ? never : K
  4. }[keyof T]

4、PickRequired

保留一个对象中的必须属性

  1. type a1 = PickRequired<{ foo: number | undefined, bar?: string, flag: boolean }> // {foo:number|undefined,flag:boolean}
  2. type a2 = PickRequired<{ foo: number, bar?: string }> // {foo:number}
  3. type a3 = PickRequired<{ foo: number, flag: boolean }> // {foo:number,flag:boolean}
  4. type a4 = PickRequired<{ foo?: number, flag?: boolean }> // {}
  5. type a5 = PickRequired<{}> // {}

实现:

  1. type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
  2. type OptionalKeys<T> = {
  3. [K in keyof T]: Omit<T, K> extends T ? K : never
  4. }[keyof T]
  5. //需要采用提出可选属性的方式获得必须属性,否则联合类型的值,undefined会消失(foo: number | undefined)
  6. type PickRequired<T> = Omit<T, OptionalKeys<T>>

5、Merge

合并两个对象类型T以及K,如果属性重复,则以K中属性类型为准

  1. type obj1 = {
  2. el: string,
  3. age: number
  4. }
  5. type obj2 = {
  6. el: HTMLElement,
  7. flag: boolean
  8. }
  9. type obj3 = Merge<obj1, obj2> // {el:HtmlElement,age:number,flag:boolean}
  10. const a = {...{} as obj3}
  11. console.log(a.el.scrollTop, a.age.toFixed(0), a.flag.valueOf())
  12. // console.log(a.el.charAt(0)) // error

实现:

  1. //优先返回U中的键值对方式
  2. type Merge<T extends object, U extends object> = {
  3. [key in (keyof T | keyof U)]: key extends keyof U ? U[key] : key extends keyof T ? T[key] : never
  4. }

6、IsNever

判断是否为never类型

  1. type A = IsNever<never> // true
  2. type B = IsNever<string> // false
  3. type C = IsNever<undefined> // false
  4. type D = IsNever<any> // false

实现:

  1. type IsNever<T> = [T, never] extends [never, T] ? true : false

7、IsEmptyType

判断是否为没有属性的对象类型{}

  1. type A = IsEmptyType<string> // false
  2. type B = IsEmptyType<{ a: 3 }> // false
  3. type C = IsEmptyType<{}> // true
  4. type D = IsEmptyType<any> // false
  5. type E = IsEmptyType<object> // false
  6. type F = IsEmptyType<Object> // false
  7. type G = IsEmptyType<unknown> // false

实现:

  1. type IsEmptyType<T> = T extends object ? [keyof T] extends [] ? true : false : false

8、IsAny

判断是否为any类型

  1. type A = IsAny<string> // false
  2. type B = IsAny<any> // true
  3. type C = IsAny<unknown> // false
  4. type D = IsAny<never> // false

实现:

  1. // unknown 只能赋值给 any或者unknown
  2. // any可以赋值给string,但是unknown不可以赋值给string
  3. type IsAny<T> = [unknown] extends [T] ? [T] extends [string] ? true : false : false
  4. //还有一种更加简洁的写法,通过any可以被赋值任意类型,而其它不行的特性
  5. type IsAny<T> = 0 extends (1 & T) ? true : false

9、Redux Connect

实现Connect类型,能够自动地转化Redux Module对象中的函数类型

  1. interface Module {
  2. count: number;
  3. message: string;
  4. asyncMethod<T, U>(input: Promise<T>): Promise<Action<U>>;
  5. syncMethod<T, U>(action: Action<T>): Action<U>;
  6. }
  7. interface Action<T> {
  8. payload?: T;
  9. type: string;
  10. }
  11. // 这个要求的结果
  12. type Result = {
  13. asyncMethod<T, U>(input: T): Action<U>;
  14. syncMethod<T, U>(action: T): Action<U>;
  15. }
  16. // 实现类型Connect,要求 Connect<Module> 的结果为上面的 Result
  17. // 只要函数类型的属性;
  18. // 如果函数是异步函数,要求自动解析出来Promise中的类型;

实现:

  1. //获取普通属性的key
  2. type NormalKeys<T> = {
  3. [K in keyof T]: T[K] extends (...args: any) => any ? never : K
  4. }[keyof T]
  5. //剔除非函数属性
  6. type FunctionOptional<T> = Omit<T, NormalKeys<T>>
  7. type InferConnectFunctionParameterType<Func> =
  8. Func extends (<T, U>(input: Promise<T>) => Promise<Action<U>>) ? (<T, U>(input: T) => Action<U>) :
  9. Func extends (<T, U>(action: Action<T>) => Action<U>) ? (<T, U>(action: T) => Action<U>) : never
  10. type Connect<T extends object> = {
  11. [K in keyof FunctionOptional<T>]: InferConnectFunctionParameterType<T[K]>
  12. }

10、UnionToBooleanProps

有且只有一个属性

  1. // 实现一个叫做 UnionToBooleanProps 的泛型,使得以下需求成立
  2. type MessageStringType = "info" | "success" | "warning" | "error";
  3. type OneMessageTypes = UnionToBooleanProps<MessageStringType>
  4. type Props = OneMessageTypes & { id: string; }
  5. function Component(props: Props) {
  6. return <></>
  7. }
  8. const a = <Component id="abc" info/> //correct
  9. const b = <Component id="abc" success/> //correct
  10. const c = <Component id="abc"/> //wrong
  11. const d = <Component id="abc" info success/> //wrong
  12. // 组件Component所接收的属性,有且只有一个 "info" | "success" | "warning" | "error" 中的值;

实现:

  1. //联合类型extends时会分发,利用此特性生成如下结构
  2. type UnionToBooleanProps<T extends string, U extends string = T> =
  3. T extends any ? { [k in Exclude<U, T>]?: void } & { [k in T]: boolean } : never
  4. /**
  5. {
  6. info: boolean;
  7. success?: void;
  8. warning?: void;
  9. error?: void
  10. } | {
  11. info?: void;
  12. success: boolean;
  13. warning?: void;
  14. error?: void
  15. } | {
  16. info?: void;
  17. success?: void;
  18. warning: boolean;
  19. error?: void
  20. } | {
  21. info?: void;
  22. success?: void;
  23. warning?: void;
  24. error: boolean
  25. }
  26. */

11、UnionToIntersection

将联合类型转换为交叉类型

  1. type A = UnionToIntersection<{a: string} | {b: string} | {c: string}>
  2. // {a: string} & {b: string} & {c: string}

实现:

  1. type UnionToIntersection<T> = (T extends infer R ? (x: R) => any : never) extends (x: infer V) => any ? V : never
  2. /**
  3. * 1. T extends infer R ? (x: R) => any : never 将生成一个函数的联合类型,就像下面这样:
  4. * (x: {a: string}) => any | (x: {b: string}) => any | (x: {c: string}) => any
  5. *
  6. * 2. extends (x: infer V) => any ? V : never 将会寻找联合函数的超集,而满足条件的就是下面这样的函数
  7. * (x: {a: string} & {b: string} & {c: string}) => any
  8. */

这道题解答看的github:https://github.com/type-challenges/type-challenges/issues/775

12、UnionPop

得到联合类型中的最后一个类型

  1. type a = 1 | 2 | 3
  2. type b = UnionPop<a>; // 3

实现:

  1. //将联合类型转成函数类型的交叉 ((arg: "1") => void) & ((arg: "2") => void) & ((arg: "3") => void)
  2. type UnionToIntersectionFn<T> = (T extends any ? (k: (arg: T) => void) => void : never) extends (
  3. (k: infer I) => void) ? I : never
  4. //函数类型的 intersection 等价于 接口的 call signature 重载
  5. //这里有个TS特性,https://github.com/type-challenges/type-challenges/issues/737#issuecomment-791505468
  6. //如果有必要从重载中输出一种类型,TS会选择重载中的最后一个签名
  7. type UnionPop<U> = UnionToIntersectionFn<U> extends { (a: infer A): void; } ? A : never;

小结:
这个与11题都是利用了协变与逆变。针对于最后这三题的解答可以查看:https://zhuanlan.zhihu.com/p/58704376

13、UnionToTuple

联合类型转换为元组类型

  1. type a = UnionToTuple<1 | 2 | 3> // [1,2,3]
  2. type b = UnionToTuple<1 | string | boolean> // [1,string,boolean]

实现:

  1. type UnionToIntersectionFn<T> = (T extends any ? (k: (arg: T) => void) => void : never) extends (
  2. (k: infer I) => void) ? I : never
  3. type UnionPop<U> = UnionToIntersectionFn<U> extends { (a: infer A): void; } ? A : never;
  4. type UnionToTuple<T, Last = UnionPop<T>> = [T] extends [never] ? [] : [...UnionToTuple<Exclude<T, Last>>, Last]