gpt4 book ai didi

typescript :定义为元组的传播函数参数

转载 作者:行者123 更新时间:2023-12-04 14:44:35 25 4
gpt4 key购买 nike

我有一个 typescript 项目,我正在调用 Date函数内部。我想使用与 Date 中的构造函数相同的函数参数这是重载的:

interface DateConstructor {
new(): Date;
new(value: number | string | Date): Date;
new(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): Date;
// ...
}
我想使用扩展运算符将参数传递给 Date 的构造函数.所以我的函数应该是这样的:
function myFn(...args: DateComponents): Date {
return new Date(...args)
}
在我的情况下,我实际上并没有返回日期,但这仅用于测试...
现在的问题是如何定义 DateComponents .我成功实现了 DateConstructor的3个重载签名彼此分开。
// Case A: No argument provided
interface A {
(): Date;
}
const a: A = function (...args: []): Date {
return new Date(...args);
}

// Case B: 1 argument provided
interface B {
(value: number | string | Date): void;
}
const b: B = function (...args: [number | string | Date]): Date {
return new Date(...args);
}

// Case C: Between 2 and 7 arguments provided
interface C {
(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): void;
}
const c: C = function (...args: [number, number, number?, number?, number?, number?, number?]): Date {
return new Date(...args);
}
到目前为止一切顺利,一切正常。
  • 答:空元组用于覆盖不传递参数的情况
  • 乙:具有一个元素的元组涵盖了单个数字、字符串或 Date 的情况。对象被传递
  • 电话:具有 2 个必需元素和 5 个可选数字元素的元组涵盖了传递 2-7 个参数的情况

  • 如果我尝试将这 3 个示例合并为 1 个,我会得到:
    interface D {
    (): void;
    (value: number | string | Date): void;
    (year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): void;
    }
    type DateComponents = [] | [number | string | Date] | [number, number, number?, number?, number?, number?, number?];
    const d: D = function (...args: DateComponents): Date {
    return new Date(...args); // <-- ERROR: Expected 0-7 arguments, but got 0 or more.
    }
    我收到一条错误消息,说 Typescript 认为可以将 7 个以上的参数传递给 Date构造函数。我不明白为什么因为我的类型 DateComponents明确定义了 0-7 个元素的元组。
    我可以通过使用一些条件和类型断言来解决这个问题,但我希望有一个更漂亮的解决方案?
    See the code in the Playground here
    有什么办法解决这个问题吗?非常感谢!

    最佳答案

    根本原因是 TypeScript 不支持同时解析对具有多个调用/构造签名的重载函数/构造函数的调用。来电new Date(...args);是一个单一的调用,但为了让它被接受,编译器必须打破 DateComponents进入其联合成员,并查看每个成员都可以分配给至少一个构造签名。相反,它看到没有一个 Date构造签名适用于整个DateComponents联合,并放弃。
    请注意,您看到的错误的具体措辞有点像红鲱鱼;编译器无法接受输入并尝试将其硬塞到有关参数数量的可用错误消息中。这种情况以前发生过(例如, microsoft/TypeScript#28010microsoft/TypeScript#20372 ),但似乎并不是要解决的首要任务。
    无论如何,GitHub 中有一个(相当长的)开放功能请求,要求重载函数接受参数联合;见 microsoft/TypeScript#14107 .目前还不清楚这是否会发生。

    所以,你可以做什么?最简单的方法就是使用 type assertion :

    const d: D = function (...args: DateComponents): Date {
    return new Date(...args as ConstructorParameters<typeof Date>);
    }
    我知道你想要一些“更漂亮”的东西,但相信我,我能想到的任何修复方法都会变得更丑。

    例如,您可以手动引导编译器通过不同的可能性,并在其中放入一堆冗余代码:
    const e: D = function (...args: DateComponents): Date {
    return args.length === 0 ? new Date(...args) :
    args.length === 1 ? new Date(...args) :
    new Date(...args);
    }
    这也不漂亮。

    或者,您可以尝试构建一些代码,将重载的构造函数转换为采用联合类型参数的非重载构造函数。即 manually simulate an implementation of microsoft/TypeScript#14107 .如果您这样做,调用将如下所示:
    const f: D = function (...args: DateComponents): Date {
    return new (unifyConstructorOverloads(Date))(...args);
    }
    这本身并不丑陋。但是 unifyConstructorOverloads的定义会是这样的:
    type UnifyConstructorOverloads<T extends new (...args: any) => any> =
    new (...args: ConstructorParameters<ConstructorOverloads<T>[number]>) =>
    InstanceType<ConstructorOverloads<T>[number]>;

    const unifyConstructorOverloads = <T extends new (...args: any) => any>(f: T) => f as
    UnifyConstructorOverloads<T>;
    这变得越来越简单,使用类型断言,并取决于 ConstructorOverloads<T> 的定义,一个假设的类型函数,它接受一个重载的构造函数并将其多个构造签名分离成一个元组。据我所知,没有编程方式可以做到这一点,因此您必须模拟达到一定数量的重载(例如 5):
    type ConstructorOverloads<T> =
    T extends {
    new(...args: infer A1): infer R1; new(...args: infer A2): infer R2;
    new(...args: infer A3): infer R3; new(...args: infer A4): infer R4;
    new(...args: infer A5): infer R5;
    } ? [
    new (...args: A1) => R1, new (...args: A2) => R2,
    new (...args: A3) => R3, new (...args: A4) => R4,
    new (...args: A5) => R5
    ] : T extends {
    new(...args: infer A1): infer R1; new(...args: infer A2): infer R2;
    new(...args: infer A3): infer R3; new(...args: infer A4): infer R4
    } ? [
    new (...args: A1) => R1, new (...args: A2) => R2,
    new (...args: A3) => R3, new (...args: A4) => R4
    ] : T extends {
    new(...args: infer A1): infer R1; new(...args: infer A2): infer R2;
    new(...args: infer A3): infer R3
    } ? [
    new (...args: A1) => R1, new (...args: A2) => R2,
    new (...args: A3) => R3
    ] : T extends {
    new(...args: infer A1): infer R1; new(...args: infer A2): infer R2
    } ? [
    new (...args: A1) => R1, new (...args: A2) => R2
    ] : T extends {
    new(...args: infer A1): infer R1
    } ? [
    new (...args: A1) => R1
    ] : any
    就美学而言,它远远超过“丑陋”,可能徘徊在“怪诞”周围。如果你打算在你的代码库的很多地方做这种重载统一的事情,我可以想象关闭 ConstructorOverloads (以及类似的 Overloads 对于常规函数,请参阅 this question 获取该代码)到一个未点亮的库中,这样您就可以使用它而无需直视其可恶的面貌。

    但是如果你只做几次,我强烈建议使用类型断言并继续。
    Playground link to code

    关于 typescript :定义为元组的传播函数参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64738810/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com