semlinker/awesome-typescript

「重学TS 2.0 」TS 练习题第四十三题

Opened this issue · 10 comments

实现一个 Permutation 工具类型,当输入一个联合类型时,返回一个包含该联合类型的全排列类型数组。具体的使用示例如下所示:

type Permutation<T, K=T> = // 你的实现代码

// ["a", "b"] | ["b", "a"]
type P0 = Permutation<'a' | 'b'>  // ['a', 'b'] | ['b' | 'a']
// type P1 = ["a", "b", "c"] | ["a", "c", "b"] | ["b", "a", "c"] 
// | ["b", "c", "a"] | ["c", "a", "b"] | ["c", "b", "a"]
type P1 = Permutation<'a' | 'b' | 'c'> 

请在下面评论你的答案

ln0y commented
type Permutation<T, K = T> = [T] extends [never] ? [] : K extends K ? [K, ...Permutation<Exclude<T, K>>] : never
// 实现一个 Permutation 工具类型,当输入一个联合类型时,返回一个包含该联合类型的全排列类型数组。具体的使用示例如下所示:

type Om<T, I> = T extends I ? never : T;
type Test<T extends any[], F, G = F> = T extends any[]
  ? F | 1 extends 1
    ? T
    : F extends any
    ? Test<[...T, F], Om<G, F>>
    : T
  : T;
type Permutation<T> = Test<[], T>;

// ["a", "b"] | ["b", "a"]
type P0 = Permutation<"a" | "b">; // ['a', 'b'] | ['b' | 'a']
// type P1 = ["a", "b", "c"] | ["a", "c", "b"] | ["b", "a", "c"]
// | ["b", "c", "a"] | ["c", "a", "b"] | ["c", "b", "a"]
type P1 = Permutation<"a" | "b" | "c">

type Permutation<T, K = T> =
[T] extends [never]
? []
: T extends K
? [
T,
...Permutation<Exclude<K, T>>
]
: never
// 你的实现代码

// ["a", "b"] | ["b", "a"]
type P0 = Permutation<'a' | 'b'> // ['a', 'b'] | ['b' , 'a']
// type P1 = ["a", "b", "c"] | ["a", "c", "b"] | ["b", "a", "c"]
// | ["b", "c", "a"] | ["c", "a", "b"] | ["c", "b", "a"]
type P1 = Permutation<'a' | 'b' | 'c'>

//* 实现一个 Permutation 工具类型,当输入一个联合类型时,返回一个包含该联合类型的全排列类型数组。
type IsNever<T> = [T] extends [never] ? true : false;

type Permutation<T extends  string | number | symbol> = {
  [index in T]: IsNever<Exclude<T,index>> extends true ? [index] : [index, ...Permutation<Exclude<T ,index>>]
}[T];
type PP = Permutation<'a'>
// ["a", "b"] | ["b", "a"]
type P0 = Permutation<'a' | 'b'>  // ['a', 'b'] | ['b' | 'a']
// type P1 = ["a", "b", "c"] | ["a", "c", "b"] | ["b", "a", "c"]
// | ["b", "c", "a"] | ["c", "a", "b"] | ["c", "b", "a"]
type P1 = Permutation<'a' | 'b' | 'c'>
type Permutation<T, K = T> = [T] extends [never] ? [] : K extends K ? [K, ...Permutation<Exclude<T, K>>] : never

初看有些看不懂, 因此详细解释一下这位老哥的解法

// 1. 通过extends语法来对T类型中的或值的每一个值进行遍历
// 如果不理解的可以参考下面的例子:
// type TTT = ['a' | 'b'] extends ['a'] | ['b'] ? true : false // true
// 2. 遍历之后再对后续递归需要使用的K进行遍历(主要是起到排除当前T中的key的效果)
// 同时我们也知道, n个不重复值全排列的值为n的阶乘
// 也就是说我们需要在遍历的时候一直排除当前值, 遍历剩余值即可
// K extends K则是对当前递归场景下的所有key的遍历
// 然后最后, 递归处理剩余的参数即可, 期间使用了扩展运算符来对多维数组进行摊平
// 例如: 对于 'a' | 'b' | 'c' 来说, 会这样产生值
// ['a', ...['b', ...['c']]], ['a', ...['c', ...['b']]] ...
// 完成解构之后, 便成为了期望的  ['a', 'b', 'c'], ['a', 'c', 'b'] ...
type Permutation<T, K = T> = [T] extends [never] ? [] : K extends K ? [K, ...Permutation<Exclude<T, K>>] : never
type Permutation<T, K=T> = T extends K
  ?[T,...Permutation<K,Exclude<K,T>>]
  :[]

这个实现看着也可以,不知有没有什么弊端

type Permutation<T, K = T> 
  = [T] extends [never]
    ? []
    : K extends any
      ? [K, ...Permutation<Exclude<T, K>>]
      : never

// ["a", "b"] | ["b", "a"]
type P0 = Permutation<"a" | "b">;
// type P1 = ["a", "b", "c"] | ["a", "c", "b"] | ["b", "a", "c"]
// | ["b", "c", "a"] | ["c", "a", "b"] | ["c", "b", "a"]
type P1 = Permutation<"a" | "b" | "c">;
type Permutation<T, K = T> 
  = T extends any
    ? Exclude<K, T> extends never
      ? [T]
      : [T, ...Permutation<Exclude<K, T>>]
    : never
type Permutation<T, K = T> 
  = T extends any
    ? Exclude<K, T> extends never
      ? [T]
      : [T, ...Permutation<Exclude<K, T>>]
    : never
MuLoo commented
type Permutation<T, K = T> = [T] extends [never] ? [] : K extends K ? [K, ...Permutation<Exclude<T, K>>] : never

初看有些看不懂, 因此详细解释一下这位老哥的解法

// 1. 通过extends语法来对T类型中的或值的每一个值进行遍历
// 如果不理解的可以参考下面的例子:
// type TTT = ['a' | 'b'] extends ['a'] | ['b'] ? true : false // true
// 2. 遍历之后再对后续递归需要使用的K进行遍历(主要是起到排除当前T中的key的效果)
// 同时我们也知道, n个不重复值全排列的值为n的阶乘
// 也就是说我们需要在遍历的时候一直排除当前值, 遍历剩余值即可
// K extends K则是对当前递归场景下的所有key的遍历
// 然后最后, 递归处理剩余的参数即可, 期间使用了扩展运算符来对多维数组进行摊平
// 例如: 对于 'a' | 'b' | 'c' 来说, 会这样产生值
// ['a', ...['b', ...['c']]], ['a', ...['c', ...['b']]] ...
// 完成解构之后, 便成为了期望的  ['a', 'b', 'c'], ['a', 'c', 'b'] ...
type Permutation<T, K = T> = [T] extends [never] ? [] : K extends K ? [K, ...Permutation<Exclude<T, K>>] : never

这位老哥已经解析的很好了,补充下最前面

[T] extends [never]

是干嘛的?
因为递归调用,参数T 就是上一轮的 Exclude<T, K> , 那么就可能会出现 Exclude<'a', 'a'>never的情况,[T] extends [never] 就是判断T是否为never, 是的话返回一个空数组即可。