Master TypeScript Utility Types: From Basics to Advanced TS Tricks
This article introduces TypeScript’s built‑in utility types such as Partial, Required, Readonly, Record, Pick, Omit, and advanced tools like DeepPartial, AssertEqual, and FunctionMapType, demonstrating how to use them with clear code examples and showing how to create powerful higher‑order type extensions for safer, more expressive frontend development.
Introduction
TypeScript’s type system is one of its most powerful features, offering rich built‑in utility types that allow declarative type manipulation and transformation. These utilities act like a “Swiss army knife” for the type system, greatly improving frontend developers’ productivity and reducing errors.
PS: If you already understand the built‑in types, you can skip to the advanced section.
1. Common Built‑in TypeScript Utility Types
1. Basic Object Utilities
Partial<T> – makes all properties of a type optional.
<code>interface User {
id: number;
name: string;
email: string;
}
type PartialUser = Partial<User>;
/* Equivalent to:
{
id?: number;
name?: string;
email?: string;
}
*/</code>Required<T> – makes all properties of a type required.
<code>type OptionalUser = {
id?: number;
name?: string;
};
type RequiredUser = Required<OptionalUser>;
/* Equivalent to:
{
id: number;
name: string;
}
*/</code>Readonly<T> – makes all properties of a type read‑only.
<code>interface MutableConfig {
apiUrl: string;
timeout: number;
}
type ImmutableConfig = Readonly<MutableConfig>;
/* Equivalent to:
{
readonly apiUrl: string;
readonly timeout: number;
}
*/</code>Record<K, T> – creates an object type with specific key and value types.
<code>type PagePaths = Record<'home' | 'about' | 'contact', string>;
/* Equivalent to:
{
home: string;
about: string;
contact: string;
}
*/
type UserMap = Record<number, User>;
/* Equivalent to:
{
[key: number]: User;
}
*/</code>2. Property Selection Utilities
Pick<T, K> – constructs a new type by picking a set of properties K from type T.
<code>interface FullUser {
id: number;
name: string;
email: string;
password: string;
}
type PublicUser = Pick<FullUser, 'id' | 'name' | 'email'>;
/* Equivalent to:
{
id: number;
name: string;
email: string;
}
*/</code>Omit<T, K> – constructs a new type by omitting a set of properties K from type T.
<code>type SecureUser = Omit<FullUser, 'password'>;
/* Equivalent to:
{
id: number;
name: string;
email: string;
}
*/</code>3. Union Type Utilities
Exclude<T, U> – removes from T those types that are assignable to U.
<code>type T = number | string | boolean;
type Numeric = Exclude<T, string | boolean>; // number</code>Extract<T, U> – extracts from T those types that are assignable to U.
<code>type T = string | number | boolean;
type StringOnly = Extract<T, string>; // string
type Numeric = Extract<T, number | bigint>; // number</code>NonNullable<T> – removes null and undefined from T.
<code>type Nullable = string | null | undefined;
type SafeString = NonNullable<Nullable>; // string</code>4. Function Type Utilities
ReturnType<T> – obtains the return type of a function type.
<code>type Fn = () => Promise<string>;
type Result = ReturnType<Fn>; // Promise<string></code>Parameters<T> – obtains a tuple type of the parameters of a function type.
<code>type Handler = (req: Request, res: Response) => void;
type HandlerArgs = Parameters<Handler>; // [Request, Response]</code>ConstructorParameters<T> – obtains a tuple type of the constructor parameters of a class.
<code>class User {
constructor(public id: number, public name: string) {}
}
type UserParams = ConstructorParameters<typeof User>; // [number, string]</code>2. Advanced TS: Higher‑Order Type Extensions
1. Basic Type Extensions
KeyOf<T> – obtains a union of all keys of type T.
<code>type KeyOf<T> = keyof T;
type Obj = { a: number; b: string };
type Keys = KeyOf<Obj>; // "a" | "b"</code>Awaitable<T> – represents a value that can be either T or a Promise of T.
<code>type Awaitable<T> = T | Promise<T> | Awaitable<T>;
const a: Awaitable<number> = 42;
const b: Awaitable<number> = Promise.resolve(42);</code>ElementType<T> – extracts the element type of an array.
<code>type ElementType<T> = T extends Array<infer U> ? U : never;
type A = ElementType<string[]>; // string
type B = ElementType<number>; // never</code>Arrayable<T> – creates a type that can be T or an array of T.
<code>type Arrayable<T> = T | T[];
const a: Arrayable<string> = "hello";
const b: Arrayable<string> = ["hello", "world"];</code>ToArray<T> – converts T to an array type, leaving arrays unchanged.
<code>type ToArray<T> = T extends (infer U)[] ? U[] : T[];
type A = ToArray<string>; // string[]
type B = ToArray<number[]>; // number[]</code>ArgumentType<T> – obtains the parameter tuple type of a function.
<code>type ArgumentType<T> = T extends (...args: infer A) => unknown ? A : never;
type Fn = (a: number, b: string) => boolean;
type Args = ArgumentType<Fn>; // [number, string]</code>Shift<T> – removes the first element from a tuple type.
<code>type Shift<T> = T extends [_: unknown, ...args: infer Rest] ? Rest : never;
type A = Shift<[number, string, boolean]>; // [string, boolean]</code>Pop<T> – removes the last element from a tuple type.
<code>type Pop<T extends unknown[]> = T extends [...infer Rest, unknown] ? Rest : never;
type A = Pop<[number, string, boolean]>; // [number, string]</code>FlatObjectTuple<T> – flattens an object tuple type to resolve intersection display issues.
<code>type FlatObjectTuple<T> = { [K in keyof T]: T[K] };</code>2. Intermediate Extensions
(1) Type Assertion Tools
AssertEqual<T, U> – asserts whether two types are equal.
<code>type AssertEqual<T, U> = [T] extends [U] ? ([U] extends [T] ? true : false) : false;</code>(2) Deep Type Transformations
DeepPartial<T> – makes all properties of T recursively optional.
<code>type DeepPartial<T> = { [P in keyof T]?: DeepPartial<T[P]>; };
type Obj = { a: { b: number; c: string } };
type PartialObj = DeepPartial<Obj>; // { a?: { b?: number; c?: string } }</code>(3) Object Property Control
PartialByKeys<T, K> – makes a subset K of properties optional.
<code>type PartialByKeys<T, K extends keyof T = keyof T> =
FlatObjectTuple<Partial<Pick<T, Extract<keyof T, K>>> & Omit<T, K>;
type Obj = { a: number; b: string; c: boolean };
type PartialA = PartialByKeys<Obj, 'a' | 'b'>; // { a?: number; b?: string; c: boolean }</code>RequiredByKey<T, K> – makes a subset K of properties required.
<code>type RequiredByKey<T, K extends keyof T = keyof T> =
FlatObjectTuple<Required<Pick<T, Extract<keyof T, K>>> & Omit<T, K>;
type Obj = { a?: number; b?: string; c?: boolean };
type RequiredA = RequiredByKey<Obj, 'a' | 'b'>; // { a: number; b: string; c?: boolean }</code>3. Advanced Function Tools
FunctionKeysObject<T> – extracts all function‑type properties from an object.
<code>type FunctionKeysObject<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? T[K] : never;
};</code>FunctionMapType<F> – creates a type‑safe event system based on function keys.
<code>type FunctionMapType<F> = <T extends keyof FunctionKeysObject<F>>(key: T, ...args: Parameters<F[T]>) => void;
interface EventFunctions {
a(): void;
b(params: { c: string; d: number }): void;
}
declare const emit: FunctionMapType<EventFunctions>;
emit('a'); // OK
emit('b', { c: '123', d: 123 }); // OK
// emit('b', { c: 123, d: '123' }); // Error</code>Conclusion
Types are documentation, constraints, and power!
TypeScript’s utility type system is a powerful type‑conversion factory; the built‑in utilities provide the foundation, while higher‑order extensions enable you to build complex systems in a more elegant and type‑safe manner.
Sohu Tech Products
A knowledge-sharing platform for Sohu's technology products. As a leading Chinese internet brand with media, video, search, and gaming services and over 700 million users, Sohu continuously drives tech innovation and practice. We’ll share practical insights and tech news here.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.