I have this type:
(string | undefined)[]
I want to convert this type to:
(string)[]
- I want to do that without type casting.
- I can’t know the given type in advance, what I know is that it’s an array of stuff.
So I thought I can simply use array.filter() for this:
const arr = [1, 2, 3, undefined, 5, 6]
const filteredArray = arr.filter((item) => typeof item !== 'undefined')
console.log(filteredArray) // type of filteredArray (number | undefined)[]
Of course it works in JavaScript, but it TypeScript it doesn’t.
It infers filteredArray (number | undefined)[] even after filtering.
So I remember there was a type guard keyword called is, so I did the following:
function isDefined<T>(value: T | undefined): value is T {
return value !== undefined;
}
const filteredArray = arr.filter(isDefined)
But it keeps inferring it as (number | undefined)[],
so I thought if there was something like is not, probably I can say:
function isDefined<T>(value: T | undefined): value is not undefined {
Full code of my solution that did not work:
function isDefined<T>(value: T | undefined): value is T {
return value !== undefined;
}
let arr = [1, 2, 3, undefined, 5, 6]
arr = arr.filter(isDefined)
>Solution :
A good approach to this with reusable code is to create a type guard that can be passed:
function isNotUndefined<T>(item: T | undefined): item is T {
return item !== undefined;
}
This will ensure that the type of T is properly inferred to be distinct from undefined.
You can check it in this ts playground:
You already did this, so why is it not working, well because typescript is smart. Let us look at your code:
function isDefined<T>(value: T | undefined): value is T {
return value !== undefined;
}
let arr = [1, 2, 3, undefined, 5, 6] // Type of arr is (number | undefined)[];
arr = arr.filter(isDefined) // Since it is the same variable and the initial typing is compatible, it is not changed
Your origin type when you declare the initial array is (number | undefined)[].
When you perform the filter operation, this typing is still valid, so typescript does not change the array typing.
Therefore, you need to store the result in a new variable in order for a new typing to be infered:
function isDefined<T>(value: T | undefined): value is T {
return value !== undefined;
}
let arr = [1, 2, 3, undefined, 5, 6] // Type is (number | undefined)[]
let arr2 = arr.filter(isDefined) // Type is number[]