프로그래밍풀스택 개발자

TypeScript에서 Exclude는 어떻게 작동하며, union 타입과 함께 조작할 때 언제 사용하는 것이 좋은가요? 이 유틸리티 타입을 사용할 때의 주의사항은 무엇인가요?

Hintsage AI 어시스턴트로 면접 통과

답변.

Exclude<T, U>는 TypeScript에서 union 타입에서 특정 값을 제외하기 위해 사용되는 유틸리티 타입입니다.

질문의 배경

TypeScript에서는 타입을 다른 타입에서 편리하게 뺄 수 있는 방법이 없었습니다. 제너릭 API를 만들거나 리팩토링할 때 자주 "남은" 타입, 즉 금지된 값을 제외한 모든 타입을 얻어야 했습니다. 수동으로 union을 조작하는 대신 비슷한 인터페이스를 여러 개 유지해야 했습니다.

문제

예를 들어, 'A | B | C'라는 타입이 있을 때 B를 제외한 타입을 얻고 싶다면 이 경우가 많습니다. 이는 복잡한 함수의 입력 매개변수를 구성하거나 허용된 값을 필터링하며 동적으로 타입을 생성할 때 필요한 경우가 많습니다.

해결책

Exclude는 이 문제를 해결합니다. 간단한 시그니쳐는 다음과 같습니다:

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

이는 T에서 U를 제외한 타입을 반환합니다.

예제:

type Status = 'draft' | 'published' | 'removed'; type UserVisibleStatus = Exclude<Status, 'removed'>; const visible: UserVisibleStatus = 'draft'; // OK

주요 특징:

  • union에서 부분을 "뺀" 동적 타입을 생성할 수 있습니다.
  • 리팩토링을 간소화합니다 — 기본 타입이 변경될 때 모든 파생 타입이 자동으로 업데이트됩니다.
  • switch 케이스 필터링 또는 객체의 키 필터링을 위해 사용될 수 있습니다.

함정 질문.

Exclude를 일반 타입에 사용할 수 있나요, 아니면 union에서만 사용할 수 있나요?

만약 T가 union 타입이 아니더라도 U에 포함되면, Exclude는 여전히 작동하지만 결과는 never 또는 T가 될 수 있습니다. 이는 항상 직관적이지 않습니다.

Exclude<'a', 'a'> // 결과: never Exclude<'a', 'b'> // 결과: 'a'

Exclude가 객체 구조에서 타입의 모든 언급을 제거하나요?

아니요, Exclude는 타입의 중첩된 필드를 재귀적으로 통과하지 않으며, union의 최상위 레벨에서만 제외합니다.

Exclude는 인터페이스 및 객체 타입과 함께 어떻게 작동하나요?

그것은 전체 타입을 비교하며 개별 속성을 비교하지 않습니다. 따라서 다수의 인터페이스의 union에서 Exclude를 사용하면 U와 완전히 일치하는 것만 제거됩니다.

interface A { x: number }; interface B { y: string }; // Exclude<A|B, B>는 A를 반환합니다. (B가 완전히 일치함)

일반적인 오류 및 안티 패턴

  • 중첩되거나 부분적인 일치에 대해 Exclude를 사용하려는 시도
  • union의 옵션이 아닌 인터페이스 속성을 "제거"하기 위해 사용하는 것
  • 타입이 완전히 일치할 때 never 타입을 얻을 수 있는 가능성을 무시하는 것

실제 사례

부정적인 경우

Exclude<UserRoles, 'admin'>를 통해 사용자의 역할을 검증했지만, Exclude가 중첩 구조에 적용되지 않음을 잊어버려 'admin:sub' 권한이 제외되지 않았습니다.

장점:

  • 역할 타입을 간단하게 생성할 수 있습니다.

단점:

  • 중첩된 또는 유사한 타입과 함께 예상치 못한 동작이 발생할 수 있으며, 중요한 역할이 누락되었습니다.

긍정적인 경우

공용 API를 Exclude<Action, 'delete'>를 사용하여 위험한 작업을 제외함으로써 제한하는 경우입니다.

장점:

  • 타입 수준에서 안전성을 보장하여 금지된 작업을 호출할 수 없습니다.

단점:

  • 기본 타입 목록을 최신 상태로 유지해야 합니다.