在 TypeScript 中双重展开嵌套可变元组
Double expand nested variadic tuples in TypeScript
tl;dr: 我想强烈输入以下内容。
const foo = [ 'a' ] as const;
const bar = [ 1 ] as const;
const baz = [ true ] as const;
const concatted = foo.concat(bar, baz);
type Concatted = typeof concatted; // expect ['a', 1, true]
我已经想出如何为 0..n 个参数添加定义,但我想为任意数字添加定义,最好使用一个或两个定义。
假设我有:
const strArray = [ 'a' ] as const;
const numArray = [ 1 ] as const;
const concatenated = strArray.concat(numArray);
我们知道 concatenated 正好等于 ['a', 1]
。我已经想出如何为 concat()
编写一个类型定义,它给了我们这个。
declare global {
interface ReadonlyArray<T> {
concat<
A extends ReadonlyArray<T>,
I extends ReadonlyArray<unknown>
>(this: A, items: C): [...A, ...I];
}
}
type Concatenated = typeof concatenated; // => ['a', 1]
然而,JavaScript 的 Array.concat()
接受任意数量的数组。那么现在,让我们做
const strArray = [ 'a' ] as const;
const numArray = [ 1 ] as const;
const boolArray = [ true ] as const;
const concatenated = strArray.concat(numArray, boolArray); // => [ 'a', 1, true ]
在 TypeScript 4 variadic tuples 之前,解决方案类似于
declare global {
interface ReadonlyArray<T> {
concat<
A extends ReadonlyArray<T>,
I extends ReadonlyArray<unknown>
>(this: A, items: I): [...A, ...I];
concat<
A extends ReadonlyArray<T>,
I1 extends ReadonlyArray<unknown>,
I2 extends ReadonlyArray<unknown>
>(this: A, item1: I1, item2: I2): [...A, ...I1, ...I2];
// ...additional concat() definitions through I_n...
}
}
我希望使用 TypeScript 4,我可以做一些更简单的事情
declare global {
interface ReadonlyArray<T> {
concat<
A extends ReadonlyArray<T>,
I extends ReadonlyArray<ReadonlyArray<unknown>>
>(this: A, ...items: I): [...A, ...(...I)];
}
}
这显然行不通。我猜
有一些黑魔法
((val: ReadonlyArray<ReadonlyArray<unknown>>) => void) extends ((val: [infer U, ...infer R]) => void)
? [...U, ...<something something recurse with R>]
: never
我从未掌握的模式,也许与使用 Extract<>
的魔术串联,如 .
中所示
如果类型可以在没有任何魔法的情况下递归,那就太好了。然后我可以轻松地写:
type Concat<T extends ReadonlyArray<ReadonlyArray<unknown>>> =
T extends [ReadonlyArray<unknown>, ...infer U]
? [...T[0], ...Concat<U>]
: [];
interface ReadonlyArray<T> {
concat<
A extends ReadonlyArray<T>,
I extends ReadonlyArray<ReadonlyArray<unknown>>
>(this: A, ...items: I): [...A, ...Concat<I>];
}
顺便说一句,我一直不明白为什么递归类型——只要它们在 n 深度内解析——不受支持。无限递归当然是 bad/impossible,但我认为这种语法可以支持大小为 100 或更小的元组 easily/efficiently.
将在 Typescript 4.1 中添加对 recursive conditional types 的官方支持
有了它,我们将能够:
type Concat<T> =
T extends [infer Head, ...infer Tail]
? Head extends ReadonlyArray<unknown>
? [...Concat<Head>, ...Concat<Tail>] : [Head, ...Concat<Tail>]
: T
以上定义允许传递数组 and/or 值以连接成一个新数组(就像常规 concat)
递归限制为 50 个嵌套类型实例化
tl;dr: 我想强烈输入以下内容。
const foo = [ 'a' ] as const;
const bar = [ 1 ] as const;
const baz = [ true ] as const;
const concatted = foo.concat(bar, baz);
type Concatted = typeof concatted; // expect ['a', 1, true]
我已经想出如何为 0..n 个参数添加定义,但我想为任意数字添加定义,最好使用一个或两个定义。
假设我有:
const strArray = [ 'a' ] as const;
const numArray = [ 1 ] as const;
const concatenated = strArray.concat(numArray);
我们知道 concatenated 正好等于 ['a', 1]
。我已经想出如何为 concat()
编写一个类型定义,它给了我们这个。
declare global {
interface ReadonlyArray<T> {
concat<
A extends ReadonlyArray<T>,
I extends ReadonlyArray<unknown>
>(this: A, items: C): [...A, ...I];
}
}
type Concatenated = typeof concatenated; // => ['a', 1]
然而,JavaScript 的 Array.concat()
接受任意数量的数组。那么现在,让我们做
const strArray = [ 'a' ] as const;
const numArray = [ 1 ] as const;
const boolArray = [ true ] as const;
const concatenated = strArray.concat(numArray, boolArray); // => [ 'a', 1, true ]
在 TypeScript 4 variadic tuples 之前,解决方案类似于
declare global {
interface ReadonlyArray<T> {
concat<
A extends ReadonlyArray<T>,
I extends ReadonlyArray<unknown>
>(this: A, items: I): [...A, ...I];
concat<
A extends ReadonlyArray<T>,
I1 extends ReadonlyArray<unknown>,
I2 extends ReadonlyArray<unknown>
>(this: A, item1: I1, item2: I2): [...A, ...I1, ...I2];
// ...additional concat() definitions through I_n...
}
}
我希望使用 TypeScript 4,我可以做一些更简单的事情
declare global {
interface ReadonlyArray<T> {
concat<
A extends ReadonlyArray<T>,
I extends ReadonlyArray<ReadonlyArray<unknown>>
>(this: A, ...items: I): [...A, ...(...I)];
}
}
这显然行不通。我猜
有一些黑魔法((val: ReadonlyArray<ReadonlyArray<unknown>>) => void) extends ((val: [infer U, ...infer R]) => void)
? [...U, ...<something something recurse with R>]
: never
我从未掌握的模式,也许与使用 Extract<>
的魔术串联,如
如果类型可以在没有任何魔法的情况下递归,那就太好了。然后我可以轻松地写:
type Concat<T extends ReadonlyArray<ReadonlyArray<unknown>>> =
T extends [ReadonlyArray<unknown>, ...infer U]
? [...T[0], ...Concat<U>]
: [];
interface ReadonlyArray<T> {
concat<
A extends ReadonlyArray<T>,
I extends ReadonlyArray<ReadonlyArray<unknown>>
>(this: A, ...items: I): [...A, ...Concat<I>];
}
顺便说一句,我一直不明白为什么递归类型——只要它们在 n 深度内解析——不受支持。无限递归当然是 bad/impossible,但我认为这种语法可以支持大小为 100 或更小的元组 easily/efficiently.
将在 Typescript 4.1 中添加对 recursive conditional types 的官方支持
有了它,我们将能够:
type Concat<T> =
T extends [infer Head, ...infer Tail]
? Head extends ReadonlyArray<unknown>
? [...Concat<Head>, ...Concat<Tail>] : [Head, ...Concat<Tail>]
: T
以上定义允许传递数组 and/or 值以连接成一个新数组(就像常规 concat)
递归限制为 50 个嵌套类型实例化