🔷 TypeScript Q69 / 101

Explain utility type implementation.

AI-Powered Answer ✓ Answered

TypeScript utility types are powerful constructs that enable type transformations. They are built using advanced type system features like mapped types, conditional types, and inference, allowing developers to create new types based on existing ones in a concise and robust manner. Understanding their underlying implementation provides deeper insight into TypeScript's type manipulation capabilities.

Core Concepts for Utility Types

Many utility types leverage core TypeScript features like mapped types, which iterate over properties of an object type; the 'keyof' operator, which gets the union of known property names of an object type; and conditional types, which allow types to be chosen based on a condition ('infer' keyword is often used with conditional types to extract parts of a type).

Partial<T>

Makes all properties of T optional. It iterates through each property P of T and applies the '?' modifier to make it optional. This is achieved using a mapped type.

typescript
type MyPartial<T> = {
  [P in keyof T]?: T[P];
};

Required<T>

Makes all properties of T required. It iterates through each property P of T and applies the '-?' modifier to remove the optional flag. This is also a mapped type.

typescript
type MyRequired<T> = {
  [P in keyof T]-?: T[P];
};

Readonly<T>

Makes all properties of T readonly. It iterates through each property P of T and applies the 'readonly' modifier. This is a mapped type.

typescript
type MyReadonly<T> = {
  readonly [P in keyof T]: T[P];
};

Record<K, V>

Constructs an object type whose property keys are K and whose property values are V. It's essentially a mapped type where K provides the keys and V provides the value type for each key.

typescript
type MyRecord<K extends keyof any, T> = {
  [P in K]: T;
};

Pick<T, K>

Constructs a type by picking the set of properties K from T. It uses a mapped type to iterate over the keys K that must be a subset of 'keyof T'.

typescript
type MyPick<T, K extends keyof T> = {
  [P in K]: T[P];
};

Omit<T, K>

Constructs a type by picking all properties from T and then removing K. It combines a mapped type with the Exclude utility type (or a similar conditional type) to filter out undesired keys.

typescript
type MyOmit<T, K extends keyof any> = {
  [P in Exclude<keyof T, K>]: T[P];
};

Exclude<T, U>

Excludes from T those types that are assignable to U. This is a conditional type that checks if each member of T (when T is a union) extends U. If it does, it's excluded (resolves to 'never'); otherwise, it's included.

typescript
type MyExclude<T, U> = T extends U ? never : T;

Extract<T, U>

Extracts from T those types that are assignable to U. This is the inverse of Exclude, using a conditional type to include members of T that extend U.

typescript
type MyExtract<T, U> = T extends U ? T : never;

NonNullable<T>

Excludes 'null' and 'undefined' from T. It's a specific application of Exclude to remove the 'null' and 'undefined' types from a union.

typescript
type MyNonNullable<T> = T extends null | undefined ? never : T;

Parameters<T>

Obtains the parameters of a function type T as a tuple. It uses conditional types with the 'infer' keyword to extract the parameter types when T is assignable to a function type.

typescript
type MyParameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

ReturnType<T>

Obtains the return type of a function type T. It also uses conditional types with 'infer' to extract the return type of the function.

typescript
type MyReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

Awaited<T>

This type is intended to model the operations of 'await' in 'async' functions, specifically how it recursively unwraps Promises. It repeatedly unwraps PromiseLike types until a non-Promise type is reached. The actual implementation handles more edge cases, but the core idea is recursive Promise unwrapping.

typescript
type MyAwaited<T> =
  T extends PromiseLike<infer U> ? MyAwaited<U> : T;