Conditional types allow for types to be expressed using a ternary syntax.
class Grill {
startGas() {}
stopGas() {}
}
class Oven {
setTemperature(degrees: number) {}
}
type CookingDevice<T> = T extends 'grill' ? Grill : Oven; // <- conditional
// "grill" here is a literal type, not a value
let device1: CookingDevice<'grill'>;
// let device1: Grill
let device2: CookingDevice<'oven'>;
// let device2: Oven<condition> ? <expr-If-True> : <expr-If-False>;
| part | expression |
|---|---|
| condition | T extends "grill" |
| expr-If-True | Grill |
| expr-If-False | Oven |
Note the use of the
extendskeyword in the condition.You can think of it like a
>=comparison (as in more specific or equally specific).Or like "
Tmust be at least the literal type"grill""Or "Does everything in
Tfit into"grill"?"Since
"grill"is a literal type thenT's only truthy option is to be"grill"This is basically the same as a generic constraint...it's setting a minimum requirement
Expressing conditions
T extends <conditional-type> ? <if-true> : <if-false>
If T is more specific than <conditional-type> then the condition is true.
An object with multiple properties is more specific than an object with only one of those properties.
interface obj_1 {
id: number;
name: string;
address: string;
}
// is more specific than
interface obj_2 {
id: number;
}That's why something like obj_2 is often used as a <conditional-type> or generic constraint.
So in the case of objects, having "more things" means more specific.
But in the case of 64 extends number, 64 is more specific because it has "less things" than number (which can be thought of as "all numbers").
| condition | evaluate to: |
|---|---|
64 extends number | true |
number extends 64 | false |
string[] extends any | true |
string[] extends any[] | true |
never extends any | true |
any extends any | true |
Date extends {new (...args: any[]): any } | false |
(typeof Date) extends {new (...args: any[]): any } | true |
(typeof Date) extends {new (...args: any[]): any } is true because:
typeof Daterefers to the class itself...the constructor (or the "factory") for Dates.{new (...args: any[]): any }refers to things that can be used with thenewkeyword (or things that are "new-able").Daterefers to aninstanceof the class Date. It is created by usingnewwith the Date class constructor, butDateas aninstanceis not "new-able".
From the Intermediate TypeScript↗ course on FEM↗ taught by Mike North↗.