To ensure that the function acts as a type guard, you need to explicitly declare it 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;
});
If you want to have some fun and generalize the type guard, you can create a guard factory that will work for 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;
});