TypeScript doesn't infer the return type of an array filter operation in the same way that it does for an if statement to check whether a variable is defined.
Let's consider the following example:
type User = {
id: number;
name?: string;
};
const listUsers = (): User[] => {
// fetch users from some API and return them
return [];
};
// names will be of type (string | undefined)[]
const names = listUsers()
.map((user) => user.name)
.filter((name) => !!name);
The type of names
is inferred by TypeScript as (string | undefined)[]
, although we explicitly filter out falsy values. TypeScript somehow doesn't interpret the condition inside the filter function.
To get TypeScript to infer the return type correctly, we need to use a custom type-guard.
// type-guard to assure name is a string
const filterNames = (name?: string): name is string => {
return !!name;
}
const names = listUsers()
.map((user) => user.name)
.filter(filterNames);
The return type name is string
of this type-guard function will assure TypeScript that we have checked the type and can safely treat it as string
.
However, if we don't want to define a separate filter function, we can also use the type guard inside the inline filter function:
const names = listUsers()
.map((user) => user.name)
.filter((name?: string): name is string => !!name);
Top comments (1)
Great article. 🙌 Needed to solve a similar problem just now.