简体   繁体   中英

Why can't Typescript infer function argument types for optional arguments?

I think it's clearer to try it on TS Playground :

function identity<T extends (...args: any[]) => any>(fn: T): T {
  return fn;
}

function fn(args: {
  cb: (foo: number) => void,
}) {}

fn({
  cb: identity((foo /* infers number */) => {}),
});

function fn2(args: {
  cb?: (foo: number) => void,
}) {}

fn2({
  cb: identity((foo /* doesn't infer number */) => {}),
});

function fn3(args: {
  cb: (foo: number) => void,
} | {}) {}

fn3({
  cb: identity((foo /* infers number */) => {}),
});

For fn and fn3 , TS was able to infer that foo is a number . However, for fn2 , TS just typed foo as any . The typing for fn2 and fn3 are functionally the same thing, so I'm wondering why TS couldn't infer the type of foo .

The realistic use-case for this is React's useCallback , I was trying to infer the argument types for functions that pass through useCallback .

Why does TS behave like this? Is there a less hacky solution?

I think the crucial step here is the inference of identity s return type:

 let result: ((foo: number) => void | undefined) = identity(...);

As the return type has a base constraint of (...args: any[]) => any this rule of the Typescript specification applies:

The inferred type argument for each type parameter is the union type of the set of inferences made for that type parameter. However, if the union type does not satisfy the constraint of the type parameter, the inferred type argument is instead the constraint.

~ TypeScript Language Specification (outdated), Contextual Signature Instantiation

As undefined (the optional value) does not satisfy the constraint, the constraint is taken, and T gets inferred as (...args: any[]) => any .

By removing the constraint, T gets inferred correctly

The problem is that in fn2 the cb arg is optional , so identity can't infer from that. So you want to explicit tell TS that that arg can be undefined like so:

function fn2(args: {
  cb: (foo: number) => void | undefined
}) {}

This should do the trick and make the infer work again.

TS Playground link

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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