使用不处理单个重载中的整个联合的重载函数处理联合
Handling union with an overloaded function that doesn't handle the whole union within a single overload
我创建了一个带有一个参数的重载函数,并尝试在一个被各种重载“完全覆盖”的联合中调用 pass - 就像这样:
function overloaded(id: number): number;
function overloaded(ids: number[]): boolean;
function overloaded(idOrIds: number | number[]): number | boolean {
if (Array.isArray(idOrIds)) return 0;
return false;
}
function caller(idOrIds: number | number[]) {
return overloaded(idOrIds);
}
但是我在调用 overloaded
时出现以下错误:
TS2769: No overload matches this call.
Overload 1 of 2, '(id: number): void', gave the following error. Argument of type 'number | number[]' is not assignable to parameter of type 'number'. Type 'number[]' is not assignable to type 'number'.
Overload 2 of 2, '(ids: number[]): void', gave the following error. Argument of type 'number | number[]' is not assignable to parameter of type 'number[]'. Type 'number' is not assignable to type 'number[]'.
看起来 TS 想要一个单一的重载来覆盖整个联合 - 我怎样才能让我的重载工作?
TypeScript 不会自动将多个调用签名合成为单个签名,这些签名采用来自不同调用签名的参数联合。有一个悬而未决的问题表明:microsoft/TypeScript#14107,但到目前为止还没有发生太多事情。有人必须提出一个不会降低编译器性能或破坏 real-world 代码的实现。
您可以自己提供所需的联合调用签名,如下所示:
function overloaded(id: number): number;
function overloaded(ids: number[]): boolean;
function overloaded(idOrIds: number | number[]): number | boolean; // add this
function overloaded(idOrIds: number | number[]): number | boolean {
if (Array.isArray(idOrIds)) return 0;
return false;
}
function caller(idOrIds: number | number[]) {
return overloaded(idOrIds); // okay
}
或者,您可以将多个调用签名合并为一个通用 conditional 调用签名:
function genericConditional<T extends number | number[]>(
idOrIds: T
): T extends number ? number : boolean;
function genericConditional(idOrIds: number | number[]): number | boolean {
if (Array.isArray(idOrIds)) return 0;
return false;
}
const n = genericConditional(123); // number
const b = genericConditional([123]); // boolean
const nb = genericConditional(Math.random() < 0.5 ? 123 : [123]); // number | boolean
请注意,不幸的是,通用条件仍然需要将实现与调用签名分开(通过重载或通过 type assertion) because the compiler does not understand how to verify that a return value is assignable to a generic conditional type. There's an open issue for that too, microsoft/TypeScript#33912。
如果您只有几个重载,那么额外的重载就可以了。但是随着重载数量的增加,添加调用签名来处理它们的每个子集变得站不住脚了。在这些情况下,您会发现单个通用条件调用签名的扩展性会更好。
我创建了一个带有一个参数的重载函数,并尝试在一个被各种重载“完全覆盖”的联合中调用 pass - 就像这样:
function overloaded(id: number): number;
function overloaded(ids: number[]): boolean;
function overloaded(idOrIds: number | number[]): number | boolean {
if (Array.isArray(idOrIds)) return 0;
return false;
}
function caller(idOrIds: number | number[]) {
return overloaded(idOrIds);
}
但是我在调用 overloaded
时出现以下错误:
TS2769: No overload matches this call.
Overload 1 of 2, '(id: number): void', gave the following error. Argument of type 'number | number[]' is not assignable to parameter of type 'number'. Type 'number[]' is not assignable to type 'number'.
Overload 2 of 2, '(ids: number[]): void', gave the following error. Argument of type 'number | number[]' is not assignable to parameter of type 'number[]'. Type 'number' is not assignable to type 'number[]'.
看起来 TS 想要一个单一的重载来覆盖整个联合 - 我怎样才能让我的重载工作?
TypeScript 不会自动将多个调用签名合成为单个签名,这些签名采用来自不同调用签名的参数联合。有一个悬而未决的问题表明:microsoft/TypeScript#14107,但到目前为止还没有发生太多事情。有人必须提出一个不会降低编译器性能或破坏 real-world 代码的实现。
您可以自己提供所需的联合调用签名,如下所示:
function overloaded(id: number): number;
function overloaded(ids: number[]): boolean;
function overloaded(idOrIds: number | number[]): number | boolean; // add this
function overloaded(idOrIds: number | number[]): number | boolean {
if (Array.isArray(idOrIds)) return 0;
return false;
}
function caller(idOrIds: number | number[]) {
return overloaded(idOrIds); // okay
}
或者,您可以将多个调用签名合并为一个通用 conditional 调用签名:
function genericConditional<T extends number | number[]>(
idOrIds: T
): T extends number ? number : boolean;
function genericConditional(idOrIds: number | number[]): number | boolean {
if (Array.isArray(idOrIds)) return 0;
return false;
}
const n = genericConditional(123); // number
const b = genericConditional([123]); // boolean
const nb = genericConditional(Math.random() < 0.5 ? 123 : [123]); // number | boolean
请注意,不幸的是,通用条件仍然需要将实现与调用签名分开(通过重载或通过 type assertion) because the compiler does not understand how to verify that a return value is assignable to a generic conditional type. There's an open issue for that too, microsoft/TypeScript#33912。
如果您只有几个重载,那么额外的重载就可以了。但是随着重载数量的增加,添加调用签名来处理它们的每个子集变得站不住脚了。在这些情况下,您会发现单个通用条件调用签名的扩展性会更好。