Conditional Type Expression
SomeType extends OtherType ? TrueType : FalseType;
Basic Usages
Example 1
type NumberOrNot<T> = T extends number ? string : never;
Example 2
type StringOrNot<T> = T extends string ? string : never;
Example 3
type BooleanOrNumberOnly<T> = T extends boolean | number ? T : never;
type NewType = StringOrNumberOnly<string | number | boolean>;
const a1: NewType = true; // boolean
const a2: NewType = 23; // number
Example 4
type TypeStringName<T> = T extends string ? 'string' : T extends boolean ? 'boolean' : T extends Error ? 'error' : never;
type STRING_TYPE = TypeStringName<'hello'>; // 'string'
type BOOLEAN_TYPE = TypeStringName<false>; // 'boolean'
type ERROR_TYPE = TypeStringName<TypeError>; // 'error'
interface Animal {
live(): void;
}
interface Dog extends Animal {
woof(): void;
}
type Example = Dog extends Animal ? number : string;
type Example2 = RegExp extends Animal ? number : string;
Conditional Type Constraint
Wrong Usage
type MessageOf<T> = T['message'];
// TypeScript errors in this line because T doesn't have `message` field.
Correct Usage
MessageOf
type has a generic type T
and T
should have message
field.
type MessageOf<T extends { message: unknown }> = T['message'];
type Email = { message: string };
type EmailMessageContents = MessageOf<Email>;
Email
type has a message
field in line 3 in the above code.
So, EmailMessageContents
type will be string
due to { message: string }
as shown below popup;
Assume that Dog
type doesn't have message
field. So TypeScript errors in line 9 as shown below;
Some Usages
Example 1
interface IdLabel {
id: number;
}
interface NameLabel {
name: string;
}
type NameOrId<T extends number | string> = T extends number ? IdLabel : NameLabel;
function createLabel<T extends number | string>(idOrName: T): NameOrId<T> {
throw "unimplemented";
}
const a1 = createLabel('TypeScript');
const a2 = createLabel(2893);
const a3 = createLabel(Math.random() ? 'hello' : 333);