Nate Glenn
Nate Glenn

Reputation: 6744

type narrowing in ternary

In myFunction below, I accept a parameter which is either a string or an array of strings, and I normalize it to an array in the function body:

export const myFunction = <T extends string | string[]>(
  myParam: T
) => {
  let myStringArray = (Array.isArray(myParam) ? myParam : [myParam])
}

I expected Array.isArray to narrow the type in the branches of the ternary operator to either string or string[] so that the final type of myStringArray would be string[]. Instead, the final type is more complicated: (T & any[]) | T[].

TypeScript Playground

I noticed, however, that if I restrucure the code to use an if-else instead, the type-narrowing works perfectly:

export const myFunction = <T extends string | string[]>(
  myParam: T
) => {
  let myStringArray;
  if (Array.isArray(myParam)) {
    myStringArray = myParam;
  } else {
    myStringArray = [myParam];
  } 
  console.log(myStringArray);
}

The type of myStringArray on the last line is string[], as expected.

TypeScript Playground

Is it possible to get the type-narrowing to work with the ternary operator? Are the two expressions not equivalent?

Upvotes: 3

Views: 735

Answers (1)

Slava Knyazev
Slava Knyazev

Reputation: 6081

The TS compiler does not evaluate the logic of ternary expressions when producing a return type.

For a simpler example:

const x = true ? 1 : 0 // TS says 1 | 0

Despite the 0 value being impossible, TS does not discard it.

The only analysis TS performs on ternary expressions is type refinement within them:

enter image description here enter image description here enter image description here

This has been previously reported as marked as working as intended: https://github.com/microsoft/TypeScript/issues/39550

Upvotes: 4

Related Questions