We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

This lesson's interactive features are locked, please to keep using them

Conditional Types

We're now getting into some pretty advanced stuff that, while useful in really tricky modelling situations, is not something you'll need in application level code.

As a general rule, advanced TypeScript features are more useful in library code that needs to be more flexible, abstract, and reusable. Application level TypeScript code is generally much simpler and more concrete... grug make object type, grug use object type... grug happy.

Conditional types allow us to create new types based on conditions within the type system, they take this form:

type NewType = SomeType extends OtherType ? TrueType : FalseType;

It reads like a ternary expression: "If SomeType extends (satisfies) OtherType, then NewType is TrueType; otherwise, it's FalseType."

Here's a simple example:

type IsString<T> = T extends string ? true : false;

// Usage
type Result1 = IsString<"hello">; // true
type Result2 = IsString<42>;      // false
type Result3 = IsString<string>;  // true

In this example, IsString is a conditional type that checks if the type parameter T extends string. If it does, the resulting type is true; otherwise, it's false. TypeScript actually ships with some built-in conditional types:

As usual, the question is, "when the heck is this useful???" Well, let's say we have some events that can fire in our front end application:

type ClickEvent = { type: "click"; x: number; y: number };
type KeyEvent = { type: "key"; key: string };
type MouseMoveEvent = { type: "mousemove"; x: number; y: number };
type FormEvent = { type: "submit"; formId: string };

type Event = ClickEvent | KeyEvent | MouseMoveEvent | FormEvent;

It may be useful to dynamically create a type that only includes "mouse-related" events: the ones that have an x and y property. We can use the Extract conditional type to do so:

// Extract is a TS built-in type, this is the implementation:
type Extract<T, U> = T extends U ? T : never;

// This is an example of how it can be used:
type MouseRelatedEvents = Extract<Event, { x: number; y: number }>;

Now MouseRelatedEvents is the same as:

type MouseRelatedEvents = ClickEvent | MouseMoveEvent;

The difference is that it's dynamic. If we add more events to the Event union, MouseRelatedEvents will automatically include them if they match the condition (i.e., if they have x and y properties).

Assignment

Support.ai is experimenting with auto-replies – but only for tickets that seem emotionally safe.