-
Notifications
You must be signed in to change notification settings - Fork 0
Exercise: Discriminated union in Typescript
Let's imagine that your business logic includes objects whose type is only known at runtime, but are known to be of one of a defined set of types. For example, the result of a search query can be an array of articles and videos, which have some shared fields as well as individual fields.
Define a data type for an object that is either an article or a video (e.g. called MediaItem
). Both have a shared property called type
, which works as a discriminant - it is equal to either "article"
or "video"
. Articles and videos have some other shared properties (e.g. title
), but also some properties that are present only for articles (e.g. text
) or videos (e.g. duration
).
Define the data type so that Typescript can infer which properties are available (including non-shared) based on your control flow. The following example should compile (even with strict null checks enabled):
interface ItemPreview {
title: string;
info: string;
}
function getPreview(item: MediaItem): ItemPreview {
// item can be article or video, so only shared properties can be used here
const { title } = item;
if (item.type === 'video') {
// now item is known to have shared and video-specific properties
const minutes = Math.floor(item.duration / 60);
const seconds = item.duration % 60;
const info = `${minutes} min ${seconds} sec`;
return { title, info };
} else {
// now item is known to have shared and article-specific properties
const info = `${item.text.slice(0, 10)}...`;
return { title, info };
}
}
Let's say we will also want to filter items of only a given type (e.g. to store article entities in a separate NgRx slice from video entities). Implement an isArticle
function so that the following code compiles:
function separateEntities(items: MediaItem[]) {
const articles: Article[] = items.filter(isArticle);
// ...
}