简体   繁体   English

TypeScript中的回调参数类型推导

[英]Callback parameter type deduction in TypeScript

type CallBack = (_: number) => number

function sqk({x}:{x:number}): Promise<number>;  
function sqk({x,callback}:{x:number,callback:CallBack}): void;
function sqk({x,callback}:{x:number,callback?:CallBack}): Promise<number>|void  {
  if(callback){
   callback (x * x);
  }
  return Promise.resolve(x)
}

// here our callback will receive a number, but the type of `x` can‘t  be deduced
const cb = sqk({x:5, callback:function(x) {
  console.log(x);
  return x;      
}});

// here our promise will receive a number, and the type of `x` is `number`
const promise = sqk({x:5}).then(x=>{console.log(x)})

What should I do to make TS automatically deduce the parameter type in the call?应该怎么做才能让TS自动推断调用中的参数类型?

A possible solution is to remove the callback from the object as the second parameter,as follows:一个可能的解决方案是从对象中去掉回调作为第二个参数,如下:

type CallBack = (_: number) => number

function sqk(x:number): Promise<number>;  
function sqk(x:number,callback:CallBack): void;
function sqk(x:number,callback?:CallBack): Promise<number>|void  {
  if(callback){
   callback (x * x);
  }
  return Promise.resolve(x*x)
}

const cb = sqk(5,(x)=> {
  console.log(x);
  return x;      
});


const promise = sqk(5).then(x=>{console.log(x)})

But this doesn't answer my question, why the first solution doesn't work?但这并不能回答我的问题,为什么第一个解决方案不起作用?

First some observations, if you don't care for it, you can scroll down to the bottom for the answer:先说一些观察,如果你不关心,可以向下滚动到底部寻找答案:

Notice how it works without the overloads:注意它是如何在没有重载的情况下工作的:

function workingUnary({ x , callback }: { x: number, callback: (x: number) => void }): number | void {
  return null!
}

workingUnary({x: 5, callback: (x) => {return null!}})
// ^x type ==> number

We can also gain some insight on how your overloaded function breaks if callback is undefined如果回调undefined ,我们还可以了解重载函数如何中断

sqk({x: 5, callback: undefined})
// The expected type comes from property 'callback' 
// which is declared here on type '{ x: number; callback: (x: number) => void; }'
// The call would have succeeded against this implementation, 
// but implementation signatures of overloads are not externally visible.

Noticeably this behavior is fixed if you change the order of the overloads:值得注意的是,如果您更改重载的顺序,则此行为是固定的:

function sqk2({ x , callback }: { x: number, callback: (x: number) => void }): void
function sqk2({ x }: { x: number }): number
function sqk2({ x , callback }: { x: number, callback?: (x: number) => void }): number | void {
  return null!
}

const test7 = sqk2({x: 5, callback: (x) => {return null!}})
// now it works fine?
const test8 = sqk2({x: 5})
// works fine if there is no callback
const test9 = sqk2({x: 5, callback: undefined})
// still broken

Answer回答

Your overloads are in the wrong order.您的重载顺序错误。

This is quoted from the (now deprecated) but still relevant TS docs on overloads ( link )这引用自(现已弃用)但仍然与重载相关的 TS 文档( 链接

In order for the compiler to pick the correct type check, it follows a similar process to the underlying JavaScript.为了让编译器选择正确的类型检查,它遵循与底层 JavaScript 类似的过程。 It looks at the overload list and, proceeding with the first overload, attempts to call the function with the provided parameters.它查看重载列表,并继续第一个重载,尝试使用提供的参数调用函数。 If it finds a match, it picks this overload as the correct overload.如果找到匹配项,它将选择此重载作为正确的重载。 For this reason, it's customary to order overloads from most specific to least specific.出于这个原因,习惯上按照从最具体到最不具体的顺序对重载进行排序。

Basically, you should reorder your overloads.基本上,您应该重新排序您的重载。 And possibly add another overload to support if callback is undefined如果回调未定义,可能会添加另一个重载来支持

function sqk2({ x , callback }: { x: number, callback: undefined }): number
function sqk2({ x , callback }: { x: number, callback: (x: number) => void }): void
function sqk2({ x }: { x: number }): number
function sqk2({ x , callback }: { x: number, callback?: (x: number) => void }): number | void {
  return null!
}

const test4 = sqk2({x: 5, callback: (x) => {return null!}})
// ^x type ==> number
const test5 = sqk2({x: 5})
// works fine if there is no callback
const test6 = sqk2({x: 5, callback: undefined})
// now works

View this on TS Playground在 TS Playground 上查看

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM