In TypeScript we can apply constraints on Generic type parameters (e.g. T) by using keyword extends (e.g. T extends Serializable).
Examples
generic-constraints-example.tsinterface Shape {
draw();
}
//applying constraint on Type Parameter S to be of only Shape type
function drawShapes<S extends Shape>(shapes: S[]): void{
shapes.forEach(shape => shape.draw());
}
class Circle implements Shape{
draw() {
console.log(`drawing Circle`)
}
}
class Rectangle implements Shape{
draw() {
console.log(`drawing Rectangle`)
}
}
let circle = new Circle();
let rectangle = new Rectangle();
drawShapes([circle, rectangle]); Outputdrawing Circle drawing Rectangle
Passing anything other than Shape type to drawShape() function will cause compile time error:
generic-constraints-example2.tsinterface Shape {
draw();
}
//applying constraint on Type Parameter S to be of only Shape type
function drawShapes<S extends Shape>(shapes: S[]): void{
shapes.forEach(shape => shape.draw());
}
drawShapes(["my shape"]); Outputgeneric-constraints-example2.ts(10,13): error TS2322: Type 'string' is not assignable to type 'Shape'.
Using other Type Parameters in Generic Constraints
We can declare a type parameter that is constrained by another type parameter.
Following example applies the constraint on K to be keyof of type T:
generic-constraint-type-param.tsfunction getProp<T, K extends keyof T>(key: K, obj: T): any {
return obj[key];
}
let obj = {a: 2, b: 3, c: 4};
let prop = getProp('c', obj);
console.log(prop); Output4
Generic constraint on constructor function
A constructor can be used as a function parameter via function type syntax (i.e. new() => MyObj). This might be useful for creating factory functions involving generic:
generics-in-factories.tsfunction createInstance<T>(t: new () => T): T {
return new t();
}
class Test {
x: number = 4;
}
let test: Test = createInstance(Test);
console.log(test); OutputTest { x: 4 }
A constructor with parameters:
generics-in-factories2.tsfunction createInstance2<T>(t: new(...constructorArgs:any[]) => T, ...args: any[]): T {
return new t(args);
}
class Test2 {
private x: number;
constructor(x: number) {
this.x = x;
}
}
let test2: Test2 = createInstance2(Test2, 5);
console.log(test2); OutputTest2 { x: [ 5 ] }
In above examples we used generics to specify return type T only. We can use generics for the constructor type as well:
generics-in-factories3.tsfunction createInstance3<R, T extends { new(...constructorArgs: any[]): R }>(constructor: T, ...args: any[]): R {
return new constructor(args);
}
class Test3 {
private x: number;
constructor(x: number) {
this.x = x;
}
}
let test3: Test3 = createInstance3(Test3, 6);
console.log(test3);
OutputTest3 { x: [ 6 ] }
Example ProjectDependencies and Technologies Used: |