我正在使用一个给我一个回调的库,它希望 一个错误作为它的第一个参数,或者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)
答案 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
的错误
因此,error
和Error: 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}
这有意义吗?希望能帮助到你。祝你好运。