TypeScript Basic Generics
Generics let you create "type variables" — placeholders for a type that is filled in later. You can use them in classes, functions and type aliases so they do not need to name the exact types they work with.
Generics make it easier to write code that can be reused with many different types.
Functions
Generic functions are more general: they work with any type, while still describing the input and return types accurately.
Note: TypeScript can also infer the generic type parameters from the arguments you pass, so you often do not need to write them out.
Example
S and T are type variables. The call fills them in as string and number, so the result is typed [string, number].
createPair<S, T> that takes v1: S and v2: T and returns the tuple [S, T], then log createPair<string, number>('hello', 42) so the output is the array [ 'hello', 42 ].
Output: TypeScript file and captured output
function createPair<S, T>(v1: S, v2: T): [S, T] {
return [v1, v2];
}
console.log(createPair<string, number>('hello', 42)); // ['hello', 42]
Classes
Generics can also make a class general, like the built-in Map, which works with any key and value types.
Note: TypeScript can infer the generic type parameter of a class when it is used in a constructor parameter.
Example
NamedValue<number> makes T a number, so setValue only accepts numbers.
NamedValue<T> with a private _value: T | undefined, a constructor taking private name: string, setValue(value: T), getValue(): T | undefined and a toString() returning "name: value"; create new NamedValue<number>('myNumber'), set the value to 10 and log toString(), printing "myNumber: 10".
Output: TypeScript file and captured output
class NamedValue<T> {
private _value: T | undefined;
constructor(private name: string) {}
public setValue(value: T) {
this._value = value;
}
public getValue(): T | undefined {
return this._value;
}
public toString(): string {
return `${this.name}: ${this._value}`;
}
}
let value = new NamedValue<number>('myNumber');
value.setValue(10);
console.log(value.toString()); // myNumber: 10
Type Aliases
Generics in type aliases let you create types that are reusable with many different inner types.
Note: The same works for interfaces, with the syntax interface Wrapped<T> {
Generic type alias syntax
Wrapped<T> is an object type whose value has whatever type you pass in, such as number.
type Wrapped<T> = { value: T };
const wrappedValue: Wrapped<number> = { value: 10 };
Default Value
A generic type parameter can have a default value. The default is used when no type is given and none can be inferred.
Default type parameter
T = string means new NamedValue('myNumber') without a type argument uses string for T.
class NamedValue<T = string> {
private _value: T | undefined;
constructor(private name: string) {}
public setValue(value: T) {
this._value = value;
}
public getValue(): T | undefined {
return this._value;
}
public toString(): string {
return `${this.name}: ${this._value}`;
}
}
let value = new NamedValue('myNumber');
value.setValue('myValue');
console.log(value.toString()); // myNumber: myValue
Extends
You can add constraints to a generic to limit which types are allowed.
A constraint lets you rely on a more specific type when you use the type variable.
Note: A constraint can be combined with a default value.
Constraining a type parameter
S extends string | number means only strings and numbers can be passed for S, so the values can safely be printed.
function createLoggedPair<S extends string | number, T extends string | number>(v1: S, v2: T): [S, T] {
console.log(`creating pair: v1='${v1}', v2='${v2}'`);
return [v1, v2];
}