Exclude, Extract, NonNullable은 유니온 타입에서 특정 멤버를 걸러내거나 추출 하는 유틸리티 타입입니다.

Exclude<T, U> — 유니온에서 제거

T에서 U에 해당하는 멤버를 제거 합니다.

TYPESCRIPT
type AllColors = 'red' | 'green' | 'blue' | 'yellow';

// 'red'와 'blue'를 제거
type WarmColors = Exclude<AllColors, 'red' | 'blue'>;
// 'green' | 'yellow'

// 타입 종류로 필터링
type Mixed = string | number | boolean | null;
type OnlyPrimitives = Exclude<Mixed, null>;
// string | number | boolean

Exclude의 내부 구현

TYPESCRIPT
// 조건부 타입의 분배 법칙을 이용
type MyExclude<T, U> = T extends U ? never : T;

// 동작 원리 (유니온은 분배됨):
// Exclude<'a' | 'b' | 'c', 'a'>
// = ('a' extends 'a' ? never : 'a') | ('b' extends 'a' ? never : 'b') | ('c' extends 'a' ? never : 'c')
// = never | 'b' | 'c'
// = 'b' | 'c'

Extract<T, U> — 유니온에서 추출

T에서 U에 해당하는 멤버만 추출 합니다. Exclude의 반대입니다.

TYPESCRIPT
type AllColors = 'red' | 'green' | 'blue' | 'yellow';

// 'red'와 'blue'만 추출
type CoolColors = Extract<AllColors, 'red' | 'blue' | 'purple'>;
// 'red' | 'blue' (purple은 원래 없으므로 무시)

// 타입 종류로 추출
type Mixed = string | number | boolean | (() => void);
type OnlyFunctions = Extract<Mixed, Function>;
// () => void

Extract의 내부 구현

TYPESCRIPT
type MyExtract<T, U> = T extends U ? T : never;

NonNullable<T> — null과 undefined 제거

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

// 복합 타입에서도 동작
type MaybeUser = { name: string } | null | undefined;
type DefiniteUser = NonNullable<MaybeUser>;
// { name: string }

NonNullable의 내부 구현

TYPESCRIPT
type MyNonNullable<T> = T & {};
// 또는
type MyNonNullable2<T> = T extends null | undefined ? never : T;

실전 활용 패턴

이벤트 타입 필터링

TYPESCRIPT
type Event =
  | { type: 'click'; x: number; y: number }
  | { type: 'keypress'; key: string }
  | { type: 'scroll'; offset: number };

// 특정 이벤트 타입만 추출
type ClickEvent = Extract<Event, { type: 'click' }>;
// { type: 'click'; x: number; y: number }

type NonClickEvent = Exclude<Event, { type: 'click' }>;
// { type: 'keypress'; key: string } | { type: 'scroll'; offset: number }

객체 키 필터링

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

// string 타입인 키만 추출
type StringKeys = {
  [K in keyof User]: User[K] extends string ? K : never;
}[keyof User];
// 'name' | 'email'

// 그 키들로 Pick
type StringFields = Pick<User, StringKeys>;
// { name: string; email: string }

함수 매개변수 필터링

TYPESCRIPT
// 특정 타입의 메서드만 추출
type MethodNames<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never;
}[keyof T];

class UserService {
  name: string = '';
  age: number = 0;
  greet() { return 'hello'; }
  getAge() { return this.age; }
}

type Methods = MethodNames<UserService>;
// 'greet' | 'getAge'

Exclude vs Omit 차이

공부하다 보니 이 둘을 혼동하는 경우가 많더라고요.

TYPESCRIPT
// Exclude — 유니온 멤버를 제거
type Colors = 'red' | 'green' | 'blue';
type WarmColors = Exclude<Colors, 'blue'>; // 'red' | 'green'

// Omit — 객체 속성을 제거
interface User { name: string; age: number; email: string; }
type WithoutEmail = Omit<User, 'email'>; // { name: string; age: number }
ExcludeOmit
대상유니온 멤버객체 속성
동작유니온에서 매칭되는 멤버 제거객체에서 지정한 키 제거

정리

  • Exclude<T, U>: 유니온에서 U에 해당하는 멤버를 제거한다
  • Extract<T, U>: 유니온에서 U에 해당하는 멤버만 추출한다
  • NonNullable<T>: null과 undefined를 제거한다
  • 세 유틸리티 모두 조건부 타입의 분배 법칙으로 동작한다
  • Exclude는 유니온 필터링, Omit은 객체 속성 제거 — 대상이 다르다
댓글 로딩 중...