首页 > 解决方案 > 如何使用对象接口的键路径数组的联合类型作为接口中这些键路径的联合类型?

问题描述

我为对象接口中的键路径类型创建了一个泛型类型:TypeOfPath<T, P>. 路径是对象接口键的数组以及任何嵌套的对象键路径,迭代。

我还创建了一些辅助类型来将数组作为类型处理。 Head<T>定义数组的第一个元素(可以是undefined),或者never如果参数不是数组。 Tail<T>定义数组的其余元素(可以为空),或者never如果参数不是数组。我有点惊讶的类似列表的标准东西不是语言的一部分。

type Head<T> = T extends unknown[] ? T[0] : never;
type Tail<T> = T extends unknown[]
  ? T extends [unknown, ...infer R]
    ? R
    : []
  : never;
type TypeOfPath<T, P> = T extends object
  ? Head<P> extends keyof T
    ? Tail<P> extends []
      ? T[Head<P>]
      : TypeOfPath<T[Head<P>], Tail<P>>
    : never
  : never;

乍一看,所有这些类型似乎都按预期工作。与interface I { foo: { bar: boolean }; baz?: number }

但是,一旦我尝试使用TypeOfPath<I, ['foo'] | ['foo', 'bar'] | ['baz']>,类型最终是never. 我希望类型是{ bar: boolean } | boolean | number | undefined.

似乎P参数的联合TypeOfPath<T, P>总是never。我认为联合泛型是分布式的。我在这里遗漏了一些明显的(或不那么明显的)吗?

标签: typescripttypescript-generics

解决方案


你得到never是因为你的联合包含不同长度的键数组。您的每个泛型都将其参数作为联合接收,因此当我们TypeOfPath<I, ['foo'] | ['foo', 'bar'] | ['baz']>逐步评估时会出现以下情况:

// Set up the arguments to TypeOfPath.
type T = I;
type P = ['foo'] | ['foo', 'bar'] | ['baz'];
// Evaluate the first ternary condition.
type condition1 = T extends object ? true : false; // true
// So take the true branch and evaluate the next condition.
type condition2 = Head<P> extends keyof T ? true : false; // true
// So take the true branch and evaluate the next condition.
type condition3 = Tail<P> extends [] ? true : false; // false
// So take the false branch, calling TypeOfPath recursively.
// Set up the arguments to the new TypeOfPath call.
type Tbis = T[Head<P>]; // number | { bar: boolean } | undefined
type Pbis = Tail<P>; // [] | ['bar']
// Now back to the first ternary condition.
type condition1bis = Tbis extends object ? true : false; // false
// False, because not every branch is an object: two of them have already resolved.
// That leaves us with the false branch, which is never.

TypeOfPath最简单的解决方法是在调用之外放置一个不同长度数组的联合;这将起作用:

TypeOfPath<I, ['foo'] | ['baz']> | TypeOfPath<I, ['foo', 'bar']>

如果您希望能够TypeOfPath按照您的意图传递不同长度数组的联合,我认为解决方案将涉及将联合的对象ExcludeExtract非对象成员分开以进行单独处理的实用程序类型的组合,但是实施的总体复杂性会更大。


推荐阅读