Typescript 4.5比较类型时堆栈深度过大



TS4.5发布了条件类型的尾部递归求值功能,我有这段代码。

type TupleOfLength<Len extends number, Arr extends any[] = []> = 
Arr["length"] extends Len ? Arr : TupleOfLength<Len, [...Arr, Arr["length"]]>;
// error: Excessive stack depth comparing types 'number' and 'keyof TupleOfLength<T["length"], []>'
type RandomIndex<T extends any[]> = Array<TupleOfLength<T["length"]>[number]>;
// can infer correct type, which is (0 | 1 | 2)[]
type RandomIndexArr = RandomIndex<["smile", "hey", "goodbye"]>;
// What I want is that having a tuple, and infer an array of tuple indexes

我搜索了一段时间,发现这个问题很有用,但在这种情况下,递归有一个明确的基本情况,而在我的情况下,Len可以是任何长度,Len与该问题的Depth非常相似,我如何在我的类型中添加另一个Depth泛型参数。

操场

用例:


type TupleOfLength<Len extends number, Arr extends any[] = []> = 
Arr["length"] extends Len ? Arr : TupleOfLength<Len, [...Arr, Arr["length"]]>;
// error: Excessive stack depth comparing types 'number' and 'keyof TupleOfLength<T["length"], []>'
type RandomIndex<T extends any[]> = TupleOfLength<T["length"]>[number];
// can infer correct type, which is 0 | 1 | 2
type RandomIndexRes = RandomIndex<["smile", "hey", "goodbye"]>;
class Father {}
class Son extends Father {}
class AnotherSon extends Father {}
class Test<T extends Father[]> {
anotherClasses!: T;
stuffNeedBindingToAnotherClassBasedOnIndex = ["hey", "goodbye", "smile", "punch"];
setAnotherClasses(...args: T): Array<RandomIndex<T>> {
this.anotherClasses = args;
/*
Here I want
hey => AnotherSon
goodbye => Father
smile => Son
punch => AnotherSon 
*/
return [2, 0, 1, 2] as Array<RandomIndex<T>>;
}
combine() {
// @ts-ignore
const order = this.setAnotherClasses(new Father(), new Son(), new AnotherSon());
Test.zip(order.map(i => this.anotherClasses[i]), this.stuffNeedBindingToAnotherClassBasedOnIndex);
}
// for sake of integrity, I gave the full function body of zip in my project
// Though silly here, the real scenario is way more complex that `zip` is quite useful 
static *zip<T extends Iterable<unknown>[]>(...iterables: T) {
type IterableType<T> = T extends Iterable<infer U> ? U : never;
type TransformIterableType<T extends Iterable<unknown>[]> = {
[K in keyof T]: IterableType<T[K]>;
};
const iterators = iterables.map((i) => i[Symbol.iterator]());
while (1) {
const results = iterators.map((iter) => iter.next());
if (results.some((res) => res.done)) return;
yield results.map((res) => res.value) as TransformIterableType<T>;
}
}
}

多亏了@jcalz,答案很简单,我想得太多了。

type ToNumber<T, R extends any[] = []> =
T extends `${R['length']}` ? R['length'] : ToNumber<T, [1, ...R]>;
type RandomStringIndex<T extends unknown[]> = Exclude<keyof T, keyof any[]>;
type RandomNumericIndex<T extends unknown[]> = ToNumber<RandomStringIndex<T>>;
// can infer correct type, which is 0 | 1 | 2
type RandomNumericIndexRes = RandomNumericIndex<["hello", "bye", "smile"]>;

最新更新