Reputation: 255
I have an array of object, one of the property has a null value, I used filter to remove it, but typescript give me warning null is not assignable? since filter will never return null why typescript give me such an error?
const data = [
{
id: "123"
},
{
id: "456"
},
{
id: null
}
];
const d2: string[] = data.map((d) => d.id).filter((o) => o);
console.log(d2);
demo https://codesandbox.io/s/gallant-water-vio56?file=/src/index.ts:0-165
Upvotes: 8
Views: 5521
Reputation: 18
Using filter
won't narrow down types for you. map
transforms the data into an array of string | null
data, and TypeScript doesn't know that d2
won't contain null
after filtering (TypeScript isn't exceptionally bright, I guess…).
There are a couple of solutions — one of them is complicated, and the other is complicated (though not as complicated) compared to your original code.
We can explicitly use types in the filter function itself so that TypeScript knows the output will not contain null:
const data = [
{ id: "123" },
{ id: "456" },
{ id: null }
];
// Type guard to filter out null values
const d2: string[] = data.map((d) => d.id).filter((o): o is string => o !== null);
console.log(d2);
We can also just cast the result after the filter is run instead of assigning the type beforehand. Just add as string[]
to the end of your line:
const data = [
{
id: "123"
},
{
id: "456"
},
{
id: null
}
];
const d2 = data.map((d) => d.id).filter((o) => o) as string[];
console.log(d2);
Note: It would be a good practice to include !== null
as well, like this:
const d2 = data.map((d) => d.id).filter((o) => o !== null) as string[];
But it isn't necessary, and I was going for simplicity in the second solution…
Upvotes: 0
Reputation: 84992
UPDATE SEPTEMBER 2024:
As of typescript 5.5, typescript is now able to infer type predicates.
However, the code in the question would still not work, and the reason for this is that the function might return an empty string. This possibility of an empty string means there's still a possibility you made a mistake, and so typescript keeps the error to force you to take a look.
You can either add an explicit return type (See the original answer below), or you can change the code so the empty string is no longer filtered out:
const d2: string[] = data.map((d) => d.id).filter((o) => o !== null);
ORIGINAL ANSWER:
Typescript can't figure out that .filter((o) => o)
will narrow the types of the array, not without some help. You can define a custom type guard and use that though. For example:
function notNull<T>(val: T | null): val is T {
return val !== null;
}
const d2 = data.map((d) => d.id).filter(notNull); // d2 is a string[]
Or an inline version that only works with strings:
const d2 = data.map(d => d.id).filter((o): o is string => !!o);
Upvotes: 9