将 Typescript 表达式简化为递归

Simplify Typescript expression to recursive

如何将这种类型简化为递归类型:

[
keyof T,
keyof T[keyof T],
keyof T[keyof T][keyof T[keyof T]]
]

TypeScript 并不真正支持递归发生在相同对象深度的递归类型,例如在元组中。你需要循环条件类型,这目前是被禁止的(参见 microsoft/TypeScript#26980). You can trick the compiler into evaluating such types, but these tricks are really not supported (see here and here),所以如果你使用这些技巧并且编译器爆炸,那么你的工作就是捡起损坏的编译器的碎片(这意味着,不要在生产代码中使用它)。

我能想象到的最接近的是这样的:


首先,我想了解一下您的类型中未指定的 T

type Manual<T> = [
    keyof T,
    keyof T[keyof T],
    keyof T[keyof T][keyof T[keyof T]]
]

现在我将定义类型函数 KXK<T> 只是将 keyof 映射到 T 的属性上,而 X<T> 接受一个元组参数 T,将 V[keyof V] 映射到它的每个属性上,并在其前面添加第一个元素:

type K<T> = { [K in keyof T]: keyof T[K] };
type X<T extends any[]> =
    ((h: T[0], ...t: { [K in keyof T]: T[K][keyof T[K]] }) => void) extends
    ((...r: infer R) => void) ? R : never;

那么,你的类型Manual<T>可以这样表达:

type PartiallyAutomated<T> = K<X<X<[T]>>>;
// type PartiallyAutomated<T> = 
//  [keyof T, keyof T[keyof T], keyof T[keyof T][keyof T[keyof T]]]

到目前为止我所做的一切都是支持的,所以你可以写K<X<X<[T]>>>K<X<X<X<[T]>>>>等。如果你想告诉编译器"do K<X<X<...[T]...>>> to produce a tuple of length N",那就涉及欺骗循环条件类型,或以某种方式展开循环。


下面是作弊方法,即NOT SUPPORTED:

// not supported
type Evil<N extends number, T, V extends any[] = [T]> =
    { 0: K<V>, 1: Evil<N, T, X<V>> }[N extends V['length'] ? 0 : 1]

type FullyAutomated<T> = Evil<3, T>;
// type FullyAutomated<T> =
//  [keyof T, keyof T[keyof T], keyof T[keyof T][keyof T[keyof T]]]

或者您可以将(非法)循环展开为一组(合法但多余的)几乎相同的类型,如下所示:

// supported but redundant
type Redundant<N extends number, T, V extends any[] = [T]> = N extends V['length'] ? K<V> : R0<N, T, X<V>>;
type R0<N extends number, T, V extends any[] = [T]> = N extends V['length'] ? K<V> : R1<N, T, X<V>>;
type R1<N extends number, T, V extends any[] = [T]> = N extends V['length'] ? K<V> : R2<N, T, X<V>>;
type R2<N extends number, T, V extends any[] = [T]> = N extends V['length'] ? K<V> : R3<N, T, X<V>>;
type R3<N extends number, T, V extends any[] = [T]> = N extends V['length'] ? K<V> : R4<N, T, X<V>>;
type R4<N extends number, T, V extends any[] = [T]> = N extends V['length'] ? K<V> : R5<N, T, X<V>>;
type R5<N extends number, T, V extends any[] = [T]> = N extends V['length'] ? K<V> : R6<N, T, X<V>>;
type R6<N extends number, T, V extends any[] = [T]> = N extends V['length'] ? K<V> : RX<N, T, X<V>>;
type RX<N extends number, T, V extends any[] = [T]> = N extends V['length'] ? K<V> : never; // bail out at depth 7

type AlsoAutomated<T> = Redundant<3, T>;
// type AlsoAutomated<T> = 
//  [keyof T, keyof T[keyof T], keyof T[keyof T][keyof T[keyof T]]]

所有 PartiallyAutomatedFullyAutomatedAlsoAutomated 都计算为与 Manual 相同类型的函数。这值得么?我有点怀疑,因为你说 "simplify" 并且所有的解决方案似乎都比你原来的定义更复杂。也许如果你想要一个长度为 6 的元组而不是像

这样的 3
[keyof T, keyof T[keyof T], keyof T[keyof T][keyof T[keyof T]], 
 keyof T[keyof T][keyof T[keyof T]][keyof T[keyof T][keyof T[keyof T]]], 
 keyof T[keyof T][keyof T[keyof T]][keyof T[keyof T][keyof T[keyof T]]][
 keyof T[keyof T][keyof T[keyof T]][keyof T[keyof T][keyof T[keyof T]]]], 
 keyof T[keyof T][keyof T[keyof T]][keyof T[keyof T][keyof T[keyof T]]][
 keyof T[keyof T][keyof T[keyof T]][keyof T[keyof T][keyof T[keyof T]]]][
 keyof T[keyof T][keyof T[keyof T]][keyof T[keyof T][keyof T[keyof T]]][
 keyof T[keyof T][keyof T[keyof T]][keyof T[keyof T][keyof T[keyof T]]]]]]

您会开始看到让编译器为您生成类型的好处,但现在我建议您的原始元组足够简单。


好的,希望对您有所帮助;祝你好运!

Playground link to code