[TypeScript] 고급 타입 활용법: 조건부 타입, 제네릭 심화와 Exclude/Extract 활용

2025. 4. 1. 15:45·Development/React

📘 2025.03.22 TypeScript 정리

@2025.03.29 곽지혜


📑 주제

  • 조건부 타입 / 맵드 타입
  • 제네릭 심화 (제약 조건, 기본값, 다중 파라미터)
  • 타입 조작: Exclude, Extract, NonNullable, Partial 등

📂 파일 구조 (예제 위치)

src/
├── 01_conditional_types.ts
├── 02_mapped_types.ts
├── 03_generics_advanced.ts
├── 04_utility_types.ts
├── 05_examples_comparison.ts

      •  

1. 조건부 타입 (Conditional Types)

📁 예제: 01_conditional_types.ts

T extends U ? X : Y 

조건에 따라 타입을 분기할 수 있습니다.

T가 U를 확장(extends) 하면 타입은 X, 아니면 Y가 됩니다.

  • 조건부 타입 문법에는 extends 키워드와 물음표 ? 기호를 사용합니다. (삼항 연산자)
  • 자바스크립트의 삼항 연산자는 변수의 값을 조건에 따라 결정한다면, 타입스크립트의 조건부 타입은 값 대신 타입을 조건에 따라 결정합니다.
  • 조건부 타입도 유니온처럼 하나의 타입입니다.
  • 조건부 타입을 중첩해서 쓰면 가독성이 매우 안좋아지므로 고려해야합니다.

2. 맵드 타입 (Mapped Types)

📁 예제: 02_mapped_types.ts

객체의 키를 순회하며 타입을 재구성할 수 있습니다.
객체 타입을 변형하는데 사용되며, keyof, in 와 함께 사용됩니다.

  • keyof: 객체 형태의 타입을, 따로 속성들만 뽑아 모아 유니온 타입으로 만들어주는 연산자
  • in: 객체에 특성 속성이 존재하는지 확인하는데 사용
// 단순한 타입: 두개의 필드를 가진 Person 객체
type Person = { name: string; age: number };

// 변형된 타입
type OptionalPerson = {
  [K in keyof Person]?: Person[K];
};

// 결과적으로 생성된 타입
type OptionalPerson = {
  name?: string;
  age?: number;
};

// 즉, Person의 모든 필드를 Optional한 버전으로 생성
const someone: OptionalPerson = {};
const another: OptionalPerson = { name: "Tom" };
  1. keyof Person: Person 타입의 키를 유니온으로 추출
  2. [K in keyof Person]: name, age를 K에 넣어 반복, 즉 Ksms name->age 두 번 순회
  3. ?: 와 Person[K]: K는 name 또는 age니까 Person[K]는 각각 strng, number가 됨
    ?:는 해당 필드를 optional로 만들어줍니다.

3. 제네릭 고급 문법

제네릭 타입은 함수나 클래스의 선언 시점이 아닌, 사용 시점에 타입을 선언할 수 있는 방법을 제공합니다.

  1. 타입이 고정되는 것을 방지하고 재사용 가능한 요소를 선언할 수 있다.
  2. 타입 검사를 컴파일 시간에 진행함으로써 타입 안정성을 보장.
  3. 캐스팅 관련 코드를 제거할 수 있다.
  4. 제네릭 로직을 이용해 타입을 다르게 받을 수 있는 재사용 코드를 만들 수 있다.

꺾쇠 <> 기호를 변수명, 함수명 앞에다 쓰면 '타입 단언' 이 되게 됩니다.
따라서 제네릭을 구현하려면 변수명, 함수명 뒤에다가 꺾쇠 괄호를 적어주어야 합니다.
제네릭명은 꼭 T가 아니어도 됩니다.

제네릭에서 T를 많이 사용하는 데에는 관습적인 이유와 의미적인 이유가 있습니다.

  • T는 "Type"의 약자로 많이 쓰이기 때문에, 타입을 나타내는 변수명으로 T를 지정하는 게 일반적입니다.
  • 타입스크립트에서 타입을 유연하게 다룰 때 제네릭을 사용하는데, 그때 T, U, V, K 등이 자주 사용됩니다.

