Search code examples
typescripttypescript-types

Why do unspecified generics get inferred differently, simple call vs null-coalesce, in TypeScript?


Why do a and b have different types in the below code?

function empty<T>() { return [] as T[] }

const defEmpty = empty()

function test1(abc: number[]|null) {
  const a = abc ?? defEmpty
  const b = abc ?? empty()
}
 

The the playground, I can see a: unknown[] and b: number[]. I was expected them both to have the type unknown[].


Solution

  • You did not provide explicit generic type in empty() call here:

    const b = abc ?? empty()
    

    But Typescript can infer that type from the expression - it knows that abc is of type number[] | null so it infers that type parameter for empty call is number (you can see it by the way in the playground by your link if you hover over empty()). So your line basically becomes:

    const b = abc ?? empty<number>();
    

    And as such b is also of type number[]. Note that omitting generic type parameter does not mean it will become unknown.

    As for how exactly type inference works - as far as I know there is no strict specification. The last specification version is archived and no longer maintained. And there were changes to type inference rules compared to this archived specification. As such, the best source of information you have is docs which are pretty vague. The relevant section is called "Contextual Typing" and it describes in broad terms that Typescript is capable of inferring generic type parameter from context in many cases, but do not describe in strict terms how it works.

    This case however seems pretty clear. On the left hand side of ?? expression you have expression of known type, so it makes sense to use that same type on the right hand side with omitted generic type parameter.