[TypeScript] Type Challenges #296 - Permutation

99 阅读4分钟

题目描述

实现联合类型的全排列,将联合类型转换成所有可能的全排列数组的联合类型。

type perm = Permutation<'A' | 'B' | 'C'>; // ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']

题解

// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'

type cases = [
  Expect<Equal<Permutation<'A'>, ['A']>>,
  Expect<Equal<Permutation<'A' | 'B' | 'C'>, ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']>>,
  Expect<Equal<Permutation<'B' | 'A' | 'C'>, ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']>>,
  Expect<Equal<Permutation<boolean>, [false, true] | [true, false]>>,
  Expect<Equal<Permutation<never>, []>>,
]


// ============= Your Code Here =============
type Permutation<T, Acc = T> =
  [T] extends [never]
      ? []
      : Acc extends any
          ? [Acc, ...Permutation<Exclude<T, Acc>>]
          : never;

引入泛型

引入泛型AccAcc是一个累积器,默认值为T,用来存储当前的排列

条件类型

type Permutation<T, Acc = T> =
  [T] extends [never]
      ? []
      : Acc extends any
          ? [Acc, ...Permutation<Exclude<T, Acc>>]
          : never;
  • 如果Tnever,返回一个空数组[]
  • 如果T不是never,使用Acc extends any进行分布式条件判断,将条件应用到联合类型的每个成员上
  • [Acc, ...Permutation<Exclude<T, Acc>>]
    • 将当前的成员添加到结果中
    • 通过Exclude<T, Acc>排除已经选取的元素,再递归调用Permutation对剩余元素进行排列,添加到数组后面

Case 推导思路

Case 1: Permutation<'A' | 'B' | 'C'>
  • 初始状态:

    • T'A' | 'B' | 'C'Acc默认是'A' | 'B' | 'C'
  • 递归步骤:

    • 首先检查[T] extends [never],此条件不成立,进入下一个条件分支

    • 接着,由于Acc是一个联合类型,TypeScript 的分布式条件判断会对Acc的每个成员进行单独处理:

      • Acc'A'时,调用Permutation<Exclude<'A' | 'B' | 'C', 'A'>>,得出Permutation<'B' | 'C'>。此时,T变为'B' | 'C'Acc默认是'B' | 'C'

        • [T] extends [never]不成立,进入下一个条件分支

        • 对于新的Acc的每个成员:

          • Acc'B'时,调用Permutation<Exclude<'B' | 'C', 'B'>>,得出Permutation<'C'>。此时,T变为'C'Acc默认是'C'

            • [T] extends [never]不成立,进入下一个条件分支

            • 对于新的Acc的每个成员:

              • Acc'C'时,调用Permutation<Exclude<'C', 'C'>>,得出Permutation<never>。此时,TneverAcc默认是never

              • [T] extends [never]成立,返回[]

              • 最终得到['A', 'B', 'C']作为其中一个排列

          • Acc'C'时,调用Permutation<Exclude<'B' | 'C', 'C'>>,得出Permutation<'B'>。此时,T变为'B'Acc默认是'B'

            • [T] extends [never]不成立,进入下一个条件分支

            • 对于新的Acc的每个成员:

              • Acc'B'时,调用Permutation<Exclude<'B', 'B'>>,得出Permutation<never>。此时,TneverAcc默认是never

              • [T] extends [never]成立,返回[]

              • 最终得到['A', 'C', 'B']作为其中一个排列

      • Acc'B'时,调用Permutation<Exclude<'A' | 'B' | 'C', 'B'>>,得出Permutation<'A' | 'C'>。此时,T变为'A' | 'C'Acc默认是'A' | 'C'

        • [T] extends [never]不成立,进入下一个条件分支

        • 对于新的Acc的每个成员:

          • Acc'A'时,调用Permutation<Exclude<'A' | 'C', 'A'>>,得出Permutation<'C'>。此时,T变为'C'Acc默认是'C'

            • [T] extends [never]不成立,进入下一个条件分支

            • 对于新的Acc的每个成员:

              • Acc'C'时,调用Permutation<Exclude<'C', 'C'>>,得出Permutation<never>。此时,TneverAcc默认是never

              • [T] extends [never]成立,返回[]

              • 最终得到['B', 'A', 'C']作为其中一个排列

          • Acc'C'时,调用Permutation<Exclude<'A' | 'C', 'C'>>,得出Permutation<'A'>。此时,T变为'A'Acc默认是'A'

            • [T] extends [never]不成立,进入下一个条件分支

            • 对于新的Acc的每个成员:

              • Acc'A'时,调用Permutation<Exclude<'A', 'A'>>,得出Permutation<never>。此时,TneverAcc默认是never

              • [T] extends [never]成立,返回[]

              • 最终得到['B', 'C', 'A']作为其中一个排列

      • Acc'C'时,调用Permutation<Exclude<'A' | 'B' | 'C', 'C'>>,得出Permutation<'A' | 'B'>。此时,T变为'A' | 'B'Acc默认是'A' | 'B'

        • [T] extends [never]不成立,进入下一个条件分支

        • 对于新的Acc的每个成员:

          • Acc'A'时,调用Permutation<Exclude<'A' | 'B', 'A'>>,得出Permutation<'B'>。此时,T变为'B'Acc默认是'B'

            • [T] extends [never]不成立,进入下一个条件分支

            • 对于新的Acc的每个成员:

              • Acc'B'时,调用Permutation<Exclude<'B', 'B'>>,得出Permutation<never>。此时,TneverAcc默认是never

              • [T] extends [never]成立,返回[]

              • 最终得到['C', 'A', 'B']作为其中一个排列

          • Acc'B'时,调用Permutation<Exclude<'A' | 'B', 'B'>>,得出Permutation<'A'>。此时,T变为'A'Acc默认是'A'

            • [T] extends [never]不成立,进入下一个条件分支

            • 对于新的Acc的每个成员:

              • Acc'A'时,调用Permutation<Exclude<'A', 'A'>>,得出Permutation<never>。此时,TneverAcc默认是never

              • [T] extends [never]成立,返回[]

              • 最终得到['C', 'B', 'A']作为其中一个排列

最终,Permutation<'A' | 'B' | 'C'>的结果是['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']