If you've ever used Array.filter to filter a list to certain type of items, you've probably been hit by TypeScript not realizing what type of items your list contains after filtering. To fix this, you can use user-defined type guards.
Let's say we have content items of two type: Post and Image.
interface Post {
text: string;
title: string;
date: Date;
}
interface Image {
url: string;
alt: string;
date: Date;
}
We are going to store items of both types in a array.
type Content = Post | Image;
const content: Content[] = [
{
title: "A post",
text: "...",
date: new Date()
},
{
alt: "A image",
url: "...",
date: new Date()
}
]
Now if we want to get a list of all the post titles, we can do it by first filtering filtering with "title" in obj
. But even though we know this works and there's no way title
is undefined, we still get a type error.
const postTitles = content.filter(c => "title" in c).map(c => c.title);
// Property 'title' does not exist on type 'Content'.
// Property 'title' does not exist on type 'Image'.(2339)
This is because TypeScript can't deduce that all the remaining items are of the type Post. We can fix this issue with a TypeScript feature called user-defined type guards
What are user defined type guards?
Type guards allow narrowing a type with differenty ways. For example, you can use typeof or instanceof.
User-defined type guards are functions whose return type is a type predicate.
function isPost(content: Content): content is Post {
return "title" in content;
}
Notice the return value of the function is content is Post
this tells TypeScript the function will only return true if content
is of type Post
. The first part of a type predicate (here content) must be a name of parameter of the function.
Now we can use this type guard in our Array.filter and TypeScript won't yell at us anymore 🥳
const postTitles = content.filter(isPost).map(c => c.title);
If you want to play with the code in this article, checkout this playground
Further reading
Narrowing
Using type predicates
Photo by Steve Johnson on Unsplash
Top comments (1)
I usually use interfaces for array item. Therefore, it always hints me