Skip to main content

TypeScript Generics

·263 words·2 mins·
Table of Contents

TypeScript Generics
#

Generics allow you to create reusable components that can work with a variety of types rather than a single one. This allows users to consume these components and use their own types.

Generic Functions
#

Create a function that works with any type:

function identity<T>(arg: T): T {
  return arg;
}

let output1 = identity<string>("myString");
let output2 = identity<number>(42);

// Type argument inference
let output3 = identity("hello"); // Type inferred as string

Generic Interfaces
#

Define interfaces that use type parameters:

interface GenericIdentityFn<T> {
  (arg: T): T;
}

let myIdentity: GenericIdentityFn<number> = identity;
myIdentity(123);

Generic Classes
#

Create classes that work with multiple types:

class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };

Generic Constraints
#

Restrict generics to types that have certain properties:

interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length); // Now we know it has a .length property
  return arg;
}

loggingIdentity({ length: 10, value: 3 });
// loggingIdentity(3); // Error: number doesn't have .length

Using Type Parameters in Constraints
#

Declare a type parameter that is constrained by another type parameter:

function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a");
// getProperty(x, "m"); // Error: Argument of type 'm' isn't assignable

Related