🔍 왜 T를 사용할까?

1. 의미적인 이유 (Type의 약자)

제네릭은 기본적으로 타입을 유연하게 다루기 위해 사용됩니다.
그래서 타입을 표현하는 변수명으로 T를 쓰는 것이 직관적입니다.

type ApiResponse<T> = {
  data: T;
  success: boolean;
};
  • 위 코드에서 T는 타입을 받는 제네릭 변수로, "타입"이라는 의미를 명확히 전달합니다.

2. 관습적인 이유

T는 TypeScript, Java 등의 제네릭 예제에서도 널리 사용되기 때문에
많은 개발자들이 자연스럽게 T를 사용하게 됩니다.

// T: 첫 번째 타입, U: 두 번째 타입
function merge<T, U>(a: T, b: U): T & U {
  return { ...a, ...b };
}
  • T와 U는 각각 a, b의 타입을 표현하며, 결과는 T & U 타입이 됩니다.

3. 제네릭 이름은 자유롭게 설정 가능

T는 관습일 뿐이며, 다음과 같이 의미 있는 이름으로 대체할 수도 있습니다.

type ApiResponse<ResponseType> = {
  data: ResponseType;
  success: boolean;
};

 


🔑 결론

  • T는 "Type"을 의미하고, 제네릭 변수로 가장 일반적으로 사용
  • 하지만 T, U 외에도 다음과 같은 표현이 자주 쓰입니다:
이름 의미
K Key (속성명)
V Value (값)
R Return (리턴값)

3.1 제약 조건 (Constraint)

📁 예제: 03_generics_advanced.ts

extends를 사용하여 제네릭이 특정 타입을 따라야 함을 명시합니다.

function getLength<T extends { length: number }>(item: T): number {
  return item.length;
}

3.2 기본값 (Default)

제네릭(Generic) 타입은 타입 인자를 명시하지 않으면 오류가 나는 경우가 있는데,
기본값(Default Type Parameter)을 지정하면 타입 인자를 생략해도 기본값이 자동으로 적용됩니다.

기본값은 T = 기본값처럼 제네릭 이름 오른쪽에 =를 붙여 설정합니다.

type ApiResponse<T = any> = {
  data: T;
  success: boolean;
};

const res: ApiResponse = { data: "hello", success: true }; // T는 any
  • 이 코드는 제네릭 타입 T를 받되, 타입 인자를 생략했을 경우 기본값으로 any가 설정됩니다.
  • 필요할 때 명시하고, 아닐 때는 기본값으로 동작하는 유연한 구조를 만들 수 있습니다.

3.3 다중 타입 파라미터

제네릭을 여러 개 받을 수 있습니다.
타입을 파라미터처럼 받아서 유연하게 코드를 작성할 수 있게 합니다.

function merge<T, U>(a: T, b: U): T & U {
  return { ...a, ...b };
}
// T, U라는 두 개의 타입 파라미터를 받아서 두 객체 a와 b를 합친 결과를 T & U라는 타입으로 리턴

const result = merge({ name: "Tom" }, { age: 30 });
// 타입: { name: string; age: number }
  • T는 { name: string }
  • U는 { age: number }
  • T & U는 { name: string; age: number }
    → 즉, 두 객체를 합친 타입이 결과로 추론됩니다.

💡 왜 제네릭을 두 개 이상 받는가?

단일 제네릭만으로는 두 인자의 타입이 서로 다른 상황을 표현하기 어려습니다.

❌ 단일 제네릭으로 하면 이런 문제 발생
function merge<T>(a: T, b: T): T {
  return { ...a, ...b };
}

const m = merge({ name: "Tom" }, { age: 30 }); // ❌ 에러
  • 이 경우 a, b가 같은 타입이어야 하기 때문에 에러가 발생합니다.

제네릭은 타입을 "파라미터처럼" 받아서 유연하게 코드를 작성할 수 있게 해주는 도구입니다.
그리고 **여러 타입이 동시에 필요하다면, 제네릭도 여러 개 받을 수 있습니다.

