Typescript 是否可以进行类型安全节点样式的回调?

Typescript 是否可以进行类型安全节点样式的回调?,typescript,Typescript,节点回调类似于: interface NodeCallback<TResult,TError> { (err: TError): void; (err: null, res: TResult): void; } 这不是严格的类型安全。例如,这可以很好地编译: fs.readdir('/', (err, files) => { if (err !== null) { // There's an error! files.forEach(log); // St

节点回调类似于:

interface NodeCallback<TResult,TError> {
  (err: TError): void;
  (err: null, res: TResult): void;
}
这不是严格的类型安全。例如,这可以很好地编译:

fs.readdir('/', (err, files) => {
  if (err !== null) { // There's an error!
    files.forEach(log); // Still using the result just fine.
  }
})
通过将签名更改为包含所有可能的值,可以使其更安全(嗯,某种程度上)

function readdir(path: string, callback?: (err: null | NodeJS.ErrnoException, files?: string[]) => void): void;
但是无法指定两者之间的依赖关系,因此您需要键入assert
res
以使
stricnullchecks
安静下来

fs.readdir('/', (err, files) => {
  if (err === null) { // There's no error
    // files.forEach(log); // Won't compile
    (files as string[]).forEach(log); // Type assertion
    files!.forEach(log); // Nice shorthand
    if (files !== undefined) { // Type guard
      files.forEach(log);
    }
  }
})
这还不算太糟,除了:

  • 当你需要重复做的时候
  • 当您不访问属性时,必须键入assert,这可能意味着您需要导入另一个类型。真烦人。类型卫士将避免这种情况,但这样会造成不必要的运行时惩罚
  • 实际上它仍然不安全。它更多的是在你的脸上,所以你不得不考虑它,但我们主要依靠手动断言
如果您真的想这样做,您可以使用类似于歧视性联合的
结果

type Result<R,E>
  = { error: false, value: R }
  | { error: true, value: E }

function myFunction(callback: (res: Result<string, Error>) => void) {
  if (Math.random() > 0.5) {
    callback({ error: true, value: new Error('error!') });
  } else {
    callback({ error: false, value: 'ok!' })
  }
}

myFunction((res) => {
  if (res.error) {
    // type of res.value is narrowed to Error
  } else {
    // type of res.value is narrowed to string
  }
})
类型结果
={错误:false,值:R}
|{错误:true,值:E}
函数myFunction(回调:(res:Result)=>void){
if(Math.random()>0.5){
回调({error:true,value:newerror('error!')});
}否则{
回调({错误:false,值:'ok!'})
}
}
myFunction((res)=>{
if(res.error){
//res.value的类型被缩小为Error
}否则{
//res.value的类型已缩小为字符串
}
})
老实说,这最终是非常好的,但这是大量的样板文件,完全违背了常见的节点样式

所以我的问题是,typescript目前有没有一种方法可以使这个超级通用的模式既安全又方便?我很确定现在的答案是否定的,这没什么大不了的,但我只是好奇


谢谢

除了你所做的以外,我所看到的唯一好的模式是这样的:

function isOK<T>(err: Error | null, value: T | undefined): value is T {
    return !err;
}

declare function readdir(path: string, callback: (err: null | Error, files: string[] | undefined) => void): void;

readdir('foo', (err, files) => {
    if (isOK(err, files)) {
        files.slice(0);
    } else {
        // need to err! here but 'files' is 'undefined'
        console.log(err!.message);
    }
})
函数isOK(err:Error | null,value:T |未定义):值为T{
返回!错误;
}
声明函数readdir(路径:string,回调:(err:null | Error,files:string[]| undefined)=>void):void;
readdir('foo',(错误,文件)=>{
如果(isOK(错误,文件)){
切片(0);
}否则{
//需要出错!此处的“文件”为“未定义”
console.log(err!.message);
}
})
function isOK<T>(err: Error | null, value: T | undefined): value is T {
    return !err;
}

declare function readdir(path: string, callback: (err: null | Error, files: string[] | undefined) => void): void;

readdir('foo', (err, files) => {
    if (isOK(err, files)) {
        files.slice(0);
    } else {
        // need to err! here but 'files' is 'undefined'
        console.log(err!.message);
    }
})