Typescript,需要两个函数签名中的任何一个

时间:2018-03-27 10:30:18

标签: typescript

我正在使用一个给我一个回调的库,它希望 一个错误作为它的第一个参数,或者null作为错误,值作为它的第二个参数。

我正在尝试使用类型对此进行编码,因此typescript将验证我是否传递了错误和值,或者两者都没有。

type Callback = {
  (error: undefined, value: string): void;
  (error: Error): void;
}

function doThings(c: Callback) {
    // Valid, no error, and a useful value.
    c(undefined, '1');
    // Valid, something went wrong, so we pass in an error, but no value.
    c(new Error());

    // Invalid. Since there's no value, there must be an error.
    c(undefined);
    // Invalid, there's both an error and a value.
    c(new Error(), '1');
}

function c1(error: undefined, value: string) { }

function c2(error: Error) { }

doThings(c1)
doThings(c2)

2 个答案:

答案 0 :(得分:2)

您可以使用sum和intersection类型的组合来实现此目的:

type ValueCallback = (error: undefined, value: string) => void;
type ErrorCallback = (error: Error) => void;
type Callback = (ValueCallback | ErrorCallback) & ((error: Error | undefined, value?: string) => void);

function doThings(c: Callback) {
  c(undefined, "1");
  c(new Error());
}

function c1(error: undefined, value: string) {}
function c2(error: Error) {}

doThings(c1);
doThings(c2);

需要交叉部分,因此您实际上可以在doThings内调用该函数,而不会找到兼容的调用签名。

答案 1 :(得分:2)

据我所知,您对Callback的定义完全符合您的要求。 Callback是一个可以使用参数(error: undefined, value: string)调用的函数。 也是一个可以使用参数(error: Error)调用的函数。有效的Callback 必须支持以这两种方式调用

重申一下,这很好:

type Callback = {
  (error: undefined, value: string): void;
  (error: Error): void;
}

function doThings(c: Callback) {
    c(undefined, '1');  // okay
    c(new Error()); // okay
}

TypeScript编译器没有报告任何错误,这很好。

似乎是一个问题的部分是你继续定义两个函数, 哪个符合Callback规范。让我们来看看它们。第一:

function c1(error: undefined, value: string) { 
   // some impl which expects value to be a string, e.g.,
   value.charAt(0);
};
doThings(c1); // error, as expected

此函数需要两个参数。你不能用参数(error: Error)来调用它。所以调用doThings(c1)是编译器的错误,这正是你想要的。编译器错误告诉您c1不是Callback。如果你强制编译器允许它使用类型断言或其他技巧,一切都会在编译时很好,然后在运行时doThings(c1)将最终调用c1(new Error()),假设{{1}使用c1参数进行字符串式的操作(如上所示),最终会抛出类似value的错误

类似地:

Error: undefined has no properties.

此函数要求第一个参数为function c2(error: Error) { // some impl which expects error to be an Error, e.g., console.log(error.message); }; doThings(c2); // error, as expected 。你不能用参数Error来调用它。所以调用(error: undefined, value: string)是编译器的错误,这正是你想要的。编译器错误告诉您doThings(c2)不是c2。如果你强制编译器允许它使用类型断言或其他技巧,一切都会在编译时很好,然后在运行时Callback将最终调用doThings(c2),假设{{1}与c2(undefined, '1')参数类似错误的东西(如上所示)最终会抛出类似c2的错误

因此,errorError: undefined has no properties.都不是有效的c1个对象。如果您想制作有效的c2,则可以执行此操作。一种方法是创建一个更具体的函数类型,如下所示:

Callback

Callback函数比function cSubtype(error: undefined | Error, value?: string) { if ((typeof error === 'undefined') && (typeof value === 'string')) { c1(error, value); } else if ((typeof error !== 'undefined') && (typeof value === 'undefined')) { c2(error); } else console.log('I got some other arguments'); } doThings(cSubtype); // okay 更具体,因为它接受更多通用参数。 (函数参数变化contravariantly,意味着函数参数越普遍/越宽,函数类型越具体/越窄。)它接受cSubtype和{{1}之类的参数。 }。它还接受Callback(error: undefined, value: string)。但您仍然可以将(error: Error)传递给(error: Error, value: string),因为(error: undefined)cWider子类型,就像doThings一样传递给期待cWider的东西。

另一种方法是定义overloaded函数,其行为完全符合您希望Callback的行为。它仍然需要一个更通用的实现签名,但不能使用实现签名调用(有关更多信息,请阅读有关重载的链接)。这是一个例子:

{a: string, b: boolean}

这有意义吗?希望能帮助到你。祝你好运。