TypeScript provides several built-in utility types that facilitate common type transformations.
These utilities are available globally and can be extremely helpful for everyday TypeScript development.
// Partial - Make all properties in T optional
interface Todo {
title: string;
description: string;
}
function updateTodo(todo: Todo, fieldsToUpdate: Partial) {
return { ...todo, ...fieldsToUpdate };
}
const todo1 = {
title: "Learn TypeScript",
description: "Study utility types"
};
const todo2 = updateTodo(todo1, {
description: "Study advanced TypeScript features"
});
// Required - Make all properties in T required
interface Props {
a?: number;
b?: string;
}
const obj: Props = { a: 5 }; // OK
const obj2: Required = { a: 5, b: "hello" }; // Error: Property 'b' is missing
// Readonly - Make all properties in T readonly
interface Todo {
title: string;
}
const todo: Readonly = {
title: "Delete inactive users"
};
todo.title = "Hello"; // Error: cannot assign to readonly property
// Record - Construct a type with a set of properties K of type T
interface CatInfo {
age: number;
breed: string;
}
type CatName = "miffy" | "boris" | "mordred";
const cats: Record = {
miffy: { age: 10, breed: "Persian" },
boris: { age: 5, breed: "Maine Coon" },
mordred: { age: 16, breed: "British Shorthair" }
};
// Pick - From T, pick a set of properties whose keys are in the union K
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick;
const todo: TodoPreview = {
title: "Clean room",
completed: false
};
// Omit - From T, remove a set of properties whose keys are in the union K
interface Todo {
title: string;
description: string;
completed: boolean;
createdAt: number;
}
type TodoPreview = Omit;
const todo: TodoPreview = {
title: "Clean room",
completed: false,
createdAt: 1615544252770
};
type TodoInfo = Omit;
const todoInfo: TodoInfo = {
title: "Pick up kids",
description: "Kindergarten closes at 5pm"
};
// Exclude - Exclude from T those types that are assignable to U
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude void), Function>; // string | number
// Extract - Extract from T those types that are assignable to U
type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T1 = Extract void), Function>; // () => void
// NonNullable - Exclude null and undefined from T
type T0 = NonNullable; // string | number
type T1 = NonNullable; // string[]
// Parameters - Obtain the parameters of a function type in a tuple
declare function f1(arg: { a: number; b: string }): void;
type T0 = Parameters<() => string>; // []
type T1 = Parameters<(s: string) => void>; // [string]
type T2 = Parameters<(arg: T) => T>; // [unknown]
type T3 = Parameters; // [{ a: number; b: string }]
// ReturnType - Obtain the return type of a function type
declare function f1(): { a: number; b: string };
type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void
type T2 = ReturnType<() => T>; // unknown
type T3 = ReturnType<() => T>; // number[]
type T4 = ReturnType; // { a: number; b: string }
// InstanceType - Obtain the instance type of a constructor function type
class C {
x = 0;
y = 0;
}
type T0 = InstanceType; // C
type T1 = InstanceType; // any
type T2 = InstanceType; // never
// ThisType - Marker for contextual 'this' type
interface ObjectDescriptor {
data?: D;
methods?: M & ThisType; // Type of 'this' in methods is D & M
}
function makeObject(desc: ObjectDescriptor): D & M {
let data: object = desc.data || {};
let methods: object = desc.methods || {};
return { ...data, ...methods } as D & M;
}
let obj = makeObject({
data: { x: 0, y: 0 },
methods: {
moveBy(dx: number, dy: number) {
this.x += dx; // Strongly typed this
this.y += dy; // Strongly typed this
}
}
});
obj.x = 10;
obj.y = 20;
obj.moveBy(5, 5);
// Uppercase, Lowercase, Capitalize, Uncapitalize
type Greeting = "Hello, world";
type ShoutyGreeting = Uppercase; // "HELLO, WORLD"
type ASCIICacheKey = `ID-${Uppercase}`;
type MainID = ASCIICacheKey<"my_app">; // "ID-MY_APP"
type QuietGreeting = Lowercase; // "hello, world"
type LowercaseGreeting = "hello, world";
type Greeting = Capitalize; // "Hello, world"
type UppercaseGreeting = "HELLO WORLD";
type UncomfortableGreeting = Uncapitalize; // "hELLO WORLD"
Utility types are one of TypeScript's most powerful features, allowing developers to perform complex type transformations
with minimal code. They're particularly useful for creating flexible APIs, managing state shapes, and ensuring type safety
across large codebases. By mastering these utility types, you can write more maintainable and robust TypeScript code.