요약: 다중 타입 파라미터 언제 쓰나요?

상황 제네릭 개수 예시
같은 타입을 여러 곳에 반복 사용 1개 function identity<T>(value: T): T
서로 다른 타입의 인자를 다뤄야 할 때 2개 이상 function merge<T, U>(a: T, b: U): T & U
3개 이상도 가능 필요에 따라 function zip<T, U, R>() 등

4. 타입 유틸리티 (Utility Types)

📁 예제: 04_utility_types.ts

  1. Exclude<T, U> : T에서 U를 제거한 타입을 반환
  2. Extract<T, U> : T와 U의 공통 타입만 추출
  3. NonNullable<T> : null과 undefined를 제외
type T1 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T2 = Extract<"a" | "b", "a" | "z">; // "a"
type T3 = NonNullable<string | null>;   // string

기타 유틸리티:

  • Partial, Required, Readonly, Record<K, T>

5. 실전 예제 비교

📁 예제: 05_examples_comparison.ts

초보자 스타일 vs 제네릭 + 유틸리티 적용 예시 비교:

// 초보자
function fetchUser(): { name: string } { return { name: "Tom" }; }

// 개선
function fetchData<T>(url: string): Promise<T> {
  return fetch(url).then(res => res.json());
}

6. 요약 및 정리

기능 초보 코드 고급 코드
중복 제거 함수/타입 반복 제네릭 하나로 처리
타입 안정성 any 남용 위험 제약조건 + 유틸리티 활용
유지보수 새로운 상황마다 새 타입 기존 타입 기반으로 파생
자동완성 X O (IDE 도움)

🔥 난이도 & 사용 빈도 분류

항목 설명 사용 빈도 난이도
조건부 타입 삼항 연산자 형태 타입 분기 ⭐⭐⭐ 🔥🔥🔥
맵드 타입 객체 키 기반 타입 생성 ⭐⭐⭐⭐ 🔥🔥
제네릭 기본 기본 사용 ⭐⭐⭐⭐ 🔥
유틸리티 타입 Partial, Omit 등 ⭐⭐⭐⭐ 🔥

출처

https://inpa.tistory.com/entry/TS-📘-타입스크립트-조건부-타입-완벽-이해하기

'Development > React' 카테고리의 다른 글

[React] React에서 key 중복으로 발생한 렌더링 이슈 (Reconciliation 사례)  (1) 2025.05.27
[React] useId()를 사용하다가 삽질한 기록  (4) 2025.05.25
[React] 상태 (State)  (0) 2024.02.28
[React] JSX 사용하기  (0) 2024.02.28
[React] Datepicker 사용하기  (0) 2023.05.02
'Development/React' 카테고리의 다른 글
  • [React] React에서 key 중복으로 발생한 렌더링 이슈 (Reconciliation 사례)
  • [React] useId()를 사용하다가 삽질한 기록
  • [React] 상태 (State)
  • [React] JSX 사용하기
곽진돔
곽진돔
Developer
  • 곽진돔
    echo "곽박한 세상";
    곽진돔
  • 전체
    오늘
    어제
    • 분류 전체보기 (198) N
      • Development (65) N
        • Linux (13)
        • k8s (3)
        • Docker (5)
        • AWS (1)
        • PHP (35)
        • Python (21)
        • Java (1)
        • SpringBoot (4)
        • JavaScript (1)
        • React (10)
        • MySql (19)
        • MongoDB (1)
      • Daily (5)
      • Study (7)
        • TIL (2)
        • license (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 글쓰기
    • 설정
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    Python
    SQL
    Shell
    Linux
    chromedriver
    인코딩
    윈도우
    error
    ssh
    정규표현식
    스프링부트
    CentOS7
    Selenium
    Mac
    db
    MySQL
    Java
    리액트
    CentOS
    docker
    react
    리눅스
    php
    springboot
    크롤링
    JavaScript
    date
    UTF8
    nodejs
    IP
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
곽진돔
[TypeScript] 고급 타입 활용법: 조건부 타입, 제네릭 심화와 Exclude/Extract 활용
상단으로

티스토리툴바