Consider following code with strictNullChecks
turned on:
var a: (number | null)[] = [0, 1, 2, 3, null, 4, 5, 6]; var b: { value: number; }[] = a.map(x => x != null && { value: x }).filter(Boolean);
It fails to compile due to:
Type '(false | { value: number; })[]' is not assignable to type '{ value: number; }[]'. Type 'false | { value: number; }' is not assignable to type '{ value: number; }'. Type 'false' is not assignable to type '{ value: number; }'.
But it is absolutely clear, that false
will be filtered away by .filter(Boolean)
.
Same problem with null
.
Is there a way (except writing as number[]
) to mark that value doesn't contain false
or null
?
filter(Boolean as any as ExcludesFalse); This works because you are asserting that Boolean is a type guard, and because Array. filter() is overloaded to return a narrowed array if the callback is a type guard.
filter(Boolean)` just removes values from a list which are "falsey", like empty strings or null.
The filter(Boolean) step does the following: Passes each item in the array to the Boolean() object. The Boolean() object coerces each item to true or false depending on whether it's truthy or falsy. If the item is truthy, we keep it.
In Typescript, Filter() is a built-in array method which is defined as a method for creating a new array or set of elements that contains a subset of the given array elements by returning the array of all the values of the elements in the newly created sub-array over the given array.
You can use functions like this
function nonNullable<T>(value: T): value is NonNullable<T> { return value !== null && value !== undefined; } type Truthy<T> = T extends false | '' | 0 | null | undefined ? never : T; // from lodash function truthy<T>(value: T): value is Truthy<T> { return !!value; } [1, 2, 0, null].filter(nonNullable) // number[] [1, 2, 0, null].filter(truthy) // number[]
NonNullable - https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#predefined-conditional-types
If you really don't want to change the generated JavaScript, and instead prefer to force the TypeScript compiler to recognize that Boolean
is serving as a guard against false
values, you can do this:
type ExcludesFalse = <T>(x: T | false) => x is T; var b: { value: number; }[] = a .map(x => x != null && { value: x }) .filter(Boolean as any as ExcludesFalse);
This works because you are asserting that Boolean
is a type guard, and because Array.filter()
is overloaded to return a narrowed array if the callback is a type guard.
The above (Boolean as any as ExcludesFalse
) is the cleanest code I could come up with that both works and doesn't change the generated JavaScript. The constant Boolean
is declared to be an instance of the global BooleanConstructor
interface, and you can merge an ExcludesFalse
-like type guard signature into BooleanConstructor
, but not in a way that allows you to just say .filter(Boolean)
and have it work. You can get fancier with the type guard and try to represent guarding against all falsy values (except NaN
) but you don't need that for your example.
Anyway, hope that helps; good luck!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With