Execute Program

Everyday TypeScript: Shared Fields

Welcome to the Shared Fields lesson!

This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!

  • Sometimes, we want a discriminated union with a field shared in both cases. For example, StartedCourses and UnstartedCourses both have names. We could choose to duplicate the name field:

  • >
    type StartedCourse = {
    name: string
    started: true
    lastInteractionTime: Date
    };
    type UnstartedCourse = {
    name: string
    started: false
    };
    type Course = StartedCourse | UnstartedCourse;
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    let course: Course = {
    name: 'typescript',
    started: false,
    };
    course.name;
    Result:
    'typescript'Pass Icon
  • This works, but it can get wordy with many shared fields. (Imagine StartedCourse and UnstartedCourse sharing ten fields!) We can eliminate the duplication by moving the shared fields into a shared type.

  • >
    type CourseShared = {
    name: string
    };
    type StartedCourse = CourseShared & {
    started: true
    lastInteractionTime: Date
    };
    type UnstartedCourse = CourseShared & {
    started: false
    };
    type Course = StartedCourse | UnstartedCourse;
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    let course: Course = {
    name: 'typescript',
    started: false,
    };
    course.name;
    Result:
    'typescript'Pass Icon
  • This raises a question of style. Should we define four named types: CourseShared, StartedCourse, UnstartedCourse, and Course? Or should we combine them all into a single type definition? Either way works.

  • The version of Course below is completely equivalent to the one above!

  • >
    type Course = {
    name: string
    } & (
    {
    started: true
    lastInteractionTime: Date
    } | {
    started: false
    }
    );
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    let course: Course = {
    name: 'typescript',
    started: false,
    };
    course.name;
    Result:
    'typescript'Pass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    let course: Course = {
    name: 'typescript',
    started: false,
    lastInteractionTime: new Date(),
    };
    course.name;
    Result:
    type error: Object literal may only specify known properties, and 'lastInteractionTime' does not exist in type '{ name: string; } & { started: false; }'.Pass Icon
  • But that type is a bit hard to read. It's often better to name the pieces of a type as you build it up.