Why does TypeScript interpret filtered string array as string | boolean array

Viewed 92

I've got the following expression

[isProduction() && '/prod-path', 'first', 'second'].filter(Boolean)

TypeScript does not agree this is a string[]. Could anyone please tell me why?

4 Answers

If isProduction is false, you end up with:

[false, 'first', 'second']

which is as Typescript says, an Array<string|boolean>.

The filter won't filter out boolean on the type level. You'll have to manually say it like so:

.filter((item):item is string => Boolean(item))

The .filter function has an overload for the return value of the result which looks like this

filter(callbackfn: (value: T, index: number, array: T[]) => any, thisArg?: any): T[];

Since your array is of type T == (string | boolean)[] it will return that type. However you can use type guards to change the return type e.g.

function isNotBool<T>(argument: T | boolean): argument is T {
    return typeof argument !== "boolean"
}

const test:string[] = [isProduction() && '/prod-path', 'first', 'second'].filter(isNotBool);

This way it remains as string[] by default

const s = ['first', 'second']

if (isProduction()) {
    s.push("/prod-path")
}

This is a better way:

[
  ...(isProduction() ? ['/prod-path'] : []),
  'first',
  'second',
]
Related