Jay
Jay

Reputation: 1056

How to make a declared function automatically infer types based on usage?

I feel like this should totally be possible in typescript, but it seems as if typescript doesn't properly type this. Is there any way to do this? Here's a super simple example, but I'm specifically looking to automatically infer argument types when passing a function to a react component's prop that takes a callback with a defined type

Actual

function constrainingFunction(b: number) {
    return b + 1
}

function test(a) { // ERROR: `a` implicitly has an `any` type
    constrainingFunction(a)
}

test(1)

Expected

function constrainingFunction(b: number) {
    return b + 1
}

function test(a) { // `a` should properly be inferred as `number`
    constrainingFunction(a)
}

test(1)

Upvotes: 4

Views: 3851

Answers (1)

jcalz
jcalz

Reputation: 329573

See microsoft/TypeScript#34738 for a canonical answer.

TypeScript does not perform contextual typing of function parameters from usage this way, apparently for performance reasons. According to this comment by the dev lead for the TS team,

This has been an intentional design constraint since inception; not doing this is a key reason TS performance is remotely acceptable.

There's also this comment in a related issue:

People have asked us hundreds of times why we don't do this; the answer is that it's architecturally impractical to do this in a way that results in stable, performant checking results in anything other than toy programs.

So the a parameter will need to be explicitly annotated as number in order for this code to work as intended without error.


But you don't necessarily need to be the one annotating it yourself. As both comments above go on to say:

The "infer from usage" refactor will produce a type for you on an as-requested basis.

The "infer from usage" refactor is available for this purpose.

So TypeScript can infer the type from usage, but not automatically during compilation. You can specifically request it in an IDE that supports Code Actions like Visual Studio or The TypeScript Playground.

If you select the offending a parameter, you should see a light bulb, which when clicked, suggests "infer parameter types from usage":

function constrainingFunction(b: number) {
  return b + 1
}
// 💡 [ infer parameter types from usage ]
function test(a) { // ERROR: `a` implicitly has an `any` type
  // -------> ~
  constrainingFunction(a)
}

test(1)

And if you accept that suggestion, the desired explicit annotation will occur:

function constrainingFunction(b: number) { // <-- magic!
  return b + 1
}
function test(a: number) {
  constrainingFunction(a)
}

test(1)

Supplementary animated screenshot:

animated screenshot

So that might be some consolation for you.

Playground link to code

Upvotes: 5

Related Questions