타입스크립트의 고급 유틸리티 타입과 조건부 타입 활용

타입스크립트는 복잡한 타입 정의를 간결하게 만들고, 기존 타입을 재활용하여 새로운 타입을 생성할 수 있도록 다양한 내장 유틸리티 타입과 조건부 타입을 제공합니다. 이를 통해 코드의 재사용성을 높이고 타입 안정성을 강화할 수 있습니다.

유틸리티 타입

타입스크립트 라이브러리에 포함된 주요 유틸리티 타입들을 살펴보겠습니다. 다음 ProductInfo 인터페이스를 예시로 사용하겠습니다.

interface ProductInfo {
    id: number;
    name: string;
    price: number;
    description?: string; // 선택적 필드
}

Partial<T>

Partial<T>는 타입 T의 모든 속성을 선택적(optional)으로 만들어 새로운 타입을 생성합니다. 즉, 기존 타입의 모든 속성이 ? (선택적) 수식어를 갖게 됩니다.

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

// 예시: ProductInfo의 모든 필드를 선택적으로 만듭니다.
type OptionalProduct = Partial<ProductInfo>;
// OptionalProduct 타입은 다음과 같습니다:
// {
//   id?: number;
//   name?: string;
//   price?: number;
//   description?: string;
// }

Required<T>

Required<T>는 타입 T의 모든 속성을 필수적(required)으로 만들어 새로운 타입을 생성합니다. 이는 기존 타입의 선택적 속성에서 ? 수식어를 제거하여 필수로 만듭니다.

type Required<T> = {
    [P in keyof T]-?: T[P]; // '-' 접두사는 선택적 속성을 필수로 만듭니다.
};

// 예시: ProductInfo의 모든 필드를 필수로 만듭니다.
type MandatoryProduct = Required<ProductInfo>;
// MandatoryProduct 타입은 다음과 같습니다:
// {
//   id: number;
//   name: string;
//   price: number;
//   description: string;
// }

Readonly<T>

Readonly<T>는 타입 T의 모든 속성을 읽기 전용(readonly)으로 만들어 새로운 타입을 생성합니다. 이렇게 생성된 타입의 객체는 생성 후 속성 값을 변경할 수 없습니다.

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

// 예시: ProductInfo의 모든 필드를 읽기 전용으로 만듭니다.
type ImmutableProduct = Readonly<ProductInfo>;
// ImmutableProduct 타입은 다음과 같습니다:
// {
//   readonly id: number;
//   readonly name: string;
//   readonly price: number;
//   readonly description?: string;
// }

Pick<T, K>

Pick<T, K>는 기존 타입 T에서 특정 속성 K만을 선택하여 새로운 타입을 생성합니다. KT의 속성 키들의 유니온 타입이어야 합니다.

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

// 예시: ProductInfo에서 'name'과 'price' 속성만 선택합니다.
type ProductSummary = Pick<ProductInfo, "name" | "price">;
// ProductSummary 타입은 다음과 같습니다:
// {
//   name: string;
//   price: number;
// }

Record<K, T>

Record<K, T>K 타입의 속성 키를 가지고, T 타입의 값을 가지는 객체 타입을 생성합니다. K는 문자열, 숫자, 또는 심볼 리터럴의 유니온 타입이 될 수 있습니다.

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

// 예시: 요일(문자열 리터럴 유니온)을 키로 가지고, 작업 상태(불리언)를 값으로 가집니다.
type DailyTaskStatus = Record<'Monday' | 'Tuesday' | 'Wednesday', boolean>;
// DailyTaskStatus 타입은 다음과 같습니다:
// {
//   Monday: boolean;
//   Tuesday: boolean;
//   Wednesday: boolean;
// }

조건부 타입

조건부 타입은 특정 타입이 다른 타입에 할당 가능한지 여부에 따라 다른 타입을 선택하는 메커니즘을 제공합니다. 이는 유니온 타입에서 특정 멤버를 필터링하거나 함수의 반환 타입, 클래스의 인스턴스 타입을 추론하는 데 유용하게 사용됩니다.

Exclude<T, U>

Exclude<T, U>는 타입 T에서 타입 U에 할당 가능한 모든 타입을 제외한 새로운 타입을 생성합니다.

type NumericOrString = Exclude<number | string | boolean, boolean>;
// NumericOrString 타입은 number | string 입니다.

type ValidIdTypes = Exclude<string | number | symbol, symbol>;
// ValidIdTypes 타입은 string | number 입니다.

Extract<T, U>

Extract<T, U>는 타입 T에서 타입 U에 할당 가능한 모든 타입만을 추출하여 새로운 타입을 생성합니다.

type OnlyFunctions = Extract<string | number | (() => void), Function>;
// OnlyFunctions 타입은 (() => void) 입니다.

type AllowedUserRoles = Extract<'admin' | 'user' | 'guest', 'admin' | 'user'>;
// AllowedUserRoles 타입은 'admin' | 'user' 입니다.

NonNullable<T>

NonNullable<T>는 타입 T에서 nullundefined를 제외한 새로운 타입을 생성합니다.

type CleanValue = NonNullable<string | number | null | undefined>;
// CleanValue 타입은 string | number 입니다.

type MessageContent = NonNullable<string | null>;
// MessageContent 타입은 string 입니다.

ReturnType<T>

ReturnType<T>는 함수 타입 T의 반환 타입을 추론하여 새로운 타입을 생성합니다.

// 예시 함수 정의
function processData(input: string, count: number) {
    return {
        processedInput: input.toUpperCase(),
        finalCount: count * 2,
        timestamp: Date.now()
    };
}

// processData 함수의 반환 타입을 추론합니다.
type ProcessResult = ReturnType<typeof processData>;
// ProcessResult 타입은 다음과 같습니다:
// {
//   processedInput: string;
//   finalCount: number;
//   timestamp: number;
// }

// 화살표 함수의 반환 타입 추론
const getMessage = (name: string) => `Hello, ${name}!`;
type MessageType = ReturnType<typeof getMessage>; // string

InstanceType<T>

InstanceType<T>는 생성자 함수 타입 T의 인스턴스 타입을 추론하여 새로운 타입을 생성합니다.

// 예시 클래스 정의
class Configuration {
    private settings: Record<string, any>;
    constructor(initialSettings: Record<string, any>) {
        this.settings = initialSettings;
    }
    getSetting(key: string) {
        return this.settings[key];
    }
}

// Configuration 클래스의 인스턴스 타입을 추론합니다.
type ConfigInstance = InstanceType<typeof Configuration>;
// ConfigInstance 타입은 Configuration 클래스의 인스턴스입니다.
// 즉, ConfigInstance는 { settings: Record; getSetting(key: string): any; } 입니다.

태그: TypeScript Utility Types Conditional Types Type Manipulation generics

8월 1일 17:19에 게시됨