type-challenges:Tuple to Nested Object

18 阅读2分钟

Tuple to Nested Object

问题描述

给定一个只包含字符串类型的元组类型 T 和一个类型 U,递归地构建一个对象。

type a = TupleToNestedObject<['a'], string> // {a: string}
type b = TupleToNestedObject<['a', 'b'], number> // {a: {b: number}}
type c = TupleToNestedObject<[], boolean> // boolean. 如果元组为空,返回泛型 U 即可。
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'type cases = [
  Expect<Equal<TupleToNestedObject<['a'], string>, { a: string }>>,
  Expect<Equal<TupleToNestedObject<['a', 'b'], number>, { a: { b: number } }>>,
  Expect<Equal<TupleToNestedObject<['a', 'b', 'c'], boolean>, { a: { b: { c: boolean } } }>>,
  Expect<Equal<TupleToNestedObject<[], boolean>, boolean>>
]
​
// ============= Your Code Here =============
// 答案1
type TupleToNestedObject<T, U> = T extends [infer F, ...infer R]
  ? {
      [K in F & string]: TupleToNestedObject<R, U>
    }
  : U
// 答案2
  type TupleToNestedObject<T extends readonly PropertyKey[], U> = T extends readonly [
    infer K,
    ...infer R extends readonly PropertyKey[]
  ]
    ? { [P in K as P extends PropertyKey ? P : never]: TupleToNestedObject<R, U> }
    : U
    
// 答案3
type TupleToNestedObject<T extends readonly PropertyKey[], U> = T extends readonly [
  infer K extends PropertyKey,
  ...infer R extends readonly PropertyKey[]
]
  ? { [P in K]: TupleToNestedObject<R, U> }
  : U

这里从题目可知,第一个泛型 T 应该是个元组,并且元组中的每一项都会是对象的 key 类型,也就是 string | number | symbol ,也可以用内置类型 PropertyKey,所以泛型约束应该是T extends readonly PropertyKey[], 其次不难看出需要用到递归来判断,我们的思路是,从元组中一个一个的取出类型,然后将其作为对象的 key,继续递归将后面的内容作为 value,所以应该可以写出 TupleToNestedObject<R, U>,答案2和答案3这里约束更全面一些,那为什么我们在传入泛型的时候进行了约束,条件判断的时候还需要约束一次呢?是因为当你定义一个泛型参数 T 并对其进行约束 T extends readonly PropertyKey[] 时,你是在告诉 TypeScript 编译器,任何使用 TupleToNestedObject 的地方,T 必须是一个只读的属性键数组。这个约束是在类型定义阶段就确定的,确保了 T 符合预期的类型结构。然后,在条件类型 T extends readonly [infer K extends PropertyKey, ...infer R extends readonly PropertyKey[]] 中,infer K extends PropertyKey 是在条件类型被检查时应用的。这个推断是在编译器检查类型时进行的,它确保了从 T 中推断出的第一个元素 K 是一个有效的属性键。

总结:

为什么需要两次约束?

  • 泛型参数的约束:这是为了确保在使用 TupleToNestedObject 时,传入的类型 T 符合预期的结构。它是一个类型级别的约束,确保了类型安全。
  • 条件类型中的约束:这是为了确保在条件类型被展开时,能够正确地推断出类型。它是一个类型检查阶段的约束,确保了类型推断的正确性。