working-with-utility-types
- For TypeScript, please give 15 key points, tips and tricks I should know for a job interview for what we should know about Working with Utility Types
Exploring TypeScript Utility Types:
Sure, let's restate the examples without using the word "Example":
Partial<T>
Utility:Suppose there's a type for a user with required name and age. To allow updates for just one attribute at a time,
Partial<T>
converts all these properties to optional.type User = {
name: string;
age: number;
};
function updateUser(id: number, changes: Partial<User>) {
// Logic to update user
}
// It's possible to call updateUser with an object that includes any User properties
updateUser(1, { name: "Alice" }); // Providing only nameRequired<T>
Utility:Starting with a type where all properties are optional but then needing a version where all are mandatory,
Required<T>
transforms each optional property to a required one.type OptionalUser = {
name?: string;
age?: number;
};
function createUser(user: Required<OptionalUser>) {
// Logic to create a user
}
// When creating a user, both name and age must be supplied
createUser({ name: "Alice", age: 30 }); // Both properties are providedReadonly<T>
Utility:To prevent any modifications to an object after its creation,
Readonly<T>
marks all the properties of a type as unchangeable.type User = {
name: string;
age: number;
};
const user: Readonly<User> = {
name: "Alice",
age: 30
};
// Attempting to modify 'name' would result in an error because it's readonlyRecord<K,T>
Utility:For a scenario that requires an object with keys from one type and values from another,
Record<K,T>
is used. This is ideal for a situation like creating a list of users indexed by their IDs.type User = {
name: string;
age: number;
};
// A dictionary where each key is a number and each value is a User
const users: Record<number, User> = {
1: { name: "Alice", age: 30 },
2: { name: "Bob", age: 24 }
};
// Accessing the user with ID 1
const alice = users[1];
In these snippets, the key characteristics of Partial<T>
, Required<T>
, Readonly<T>
, and Record<K,T>
are demonstrated through practical usage in type definitions and function signatures.
// Given an existing type
type Todo = {
id: number;
title: string;
completed: boolean;
};
// Pick only 'title' and 'completed' properties to form a new type
type TodoPreview = Pick<Todo, 'title' | 'completed'>;
// Example object of type TodoPreview
const todoPreview: TodoPreview = {
title: 'Buy milk',
completed: false,
};
Omit<T,K>
Utility:Omit<T, K>
creates a type by excluding the propertiesK
from a typeT
. This is useful for omitting certain properties from a type.// Omit the 'completed' property from Todo
type TodoWithoutCompleted = Omit<Todo, 'completed'>;
// Example object of type TodoWithoutCompleted
const todoWithoutCompleted: TodoWithoutCompleted = {
id: 1,
title: 'Buy milk'
};Exclude<T,U>
Utility:Exclude<T, U>
constructs a type by excluding fromT
all properties that can be assigned toU
. This is useful when you want to exclude certain properties or values from a type.// Given two types
type AvailableDrinks = 'Coffee' | 'Tea' | 'Orange Juice' | 'Lemonade';
type HotDrinks = 'Coffee' | 'Tea';
// Exclude hot drinks from the list of all available drinks
type ColdDrinks = Exclude<AvailableDrinks, HotDrinks>;
// Example of a cold drink
const myDrink: ColdDrinks = 'Lemonade';Extract<T,U>
Utility:Extract<T, U>
is the inverse ofExclude
, creating a type by extracting fromT
all properties that can be assigned toU
.// Extract hot drinks from the list of all available drinks
type FavoriteHotDrinks = Extract<AvailableDrinks, HotDrinks>;
// Example of a favorite hot drink
const myFavoriteHotDrink: FavoriteHotDrinks = 'Coffee';
These utility types are particularly useful when you want to manipulate types to create new variations without repeating the definitions. They help in maintaining DRY (Don't Repeat Yourself) principles in your codebase by reusing parts of existing types.
NonNullable<T>
Utility:
NonNullable<T>
creates a type by excluding null
and undefined
from T
. This is used to make sure a type doesn't include null
or undefined
.
type MaybeString = string | null | undefined;
type DefinitelyString = NonNullable<MaybeString>;
// DefinitelyString is now just 'string' without 'null' or 'undefined'
ReturnType<T>
Utility:
ReturnType<T>
takes a function type and produces its return type. This is useful when you want to reuse the return type of a function in another context.
function getString() {
return "hello";
}
type FunctionReturn = ReturnType<typeof getString>;
// FunctionReturn is 'string'
InstanceType<T>
Utility:
InstanceType<T>
takes a constructor function type and produces a type for the instance of that constructor. This allows you to obtain the type of an object instance based on the constructor function.
class MyClass {
constructor(public id: number) {}
}
type MyClassInstance = InstanceType<typeof MyClass>;
// MyClassInstance is an instance type of MyClass with 'id' property
ThisParameterType<T>
Utility:
This utility extracts the this
type from a function type, which can be useful when working with certain types of method decorators or higher-order functions.
function myFunction(this: string, message: string) {
console.log(this);
}
type ThisType = ThisParameterType<typeof myFunction>;
// ThisType is 'string'
Parameters<T>
Utility:
Parameters<T>
takes a function type and produces a tuple type with all the parameter types of that function. It's useful when you want to type the parameters of a callback function.
function greet(name: string, age: number) {
console.log(`Hello, ${name}! You are ${age} years old.`);
}
type GreetParameters = Parameters<typeof greet>;
// GreetParameters is '[string, number]'
These utility types are powerful tools in TypeScript that can help you to create more robust and maintainable type definitions by leveraging TypeScript's type manipulation capabilities.