可以接受两个对象或合并对象的重载函数
Overloaded Function that can take two objects or a consolidated object
我有一个接口,我们称它为Worker
。它看起来像这样。
interface Worker {
doWork(key: string): number;
};
A Worker
接受一个键值并用它做一些事情。现在我想要一个间接调用 doWork
的函数,比如
function doWork(key: string, worker: Worker): number {
// Pretend there's more complicated stuff happening around it :)
return worker.doWork(key);
}
在大多数情况下这很好。但是,有些 Worker
实例仅对特定 key
值有意义。在那些情况下,我希望我的函数能够推断出 key
值,即
function doWork(worker: Worker & { key: string }): number {
// Pretend there's more complicated stuff happening around it :)
return worker.doWork(worker.key);
}
所以我的想法是使用 Typescript 重载签名将这两个函数合并为一个函数,这样您就可以用一个或两个参数调用这个函数。
function doWork(key: string, worker: Worker): number;
function doWork(worker: Worker & { key: string }): number;
function doWork(arg0: string | (Worker & { key: string }), arg1?: Worker): number {
if (typeof arg0 === 'string') {
const key: string = arg0;
const worker: Worker = arg1!; // non-null assertion
return worker.doWork(key);
} else {
const key: string = arg0.key;
const worker: Worker = arg0;
return worker.doWork(key);
}
}
这行得通,除了我在第 6 行有一个混乱的非空(嗯,非未定义)断言。因为函数 doWork
只能用 或者 (1) 一个 string
和一个 Worker
,或者 (2) 一个有钥匙的 Worker
,在我看来,一旦我检查 typeof arg0 === 'string'
,Typescript 应该能够推断出我们处于第一种情况,因此 arg1
是 而不是 未定义。但是如果我们删除 !
,
function doWork(key: string, worker: Worker): number;
function doWork(worker: Worker & { key: string }): number;
function doWork(arg0: string | (Worker & { key: string }), arg1?: Worker): number {
if (typeof arg0 === 'string') {
const key: string = arg0;
const worker: Worker = arg1; // Oops!
return worker.doWork(key);
} else {
const key: string = arg0.key;
const worker: Worker = arg0;
return worker.doWork(key);
}
}
我们收到一个错误。
$ tsc --strict overload.ts
overload.ts:11:11 - error TS2322: Type 'Worker | undefined' is not assignable to type 'Worker'.
Type 'undefined' is not assignable to type 'Worker'.
11 const worker: Worker = arg1;
~~~~~~
Found 1 error.
是否有类型安全的方法来编写此函数,不涉及绕过 Typescript(未经检查的向下转换,any
,等)?我可以向 Typescript 证明我的函数实现能够像它应该的那样处理这两个重载吗?
Overloaded functions are split into a set of call signatures that callers see, and a single implementation with a signature that the implementation sees. These are compared loosely with each other, but that's about it. The compiler does not use the call signatures to narrow types inside the implementation; they're fairly separate. There have been a number of feature requests around improving this: see microsoft/TypeScript#14515 and microsoft/TypeScript#22609,等等。到目前为止,它的语言还没有支持。
并且根据实现签名,arg0
是 string | (Worker & { key: string })
类型,而 arg1
是 Worker | undefined
类型。因此,每个参数都是独立且不相关的 union type,检查 arg0
对 arg1
.
没有任何影响
与其尝试为此使用“真正的”重载,您可以通过使用一个采用 rest tuples:
并集的函数签名来获得类似的行为
function doWork(
...args: [key: string, worker: Worker] | [worker: Worker & { key: string }]
): number {
if (args.length === 2) {
const key: string = args[0]; // okay
const worker: Worker = args[1]; // okay
return worker.doWork(key);
} else {
const key: string = args[0].key; // okay
const worker: Worker = args[0]; // okay
return worker.doWork(key);
}
}
这样的替换是可能的,因为您的两个原始调用签名具有相同的 return 类型 (number
)。从调用者的角度来看,这些看起来仍然非常像重载:
// 1/2 doWork(key: string, worker: Worker): number
doWork("xyz", { doWork(k) { return k.length } }); // okay
// 2/2 doWork(worker: Worker & { key: string; }): number
doWork({ key: "xyz", doWork(k) { return k.length } }); // okay
但是编译器现在可以通过 control flow analysis.
验证实现体是否安全
请注意,如果我们保留了您原来的 typeof
检查,编译器仍然会抱怨 worker
可能 undefined
:
if (typeof args[0] === "string") {
const key: string = args[0];
const worker: Worker = args[1]; // error! still Worker | undefined
return worker.doWork(key);
}
即使检查在逻辑上应该区分 args
是 [string, Worker]
类型还是 [{key: string} & Worker}]
类型,编译器也看不到这一点。该语言不支持 discriminating a union by a non-literal type 类型,例如 string
.
但是元组的length
属性是数字字面量类型的;所以 length
充当适当的 判别器 ,检查 args.length === 2
允许编译器区分进行了哪种形式的调用,并且一切正常,没有编译器错误。
我有一个接口,我们称它为Worker
。它看起来像这样。
interface Worker {
doWork(key: string): number;
};
A Worker
接受一个键值并用它做一些事情。现在我想要一个间接调用 doWork
的函数,比如
function doWork(key: string, worker: Worker): number {
// Pretend there's more complicated stuff happening around it :)
return worker.doWork(key);
}
在大多数情况下这很好。但是,有些 Worker
实例仅对特定 key
值有意义。在那些情况下,我希望我的函数能够推断出 key
值,即
function doWork(worker: Worker & { key: string }): number {
// Pretend there's more complicated stuff happening around it :)
return worker.doWork(worker.key);
}
所以我的想法是使用 Typescript 重载签名将这两个函数合并为一个函数,这样您就可以用一个或两个参数调用这个函数。
function doWork(key: string, worker: Worker): number;
function doWork(worker: Worker & { key: string }): number;
function doWork(arg0: string | (Worker & { key: string }), arg1?: Worker): number {
if (typeof arg0 === 'string') {
const key: string = arg0;
const worker: Worker = arg1!; // non-null assertion
return worker.doWork(key);
} else {
const key: string = arg0.key;
const worker: Worker = arg0;
return worker.doWork(key);
}
}
这行得通,除了我在第 6 行有一个混乱的非空(嗯,非未定义)断言。因为函数 doWork
只能用 或者 (1) 一个 string
和一个 Worker
,或者 (2) 一个有钥匙的 Worker
,在我看来,一旦我检查 typeof arg0 === 'string'
,Typescript 应该能够推断出我们处于第一种情况,因此 arg1
是 而不是 未定义。但是如果我们删除 !
,
function doWork(key: string, worker: Worker): number;
function doWork(worker: Worker & { key: string }): number;
function doWork(arg0: string | (Worker & { key: string }), arg1?: Worker): number {
if (typeof arg0 === 'string') {
const key: string = arg0;
const worker: Worker = arg1; // Oops!
return worker.doWork(key);
} else {
const key: string = arg0.key;
const worker: Worker = arg0;
return worker.doWork(key);
}
}
我们收到一个错误。
$ tsc --strict overload.ts
overload.ts:11:11 - error TS2322: Type 'Worker | undefined' is not assignable to type 'Worker'.
Type 'undefined' is not assignable to type 'Worker'.
11 const worker: Worker = arg1;
~~~~~~
Found 1 error.
是否有类型安全的方法来编写此函数,不涉及绕过 Typescript(未经检查的向下转换,any
,等)?我可以向 Typescript 证明我的函数实现能够像它应该的那样处理这两个重载吗?
Overloaded functions are split into a set of call signatures that callers see, and a single implementation with a signature that the implementation sees. These are compared loosely with each other, but that's about it. The compiler does not use the call signatures to narrow types inside the implementation; they're fairly separate. There have been a number of feature requests around improving this: see microsoft/TypeScript#14515 and microsoft/TypeScript#22609,等等。到目前为止,它的语言还没有支持。
并且根据实现签名,arg0
是 string | (Worker & { key: string })
类型,而 arg1
是 Worker | undefined
类型。因此,每个参数都是独立且不相关的 union type,检查 arg0
对 arg1
.
与其尝试为此使用“真正的”重载,您可以通过使用一个采用 rest tuples:
并集的函数签名来获得类似的行为function doWork(
...args: [key: string, worker: Worker] | [worker: Worker & { key: string }]
): number {
if (args.length === 2) {
const key: string = args[0]; // okay
const worker: Worker = args[1]; // okay
return worker.doWork(key);
} else {
const key: string = args[0].key; // okay
const worker: Worker = args[0]; // okay
return worker.doWork(key);
}
}
这样的替换是可能的,因为您的两个原始调用签名具有相同的 return 类型 (number
)。从调用者的角度来看,这些看起来仍然非常像重载:
// 1/2 doWork(key: string, worker: Worker): number
doWork("xyz", { doWork(k) { return k.length } }); // okay
// 2/2 doWork(worker: Worker & { key: string; }): number
doWork({ key: "xyz", doWork(k) { return k.length } }); // okay
但是编译器现在可以通过 control flow analysis.
验证实现体是否安全请注意,如果我们保留了您原来的 typeof
检查,编译器仍然会抱怨 worker
可能 undefined
:
if (typeof args[0] === "string") {
const key: string = args[0];
const worker: Worker = args[1]; // error! still Worker | undefined
return worker.doWork(key);
}
即使检查在逻辑上应该区分 args
是 [string, Worker]
类型还是 [{key: string} & Worker}]
类型,编译器也看不到这一点。该语言不支持 discriminating a union by a non-literal type 类型,例如 string
.
但是元组的length
属性是数字字面量类型的;所以 length
充当适当的 判别器 ,检查 args.length === 2
允许编译器区分进行了哪种形式的调用,并且一切正常,没有编译器错误。