Skip to content

Exercise: Discriminated union in Typescript

Matěj Chalk edited this page Jul 30, 2020 · 1 revision

Assignment

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 };
  }
}

Bonus assignment (type predicates)

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);
  // ...
}
Clone this wiki locally