Reputation: 1496
Say I have this union:
export type IBookmarkItemFragment =
| ({ __typename: "Story" } & {
story: number;
})
| ({ __typename: "Product" } & {
product: number;
})
| ({ __typename: "Project" } & {
project: number;
});
I want .filter
and .map
based off __typeame
. Something like:
bookmarks
.filter(bookmark => {
return bookmark.__typename === "Project";
})
.map(project => {
return project.project;
});
How can I make sure that in the map project
will resolve to:
({ __typename: "Project" } & {
project: number;
})
Upvotes: 1
Views: 865
Reputation: 249536
You have to explicitly declare the function is a type guard with an annotation:
export type IBookmarkItemFragment =
| ({ __typename: "Story" } & {
story: number;
})
| ({ __typename: "Product" } & {
product: number;
})
| ({ __typename: "Project" } & {
project: number;
});
declare let bookmarks: IBookmarkItemFragment[]
bookmarks
.filter((bookmark):bookmark is Extract<IBookmarkItemFragment, { __typename: "Project"}> => {
return bookmark.__typename === "Project";
})
.map(project => {
return project.project;
});
Or just for fun if we want to generalize the type guard, we can create a guard factory that will work for this any common tagged union.
export type IBookmarkItemFragment =
| ({ __typename: "Story" } & {
story: number;
})
| ({ __typename: "Product" } & {
product: number;
})
| ({ __typename: "Project" } & {
project: number;
});
function guardFactory<T, K extends keyof T, V extends string & T[K]>(k: K, v: V) : (o: T) => o is Extract<T, Record<K, V>> {
return function (o: T): o is Extract<T, Record<K, V>> {
return o[k] === v
}
}
declare let bookmarks: IBookmarkItemFragment[]
bookmarks
.filter(guardFactory("__typename", "Project"))
.map(project => {
return project.project;
});
Upvotes: 2