TypeScript Utility Types | Partial, Pick, Omit, Record & Built-ins

TypeScript Utility Types | Partial, Pick, Omit, Record & Built-ins

이 글의 핵심

A practical tour of TypeScript’s built-in utility types: how they transform types, how they are implemented with mapped types, and how to use them in real APIs and UI state.

Introduction

Utility types are type transformation tools that TypeScript provides out of the box.


1. Partial

Concept

Makes every property optional.

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

type PartialUser = Partial<User>;
// {
//     id?: string;
//     name?: string;
//     email?: string;
//     age?: number;
// }

Real-world example

function updateUser(id: string, updates: Partial<User>): User {
    const user = getUser(id);
    return { ...user, ...updates };
}

updateUser("U001", { name: "김철수" });
updateUser("U002", { email: "[email protected]", age: 30 });

How it works

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

2. Required

Concept

Makes every property required.

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

type RequiredUser = Required<User>;
// {
//     id: string;
//     name: string;
//     email: string;
//     age: number;
// }

How it works

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

3. Readonly

Concept

Makes every property read-only.

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

type ReadonlyUser = Readonly<User>;

const user: ReadonlyUser = {
    id: "U001",
    name: "홍길동",
    email: "[email protected]"
};

// user.name = "김철수";  // ❌ Error

How it works

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

4. Pick<T, K>

Concept

Selects only certain properties.

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

type UserPreview = Pick<User, "id" | "name">;
// {
//     id: string;
//     name: string;
// }

const preview: UserPreview = {
    id: "U001",
    name: "홍길동"
};

Real-world example

type LoginForm = Pick<User, "email">;
type SignupForm = Pick<User, "name" | "email" | "age">;

How it works

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

5. Omit<T, K>

Concept

Removes specific properties.

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

type UserWithoutPassword = Omit<User, "password">;
// {
//     id: string;
//     name: string;
//     email: string;
// }

const user: UserWithoutPassword = {
    id: "U001",
    name: "홍길동",
    email: "[email protected]"
};

Real-world example

type UserResponse = Omit<User, "password">;
type CreateUserRequest = Omit<User, "id">;

How it works

type MyOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

6. Record<K, T>

Concept

Builds an object type with fixed keys and a given value type.

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

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

const permissions: Permissions = {
    admin: ["read", "write", "delete"],
    user: ["read", "write"],
    guest: ["read"]
};

Real-world example

type ErrorCode = "NOT_FOUND" | "UNAUTHORIZED" | "SERVER_ERROR";
type ErrorMessages = Record<ErrorCode, string>;

const errors: ErrorMessages = {
    NOT_FOUND: "리소스를 찾을 수 없습니다",
    UNAUTHORIZED: "인증이 필요합니다",
    SERVER_ERROR: "서버 에러가 발생했습니다"
};

type Language = "ko" | "en" | "ja";
type Translations = Record<Language, Record<string, string>>;

const translations: Translations = {
    ko: { greeting: "안녕하세요", goodbye: "안녕히 가세요" },
    en: { greeting: "Hello", goodbye: "Goodbye" },
    ja: { greeting: "こんにちは", goodbye: "さようなら" }
};

How it works

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

7. Exclude<T, U>

Concept

Removes types from a union.

type AllRoles = "admin" | "user" | "guest" | "moderator";
type NonAdminRoles = Exclude<AllRoles, "admin">;
// "user" | "guest" | "moderator"

let role: NonAdminRoles = "user";

8. Extract<T, U>

Concept

Extracts only the members of a union that are assignable to U.

type AllRoles = "admin" | "user" | "guest" | "moderator";
type AdminRoles = Extract<AllRoles, "admin" | "moderator">;
// "admin" | "moderator"

let role: AdminRoles = "admin";

9. NonNullable

Concept

Removes null and undefined from a type.

type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>;
// string

let value: DefiniteString = "hello";

10. ReturnType

Concept

Extracts a function’s return type.

function getUser() {
    return {
        id: "U001",
        name: "홍길동",
        email: "[email protected]"
    };
}

type User = ReturnType<typeof getUser>;

11. Parameters

Concept

Extracts a function’s parameter types as a tuple.

function createUser(name: string, age: number, email: string) {
    return { name, age, email };
}

type CreateUserParams = Parameters<typeof createUser>;
// [string, number, string]

const params: CreateUserParams = ["홍길동", 25, "[email protected]"];
createUser(...params);

12. Practical examples

Example 1: API typing

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

type CreateUserRequest = Omit<User, "id" | "createdAt" | "updatedAt">;
type UpdateUserRequest = Partial<Omit<User, "id" | "createdAt" | "updatedAt">>;
type UserResponse = Omit<User, "password">;
type UserListItem = Pick<User, "id" | "name" | "email">;

Example 2: Form state

interface FormField<T> {
    value: T;
    error: string | null;
    touched: boolean;
}

type FormState<T> = {
    [K in keyof T]: FormField<T[K]>;
};

interface LoginData {
    email: string;
    password: string;
}

type LoginFormState = FormState<LoginData>;

const form: LoginFormState = {
    email: { value: "", error: null, touched: false },
    password: { value: "", error: null, touched: false }
};

Example 3: App state slices

interface AppState {
    user: User | null;
    posts: Post[];
    loading: boolean;
    error: string | null;
}

type LoadingState = Pick<AppState, "loading">;
type ErrorState = Pick<AppState, "error">;
type DataState = Omit<AppState, "loading" | "error">;

Summary

Takeaways

  1. Partial: all properties optional
  2. Required: all properties required
  3. Readonly: all properties read-only
  4. Pick: select properties
  5. Omit: remove properties
  6. Record: key–value object types
  7. Exclude / Extract: filter unions
  8. ReturnType: function return type
  9. Parameters: function parameter tuple type

Utility types at a glance

TypeUse caseExample
PartialOptional update payloadsPATCH bodies
RequiredEnsure completenessValidated records
PickSubsetsList rows, previews
OmitStrip secretsResponses without password
RecordFixed key setsRoles, error catalogs

Next steps