Habibur Rahman
Theme:
TypeScript logo with wrench and other tools in the background
Habibur Rahman

Habibur Rahman

3 min read

You’re Not Using TypeScript Fully Until You Know About Utility Types

What are TypeScript utility types and how can they help you write cleaner, more maintainable code? Learn about the most commonly used utility types and their practical applications.

typescript How to

What are Utility Types?

Utility types are predefined generic types that perform common type transformations. They allow you to create new types based on existing ones, making your code more reusable and expressive.

Essential Utility Types

1. Partial<T>

Makes all properties of type T optional.

interface User {
    id: number;
    name: string;
    email: string;
    age: number;
}

// All properties become optional
type PartialUser = Partial<User>;

// Equivalent to:
type PartialUser = {
    id?: number;
    name?: string;
    email?: string;
    age?: number;
};

// Useful for update functions
function updateUser(id: number, updates: Partial<User>) {
    // Implementation here
}

updateUser(1, { name: "John" }); // Only update the name

2. Required<T>

Makes all properties of type T required (opposite of Partial).

interface Config {
    theme?: "light" | "dark";
    language?: string;
    notifications?: boolean;
}

type RequiredConfig = Required<Config>;

// All properties are now required
const config: RequiredConfig = {
    theme: "dark",
    language: "en",
    notifications: true,
};

3. Pick<T, K>

Creates a type by picking specific properties from type T.

interface User {
    id: number;
    name: string;
    email: string;
    password: string;
    createdAt: Date;
}

// Pick only the properties we want to expose
type PublicUser = Pick<User, "id" | "name" | "email">;

// Equivalent to:
type PublicUser = {
    id: number;
    name: string;
    email: string;
};

function getPublicUserInfo(user: User): PublicUser {
    return {
        id: user.id,
        name: user.name,
        email: user.email,
    };
}

4. Omit<T, K>

Creates a type by omitting specific properties from type T.

interface User {
    id: number;
    name: string;
    email: string;
    password: string;
}

// Omit sensitive information
type SafeUser = Omit<User, "password">;

// Equivalent to:
type SafeUser = {
    id: number;
    name: string;
    email: string;
};

5. Record<K, T>

Creates a type with keys of type K and values of type T.

type Theme = "light" | "dark" | "system";

type ThemeConfig = Record<
    Theme,
    {
        background: string;
        text: string;
        accent: string;
    }
>;

const themes: ThemeConfig = {
    light: {
        background: "#ffffff",
        text: "#000000",
        accent: "#0066cc",
    },
    dark: {
        background: "#000000",
        text: "#ffffff",
        accent: "#66ccff",
    },
    system: {
        background: "inherit",
        text: "inherit",
        accent: "inherit",
    },
};

Advanced Utility Types

ReturnType<T>

Extracts the return type of a function type.

function createUser(name: string, email: string) {
    return {
        id: Math.random(),
        name,
        email,
        createdAt: new Date(),
    };
}

type User = ReturnType<typeof createUser>;

// User is inferred as:
// {
//   id: number;
//   name: string;
//   email: string;
//   createdAt: Date;
// }

Parameters<T>

Extracts the parameter types of a function type as a tuple.

function updateUser(id: number, name: string, email: string) {
    // Implementation
}

type UpdateUserParams = Parameters<typeof updateUser>;
// [number, string, string]

// Useful for creating wrapper functions
function loggedUpdateUser(...args: UpdateUserParams) {
    console.log("Updating user with:", args);
    return updateUser(...args);
}

Real-World Example

Here’s how you might use utility types in a React component:

interface BaseProps {
    className?: string;
    children?: React.ReactNode;
    onClick?: () => void;
}

interface ButtonProps extends BaseProps {
    variant: "primary" | "secondary" | "danger";
    size: "sm" | "md" | "lg";
    disabled?: boolean;
}

// Create a type for required button props
type RequiredButtonProps = Required<Pick<ButtonProps, "variant" | "size">>;

// Create a type for optional styling props
type StylingProps = Pick<ButtonProps, "className">;

// Create a type for interactive props
type InteractiveProps = Omit<ButtonProps, "variant" | "size">;

function Button({ variant, size, ...restProps }: RequiredButtonProps & InteractiveProps) {
    // Component implementation
}

See Also