我正在处理经典的节点回调。示例:
myFunction('foo', (err: Error|null, data?: Buffer) =>{
if (err) {
// typeof err is Error
// typeof data is Buffer|undefined
} else {
// typeof err is null;
// typeof data is Buffer|undefined;
}
});
我正在尝试定义自己的回调接受函数myFunction
。我正在尝试实现两件事:
data
的类型来推断err
的类型示例:
// types of err and data are inferred from readFile
readFile('foo', (err, data) => {
if (err) {
// typeof err is Error
// typeof data is undefined
} else {
// typeof err is null
// typeof data is Buffer
}
}
在当前的打字稿中,有什么方法可以实现这两种方法吗?
答案 0 :(得分:3)
通常,您将使用discriminated union类型的单个参数,而不要使用类型彼此相关的两个联合类型的参数,例如err
和data
。
TypeScript确实对correlated expressions并没有太多支持。也就是说,没有一种好方法告诉编译器,尽管err
是Error | null
类型,而data
是Buffer | undefined
类型,但是这些类型的一些组合err
和data
不能使用。您可以使用control flow analysis来检查err
的类型,但这不会影响data
的感知类型……编译器错误地认为它们是独立表达式。
这是我能得到的最接近的东西;它大量使用了tuples in rest and spread expressions,因此您真的必须与编译器进行斗争才能使它发生:
declare function myFunction(
someString: string,
someCallback: (...args: [Error, undefined] | [null, Buffer]) => void
): void;
someCallback
的类型是两个自变量的函数,这两个参数要么是类型[Error, undefined]
,要么是类型为[null, Buffer]
。现在,您可以 进行两参数回调,但是您将遇到完全相同的问题:检查err
对data
无效:
// can't call it this way
myFunction("oops", (err, data) => {
err; // Error | null
data; // Buffer | undefined
if (err) {
data; // still Buffer | undefined ?
}
});
相反,您还必须在回调实现中使用rest参数:
myFunction("foo", (...errData) => {
if (errData[0]) {
const [err, data] = errData;
err; // Error
data; // undefined
} else {
const [err, data] = errData;
err; // null
data; // Buffer
}
});
之所以行之有效,是因为当您检查errData[0]
时,您正在检查元组对象的单个属性,并且编译器将使用控制流分析将errData
缩小为两种已知类型之一。在检查之后,您只能将errData
分解为err
和data
。
我强烈建议您考虑在回调中切换到单个已区分的并集参数。看看解决方案有多简单:
type Param = { type: "Error"; err: Error } | { type: "Data"; data: Buffer };
declare function myFunction(
someString: string,
someCallback: (param: Param) => void
): void;
myFunction("foo", param => {
if (param.type === "Error") {
param.err; // Error
} else {
param.data; // Buffer
}
});
这几乎就是您想要的,而无需任何战斗。
好的,希望能有所帮助;祝你好运!