Typescript 类型体操 —— Union To Tuple

2,290 阅读2分钟

🤔️要求

实现一个可以将 union 转化为 tuple 的类型 UnionToTuple

由于 union 是一个无序结构, tuple 是一个有序结构。因此在这个挑战中, tuple 中的元素顺序可以以任何顺序返回。但不能为他们的联合类型。

UnionToTuple<1>           // [1], and correct

UnionToTuple<'any' | 'a'> // ['any','a'], and correct

或者

UnionToTuple<'any' | 'a'> // ['a','any'], and correct

他不应该是所有正确类型的 union 类型

UnionToTuple<'any' | 'a'> // ['a','any'] | ['any','a'], which is incorrect

类型是可以省略的,例如。

Equal<UnionToTuple<any | 'a'>,UnionToTuple<any>>  // will always be a true

Equal<UnionToTuple<unknown | 'a'>,UnionToTuple<unknown>> // will always be a true

Equal<UnionToTuple<never | 'a'>,UnionToTuple<'a'>>  // will always be a true

Equal<UnionToTuple<'a' | 'a' | 'a'>, UnionToTuple<'a'>>// will always be a true

📖知识点

🔗知识链接

  1. UnionToIntersection
type UnionToIntersection = (T extends any ? (args: T) => any: never) extends (args: infer R) => any ? R : never;

以 UnionToIntersection<'a' | 'b' > 为例。
    * T extends any 利用了 extends 的 [distribute](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#distributive-conditional-types) 特性。 因为任何类型都 extends any, 转化为 `((args: 'a') => any | (args: 'b') => any) extends (args: infer R) => any ? : R : never;` 
    *  [如果在 extends 中使用 infer。 对于给定的infer类型变量V,如果有候选类型是从协变的位置上推断出来的,那么V的类型是那些候选类型的联合。反之,如果有候选类型是从逆变的位置上推断出来的,那么V的类型是那些候选类型的交叉类型。否则V的类型是never。](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#conditional-types)
    *  根据上面这个原理, 因为 infer R 是在逆变的位置, 所以`infer R` 的值为候选类型的联合类型 `'a' & 'b'`

具体可以看我上次写的一篇文章 juejin.cn/post/697624…

  1. 如何判断一个类型是否为 Never ?

  2. 函数重载

  3. 获取重载类型的返回类型会获得最后一项的类型

type Override<T> = UnionToIntersection<(T extends any ? (arg: T) => T : never)> 

type A = Override<'a' | 'b'> //(arg: 'a') => 'a' & (arg: 'b') => b

declare const fn : A ; // 定义一个类型为 A 的函数 fn

fn('a') // no error

fn('b') // no error

type R = ReturnType<A>; // 获得类型 A 的最后一项类型 'b'

😢问题 & 解答

  1. 答题入口:Union to Tuple
  2. 解答