Type Manipulation

Creating Types from Types

Is it possible to create new types composing, manipulating or transforming existing types.

Intersection Types (&):

Allow you to combine multiple types into a single type:

type A = { foo: number };
type B = { bar: string };
type C = A & B; // Intersection of A and B
const obj: C = { foo: 42, bar: 'hello' };

Union Types (|):

Allow you to define a type that can be one of several types:

type Result = string | number;
const value1: Result = 'hello';
const value2: Result = 42;

Mapped Types:

Allow you to transform the properties of an existing type to create new type:

type Mutable<T> = {
    readonly [P in keyof T]: T[P];
};
type Person = {
    name: string;
    age: number;
};
type ImmutablePerson = Mutable<Person>; // Properties become read-only

Conditional types:

Allow you to create types based on some conditions:

type ExtractParam<T> = T extends (param: infer P) => any ? P : never;
type MyFunction = (name: string) => number;
type ParamType = ExtractParam<MyFunction>; // string

Indexed Access Types

In TypeScript is it possible to access and manipulate the types of properties within another type using an index, Type[Key].

type Person = {
    name: string;
    age: number;
};

type AgeType = Person['age']; // number
type MyTuple = [string, number, boolean];
type MyType = MyTuple[2]; // boolean

Utility Types

Several built-in utility types can be used to manipulate types, below a list of the most common used:

Awaited\<T>

Constructs a type that recursively unwraps Promise types.

type A = Awaited<Promise<string>>; // string

Partial\<T>

Constructs a type with all properties of T set to optional.

type Person = {
    name: string;
    age: number;
};

type A = Partial<Person>; // { name?: string | undefined; age?: number | undefined; }

Required\<T>

Constructs a type with all properties of T set to required.

type Person = {
    name?: string;
    age?: number;
};

type A = Required<Person>; // { name: string; age: number; }

Readonly\<T>

Constructs a type with all properties of T set to readonly.

type Person = {
    name: string;
    age: number;
};

type A = Readonly<Person>;

const a: A = { name: 'Simon', age: 17 };
a.name = 'John'; // Invalid

Record\<K, T>

Constructs a type with a set of properties K of type T.

type Product = {
    name: string;
    price: number;
};

const products: Record<string, Product> = {
    apple: { name: 'Apple', price: 0.5 },
    banana: { name: 'Banana', price: 0.25 },
};

console.log(products.apple); // { name: 'Apple', price: 0.5 }

Pick\<T, K>

Constructs a type by picking the specified properties K from T.

type Product = {
    name: string;
    price: number;
};

type Price = Pick<Product, 'price'>; // { price: number; }

Omit\<T, K>

Constructs a type by omitting the specified properties K from T.

type Product = {
    name: string;
    price: number;
};

type Name = Omit<Product, 'price'>; // { name: string; }

Exclude\<T, U>

Constructs a type by excluding all values of type U from T.

type Union = 'a' | 'b' | 'c';
type MyType = Exclude<Union, 'a' | 'c'>; // b

Extract\<T, U>

Constructs a type by extracting all values of type U from T.

type Union = 'a' | 'b' | 'c';
type MyType = Extract<Union, 'a' | 'c'>; // a | c

NonNullable\<T>

Constructs a type by excluding null and undefined from T.

type Union = 'a' | null | undefined | 'b';
type MyType = NonNullable<Union>; // 'a' | 'b'

Parameters\<T>

Extracts the parameter types of a function type T.

type Func = (a: string, b: number) => void;
type MyType = Parameters<Func>; // [a: string, b: number]

ConstructorParameters\<T>

Extracts the parameter types of a constructor function type T.

class Person {
    constructor(
        public name: string,
        public age: number
    ) {}
}
type PersonConstructorParams = ConstructorParameters<typeof Person>; // [name: string, age: number]
const params: PersonConstructorParams = ['John', 30];
const person = new Person(...params);
console.log(person); // Person { name: 'John', age: 30 }

ReturnType\<T>

Extracts the return type of a function type T.

type Func = (name: string) => number;
type MyType = ReturnType<Func>; // number

InstanceType\<T>

Extracts the instance type of a class type T.

class Person {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    sayHello() {
        console.log(`Hello, my name is ${this.name}!`);
    }
}

type PersonInstance = InstanceType<typeof Person>;

const person: PersonInstance = new Person('John');

person.sayHello(); // Hello, my name is John!

ThisParameterType\<T>

Extracts the type of 'this' parameter from a function type T.

interface Person {
    name: string;
    greet(this: Person): void;
}
type PersonThisType = ThisParameterType<Person['greet']>; // Person

OmitThisParameter\<T>

Removes the 'this' parameter from a function type T.

function capitalize(this: String) {
    return this[0].toUpperCase + this.substring(1).toLowerCase();
}

type CapitalizeType = OmitThisParameter<typeof capitalize>; // () => string

ThisType\<T>

Servers as a market for a contextual this type.

type Logger = {
    log: (error: string) => void;
};

let helperFunctions: { [name: string]: Function } & ThisType<Logger> = {
    hello: function () {
        this.log('some error'); // Valid as "log" is a part of "this".
        this.update(); // Invalid
    },
};

Uppercase\<T>

Make uppercase the name of the input type T.

type MyType = Uppercase<'abc'>; // "ABC"

Lowercase\<T>

Make lowercase the name of the input type T.

type MyType = Lowercase<'ABC'>; // "abc"

Capitalize\<T>

Capitalize the name of the input type T.

type MyType = Capitalize<'abc'>; // "Abc"

Uncapitalize\<T>

Uncapitalize the name of the input type T.

type MyType = Uncapitalize<'Abc'>; // "abc"

NoInfer\<T>

NoInfer is a utility type designed to block the automatic inference of types within the scope of a generic function.

Example:

// Automatic inference of types within the scope of a generic function.
function fn<T extends string>(x: T[], y: T) {
    return x.concat(y);
}
const r = fn(['a', 'b'], 'c'); // Type here is ("a" | "b" | "c")[]

With NoInfer:

// Example function that uses NoInfer to prevent type inference
function fn2<T extends string>(x: T[], y: NoInfer<T>) {
    return x.concat(y);
}

const r2 = fn2(['a', 'b'], 'c'); // Error: Type Argument of type '"c"' is not assignable to parameter of type '"a" | "b"'.