extends
The extends keyword in TypeScript serves two primary purposes: interface inheritance and conditional type evaluation.
Interface Inheritance
In TypeScript, extends enables interfcae inheritance similar to class inheritance in ES6. Here's an example:
interface IName {
name: string;
}
interface IAge {
age: number;
}
interface IPerson extends IAge, IName {
sex: string;
}
const obj: IPerson = {
age: 18,
name: 'Jenny',
sex: 'female'
};
In this case, IPerson inherits properties from both IAge and IName, combining them with its own property sex.
Conditional Type Evaluation
Conditional types use extends for type checking and selecting between two types based on the relationship between them:
type WhatType<T> = T extends null | undefined ? never : T;
let typeString: WhatType<string> = 'abc'; // string
let typeNull: WhatType<null> = undefined; // never
If T is either null or undefined, it resolves to never; otherwise, it returns T.
infer
The infer keyword allows declaring type variables within conditional types for inference.
type ParamType<T> = T extends (...args: infer P) => any ? P : T;
interface User {
name: string;
age: number;
}
type Func = (user: User) => void;
type Param = ParamType<Func>; // Param = User
type AA = ParamType<string>; // string
Here, infer P captures the paramter types of a function.
keyof
The keyof operator extracts all keys of a type, returning a union type.
interface IPerson {
name: string;
age: number;
}
type allKeys1 = keyof IPerson; // 'name' | 'age'
type allKeys2 = keyof IPerson[]; // 'length' | 'toString' | ...
type allKeys3 = keyof { [x: string]: IPerson }; // string | number
typeof
The typeof operator retrieves the type of a variable or property.
interface IPerson {
name: string;
age: number;
}
const user: IPerson = {
name: 'jenny',
age: 18
};
type student = typeof user; // IPerson
in
The in operator iterates over union types.
type Keys = 'a' | 'b' | 'c';
type Obj = {
[p in Keys]: any;
}; // { a: any, b: any, c: any }
Partial
Converts all properties of a type into optional ones.
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface IPerson {
name: string;
age: number;
}
type Person = Partial<IPerson>;
// Equivalent to:
// type Person = {
// name?: string | undefined;
// age?: number | undefined;
// };
Required
Makes all properties required.
type Required<T> = {
[P in keyof T]-?: T[P];
};
interface IPerson {
name?: string;
age?: number;
}
type Person = Required<IPerson>;
// Equivalent to:
// type Person = {
// name: string;
// age: number;
// };
Readonly
Sets all properties to read-only.
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
interface IPerson {
name: string;
age: number;
}
type Person = Readonly<IPerson>;
// Equivalent to:
// type Person = {
// readonly name: string;
// readonly age: number;
// };
const obj: Person = {
name: 'lucy',
age: 18
};
// obj.name = 'jenny'; // Error: Cannot assign to read-only property
Mutable
Removes the read-only modifier from all properties.
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
interface IPerson {
readonly name: string;
readonly age: number;
}
type Person = Mutable<IPerson>;
// Equivalent to:
// type Person = {
// name: string;
// age: number;
// };
Exclude
Removes types from one union that exist in another.
type Exclude<T, U> = T extends U ? never : T;
type Excluded0 = Exclude<string | number | boolean, number>; // string | boolean
type Excluded1 = Exclude<'a' | 'b' | 'c', 'a' | 'c'>; // 'b'
Extract
Retrieves types from one union that also exist in another.
type Extract<T, U> = T extends U ? T : never;
type Extracted0 = Extract<string | number | boolean, number>; // number
type Extracted1 = Extract<'a' | 'b' | 'c', 'a' | 'c'>; // 'a' | 'c'
Pick
Selects specific properties from a type.
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
interface IPerson {
name: string;
age: number;
sex: string;
}
type Person = Pick<IPerson, 'name'>;
const user: Person = {
name: 'jenny',
// age: 18 // Error: Property 'age' does not exist on type 'Person'
};
Record
Creates an object type with keys from a union and values of a specified type.
type Record<K extends keyof any, T> = {
[P in K]: T;
};
type Record0 = Record<'a' | 'b', number>;
// Equivalent to:
// type Record0 = {
// a: number;
// b: number;
// };
Omit
Excludes specific properties from a type.
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
interface IPerson {
name: string;
age: number;
sex: string;
}
type Person = Omit<IPerson, 'name'>;
// Equivalent to:
// type Person = {
// age: number;
// sex: string;
// };
ReturnType
Obtains the return type of a function.
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type T1 = ReturnType<() => string>; // string
type T2 = ReturnType<(s: string) => void>; // void
NonNullable
Removes null and undefined from a union type.
type NonNullable<T> = T extends null | undefined ? never : T;
type T = NonNullable<string | string[] | null | undefined>; // string | string[]