将类型推到元组的末尾,并可选择跳过

2024-04-02

I've 发现 /a/58547161/4928642如何将类型推到元组的末尾:

type Cons<H, T extends readonly any[]> =
    ((head: H, ...tail: T) => void) extends ((...cons: infer R) => void) ? R : never;

type Push<T extends readonly any[], V>
    = T extends any ? Cons<void, T> extends infer U ?
    { [K in keyof U]: K extends keyof T ? T[K] : V } : never : never;

一般来说它是有效的:

type A = Push<[1, 2, 3], 4>;  // [1, 2, 3, 4]
type B = Push<[1, 2, 3?], 4>; // [1, 2, 3 | undefined, 4?]

但我实际上希望它不包含未定义,如果某些参数是可选的:

A = [1, 2, 3, 4]
B = [1, 2, 3, 4] | [1, 2, 4] // How can I get this?
B = [1, 2, 3 | undefined, 4] | [1, 2, 4] // or this?

Demo https://www.typescriptlang.org/play/#code/C4TwDgpgBAwg9gOwM4B4ASAaKAVKEAewECAJklAE4QCGJiANiFNQiANoC6AfFALwCwAKCgioACjEALGiQBcUTFAB0K4NQCW9edgCUfHgDc46knoJFS5CSqUBjREnnqEAMwgUoAJT29Dx01AA-F5Q8ggQBu4A3EJCoJBQAAoArkiSKLjmxGSUMgxMLOwcWABqXEKifDh4hNnkhUGwDihGJljYPFmWUM5uHgCqQRWiAN5QbADSPQhQANYQIHAuUP0c8lNdOfOLy7jB2JMcoVAlUAC+x+GRHmER0bGC8dAAglUpaShsAIxYAExYAGZilAACxcKIiAD0kPGPyg-ygAKwII4DyeUAAQm9Uulvn9AYFgWCIdDYfjEVAAD5QZKkCAuZwQEjIwnDSpQmEAI2SwCgAEkoAB3Fi89S84BwKCc6AiPHwwHIo7UuUIlFAA


其实我正在尝试做:

function doSmth<F extends (...args: any) => number>(f: F, ...args: Push<Parameters<F>, string>) {
  var x: string = args.pop();
  return f(...args) + x;
}

function f1(x: number, y: number) {
  return x + y;
}

function f2(x: number, y?: number) {
  return x + (y || 0);
}

doSmth(f1, 1, 2, "A");
doSmth(f2, 1, 2, "B");
doSmth(f2, 1, "C"); // Problem here: should compile, but it doen't
doSmth(f2, 1, 2);   // And here: should not compile as last is not a string, but it does
doSmth(f2, 1, undefined, "D"); // Possible, but actually I do not care

更新:TypeScript 4.0 将具有可变元组类型 https://github.com/microsoft/TypeScript/pull/39094,这将允许更灵活的内置元组操作。Push<T, V>将简单地实现为[...T, V].


TS4.0 之前的答案:

呃,为什么?!咳咳,我的意思是,我也许可以做到这一点,但是涉及的类型杂耍越多,我就越不建议为任何重要的事情这样做。有一个图书馆叫ts-工具带 https://pirix-gh.github.io/ts-toolbelt/这接近于“官方支持” https://github.com/microsoft/TypeScript/pull/33810通过 TypeScript (尽管它不适用于操场 https://www.typescriptlang.org/play/, 至少not yet https://github.com/microsoft/TypeScript-Website/issues/92,所以我不打算做一个需要它的 Stack Overflow 答案),你可以在其中构建一些可行的东西。

我的处理方法是将带有可选元素的元组转换为不带有可选元素的元组的并集。不幸的是,我缺少一种内置的方法来获取数字类型,例如6并获得该长度的元组。因此,我正在制作一个可以映射的各种长度元组的硬编码列表。如果您需要它来处理更长的元组,您可以扩展它。开始了:

type Cons<H, T extends readonly any[]> =
    ((head: H, ...tail: T) => void) extends ((...cons: infer R) => void) ? R : never;

这只是标准Cons<1, [2,3,4]>变成[1,2,3,4].

type Tup = [[], [0], [0, 0], [0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]; // make as long as you need

这是元组的大列表。所以Tup[4] is [0,0,0,0], etc.

type TruncateTuple<T extends readonly any[], N extends number> = Extract<
    Tup[N] extends infer R ? { [K in keyof R]: K extends keyof T ? T[K] : never }
    : never, readonly any[]>;

该类型需要一个元组T和一个长度N并截断T到长度N. So TruncateTuple<[1,2,3,4], 2>应该[1,2]。它的工作原理是获取一个长度元组N from Tup,并用来自的属性映射它T.

type OptTupleToUnion<T extends readonly any[]> =
    TruncateTuple<Required<T>, T['length']>;

这是主要事件...OptTupleToUnion需要一个元组T并从中生成非可选元组的并集。它的工作原理是截断Required<T>(那是,T可选元素变成必需元素)到长度T['length']这是可能长度的并集T. So OptTupleToUnion<[1,2,3?,4?]>应该成为[1,2] | [1,2,3] | [1,2,3,4].

然后我会重命名我的旧的Push不妨碍_Push:

type _Push<T extends readonly any[], V>
    = T extends any ? Cons<void, T> extends infer U ?
    { [K in keyof U]: K extends keyof T ? T[K] : V } : never : never;

和做Push<T, V> act on OptTupleToUnion<T>代替T:

type Push<T extends readonly any[], V> = T extends any ?
    _Push<OptTupleToUnion<T>, V> : never;

(与相同的T extends any ? ..T.. : never以确保工会得到分配)

让我们看看它是否有效:

type A = Push<[1, 2, 3], 4>;  // [1, 2, 3, 4]
type B = Push<[1, 2, 3?], 4>; // [1, 2, 3, 4] | [1, 2, 4]

是的,看起来不错。 ????如果你开始在这里要求更多功能,我可能不得不放弃......也许其他人有更多的耐力!

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将类型推到元组的末尾,并可选择跳过 的相关文章

随机推荐