YouTip LogoYouTip

Ts Utility Types

Utility Types are a series of built-in advanced types in TypeScript.

They help developers quickly create and transform types, improving code reusability and type safety.

Utility types are essentially generic types, implemented through mapped types and conditional types.



Why Utility Types Are Needed

In actual TypeScript development, we often need to create new types based on existing ones.

Manually creating these types is not only tedious but also error-prone.

Utility types provide a declarative way to transform and create types, greatly improving development efficiency.

Concept Explanation: Utility types are a series of predefined generic types in TypeScript. They use mapped types and conditional types to implement type transformation and generation.


Partial<T> - Optional Properties

Partial sets all properties of type T to optional.

This is very useful when creating partial update objects or handling form data.

Example

// Define user interface with required properties

interface User {

// User ID

 id: number;

// Username

 name: string;

// User email

 email: string;

}

// Partial: Make all properties optional

// The converted type has all properties as optional

 type PartialUser = Partial<User>;

// Use PartialUser type

// Can provide only some properties, not all required

var user: PartialUser ={ name:"Alice"};

console.log("Partial user: "+ JSON.stringify(user));

Output:

Partial user: {"name":"Alice"}

Application Scenario: Partial is commonly used for updating object data. For example: when updating only part of a user's information, you don't need to pass in the complete user object.


Required<T> - Required Properties

Required is the opposite of Partial, setting all optional properties to required.

Use when you need to ensure an object contains all properties.

Example

// Define config interface with optional properties

interface Config {

// Server address

 host?: string;

// Port number

 port?: number;

}

// Required: Make all optional properties required

// The converted type has all properties as required

 type RequiredConfig = Required<Config>;

// Use RequiredConfig type

// Must provide all properties

var config: RequiredConfig ={ host:"localhost", port:8080};

console.log("Config: "+ JSON.stringify(config));

Output:

Config: {"host":"localhost","port":8080}

Note: Required not only removes optional properties (?) but also removes the readonly modifier.


Readonly<T> - Read-only Properties

Readonly sets all properties to read-only.

Very useful when creating objects that should not be modified after creation.

Example

// Define user interface

interface User {

// Username

 name: string;

// User age

 age: number;

}

// Readonly: Make all properties read-only

// The converted type has all properties that cannot be modified

 type ReadonlyUser = Readonly<User>;

// Create read-only user object

var user: ReadonlyUser ={ name:"Alice", age:25};

// Attempting to modify read-only property will cause error

// user.name = "Bob"; // Error: Cannot assign to read-only property

console.log("Read-only user: "+ JSON.stringify(user));

Application Scenario: Readonly is commonly used to define configuration objects, enum value mappings, and other data that should not be modified.


Pick<T, K> - Select Properties

Pick selects specified properties K from type T to form a new type.

Use when you only need some properties of a type.

Example

// Define complete user interface

interface User {

// User ID

 id: number;

// Username

 name: string;

// User email

 email: string;

// User password

 password: string;

}

// Pick: Select specified properties to form new type

// Select id and name properties from User

 type UserBasicInfo = Pick<User,"id"|"name">;

// Use the selected type

var user: UserBasicInfo ={ id:1, name:"Alice"};

console.log("User basic info: "+ JSON.stringify(user));

Output:

User basic info: {"id":1,"name":"Alice"}

Comparison: Pick and Partial can be combined to create types containing only some optional properties.


Omit<T, K> - Exclude Properties

Omit excludes specified properties K from type T, returning a new type composed of remaining properties.

Opposite of Pick, used to remove unwanted properties.

Example

// Define complete user interface

interface User {

// User ID

 id: number;

// Username

 name: string;

// User email

 email: string;

// User password

 password: string;

}

// Omit: Exclude specified properties

// Exclude password property from User

 type UserWithoutPassword = Omit<User,"password">;

// Use the excluded type

var user: UserWithoutPassword ={ id:1, name:"Alice", email:"a@b.com"};

console.log("User without password: "+ JSON.stringify(user));

Output:

User without password: {"id":1,"name":"Alice","email":"a@b.com"}

Tip: Omit is the reverse operation of Pick. Use Omit when you need to exclude few properties, use Pick when you need to select few properties.


Record<K, T> - Construct Object Type

Record constructs an object type with keys of type K and values of type T.

Often used to create key-value pair mappings, dictionary types, etc.

Example

// Define role type

 type Role ="admin"|"user"|"guest";

// Record: Construct object type

