What are Conditional Types?
Conditional types are a TypeScript feature introduced in version 2.8 that enable you to define types based on conditions. They allow you to express complex type relationships and create reusable type logic. Conditional types are defined using the extends keyword and the ? operator, making them highly versatile.
Basic Syntax
Let's start with the basic syntax of a conditional type:
type MyConditionalType<T> = T extends U ? X : Y;
-
Tis the type parameter we want to test. -
Uis the type we're checkingTagainst. -
Xis the type thatMyConditionalTypeevaluates to if the condition istrue. -
Yis the type thatMyConditionalTypeevaluates to if the condition isfalse.
Understanding the Condition
The condition in a conditional type can be any valid type operation or expression. Commonly used operations include keyof, extends, and infer. Here's a breakdown of some common condition types:
-
keyof: Checks if a type has a specific key. -
extends: Tests if a type extends another type. -
infer: Extracts a type from another type.
Basic Examples
Conditional Type with extends
type IsString<T> = T extends string ? true : false;
const a: IsString<"Hello"> = true; // true
const b: IsString<42> = false; // false
In this example, IsString<T> checks if T extends (is assignable to) the string type. If so, it returns true, otherwise false.
keyof and Index Signatures
type KeysOfType<T, U> = {
[K in keyof T]: T[K] extends U ? K : never;
};
interface Person {
name: string;
age: number;
email: string;
}
type StringKeys = KeysOfType<Person, string>; // "name" | "email"
Here, KeysOfType<T, U> generates a new type that contains keys from T whose corresponding values extend U. In our Person interface, it extracts keys with values of type string.
Advanced Examples
Mapping Union Types
type FilterByType<T, U> = T extends U ? T : never;
type OnlyStrings = FilterByType<string | number | boolean, string>; // "string"
This example creates a type that filters out all non-U types from a union type T.
Inferring Function Parameters
type FirstParam<T> = T extends (param1: infer P, ...args: any[]) => any ? P : never;
function greet(name: string, age: number) {
return `Hello, ${name}! You are ${age} years old.`;
}
type NameParam = FirstParam<typeof greet>; // string
Here, FirstParam<T> extracts the type of the first parameter of a function. In this case, it infers string from the greet function.
Built-in Conditional Types
TypeScript provides several built-in conditional types that simplify common type operations.
Exclude
Exclude<T, U> removes all types from T that are assignable to U.
type Excluded = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
Extract
Extract<T, U> extracts all types from T that are assignable to U.
type Extracted = Extract<"a" | "b" | "c", "a" | "b">; // "a" | "b"
NonNullable
NonNullable<T> removes null and undefined from T.
type NotNullable = NonNullable<string | null | undefined>; // string
Top comments (0)