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[]
.
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.
Is it possible to get the type-narrowing to work with the ternary operator? Are the two expressions not equivalent?
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:
This has been previously reported as marked as working as intended: https://github.com/microsoft/TypeScript/issues/39550