// Keys are Role type, values are string array type

 type RolePermissions = Record<Role, string[]>;

// Use Record to create permission mapping

var permissions: RolePermissions ={

// Admin has all permissions

 admin:["read","write","delete"],

// Regular user has read and write permissions

 user:["read","write"],

// Guest only has read permission

 guest:

};

console.log("Admin permissions: "+ permissions.admin);

 console.log("Guest permissions: "+ permissions.guest);

Output:

Admin permissions: read,write,deleteGuest permissions: read

Application Scenario: Record is commonly used to create configuration mappings, state mappings, permission mappings, and other scenarios where you need to quickly look up values by key.


Exclude<T, U> - Exclude Types

Exclude excludes types from T that are assignable to U.

Mainly used for union types to exclude specific type members.

Example

// Define union type containing a, b, c, d

 type T ="a"|"b"|"c"|"d";

// Exclude: Exclude specified types from T

// Exclude "a", "b", "c", keep only "d"

 type NonABC = Exclude<T,"a"|"b"|"c">;

// Use the excluded type, can only assign "d"

var value: NonABC ="d";

console.log("Value: "+ value);

Output:

Value: d

Explanation: The principle of Exclude is: if a type in T is assignable to U, remove it from T.


Extract<T, U> - Extract Types

Extract is the opposite of Exclude, extracting types from T that are assignable to U.

Used to filter specific members from union types.

Example

// Define mixed union type containing strings and numbers

 type T ="a"|"b"|"c"|1|2|3;

// Extract: Extract specified types from T

// Extract all string types: "a", "b", "c"

 type Letters = Extract<T, string>;

// Use the extracted type

var letter: Letters ="a";

console.log("Letter: "+ letter);

Output:

Letter: a

Comparison: Extract and Exclude are complementary. Extract(T, U) is equivalent to Exclude(T, Exclude<T, U>).


NonNullable<T> - Exclude Null Values

NonNullable excludes null and undefined from type T.

Use when you need to ensure a type does not contain null values.

Example

// Define mixed type containing string, null, undefined, number

 type T = string |null|undefined| number;

// NonNullable: Exclude null and undefined

// After conversion only string and number remain

 type NotNull = NonNullable<T>;

// Use non-null type

var value: NotNull ="hello";

 value =42;

// Attempting to assign null will cause error

// value = null; // Error: Cannot assign null

console.log("Value: "+ value);

Note: NonNullable<T> is equivalent to Exclude<T, null | undefined>.


ReturnType<T> - Get Return Type

ReturnType gets the return type of function type T.

Often used to extract return types from existing functions.

Example

// Define function to get user

function getUser(){

return{ name:"Alice", age:25};

}

// Define function to get config

function getConfig(){

return{ host:"localhost", port:8080};

}

// ReturnType: Get function's return type

// Extract return type of getUser function

 type UserType = ReturnType<typeof getUser>;

// Extract return type of getConfig function

 type ConfigType = ReturnType<typeof getConfig>;

// Use extracted return types to create objects

var user: UserType ={ name:"Bob", age:30};

var config: ConfigType ={ host:"example.com", port:3000};

console.log("User: "+ JSON.stringify(user));

 console.log("Config: "+ JSON.stringify(config));

Output:

User: {"name":"Bob","age":30}Config: {"host":"example.com","port":3000}

Tip: The T in ReturnType<T> must be a function type. You can use typeof to get the function's type.


Notes

  • All utility types are generic: You should pass in specific type parameters when using them
  • Readonly and optional: Utility types can be combined, e.g., Partial<Readonly<T>>
  • Built-in types: These utility types are built into TypeScript, no installation needed
  • Custom utility types: You can create custom utility types based on mapped types and conditional types

Advanced: If built-in utility types don't meet your needs, you can refer to TypeScript source code to implement custom utility types.


Summary

TypeScript utility types are an important part of the type system.

  • Partial<T>: Make all properties optional
  • Required<T>: Make all properties required
  • Readonly<T>: Make all properties read-only
  • Pick<T,K>: Select specified properties
  • Omit<T,K>: Exclude specified properties
  • Record<K,T>: Construct object type
  • Exclude/Extract: Type filtering
  • NonNullable: Exclude null and undefined
  • ReturnType: Get function return type

Best Practice: Proper use of utility types can make code more type-safe, reduce repetitive type definitions, and improve code maintainability.


← Ts Mapped TypesTs Type Guards β†’