SHIN STORYSHIN STORY
홈포스트C#TypeScriptNext.jsNode.js시리즈
</>SHIN STORY

sdf

탐색

  • 홈
  • 모든 포스트
  • 시리즈
  • 검색

카테고리

  • C#
  • TypeScript
  • Next.js
  • Node.js
  • 알고리즘
  • 개발 도구

© 2025 Shin Blog. All rights reserved.

GitHubRSS
목록으로
TypeScript#TypeScript

TypeScript 팁 #4 — 제네릭 제약 조건과 infer 활용

SHIN

2025년 4월 21일2분 읽기0
📚

TypeScript 실전 팁 10선

10편
  1. 5TypeScript 팁 #5 — 타입 가드로 런타임 타입 좁히기
  2. 6TypeScript 팁 #6 — 매핑 타입으로 타입 변환하기
  3. 10TypeScript 팁 #1 — strict 모드를 반드시 켜야 하는 이유
  4. 10TypeScript 팁 #2 — 유틸리티 타입 완전 정복
  5. 10TypeScript 팁 #3 — 판별 유니온으로 타입 안전한 상태 관리
  6. TypeScript 팁 #4 — 제네릭 제약 조건과 infer 활용현재
  7. 10TypeScript 팁 #7 — 템플릿 리터럴 타입으로 문자열 타입 조작
  8. 10TypeScript 팁 #8 — 선언 병합과 모듈 보강으로 타입 확장하기
  9. 10TypeScript 팁 #9 — 타입 안전한 에러 처리 패턴
  10. 10TypeScript 팁 #10 — 타입 성능 최적화와 실무 패턴

TypeScript 팁 #4 — 제네릭 제약 조건과 infer 활용

제네릭 제약 조건 (Generic Constraints)

T extends Something으로 제네릭 타입이 가져야 할 최소 구조를 지정합니다.

CODE
// ❌ 제약 없음 — T에 .length가 있다는 보장 없음
function longest<T>(a: T, b: T): T {
  return a.length >= b.length ? a : b  // 컴파일 오류
}

// ✅ { length: number }를 가진 타입으로 제약
function longest<T extends { length: number }>(a: T, b: T): T {
  return a.length >= b.length ? a : b
}

longest('abc', 'de')        // ✅ string (length 있음)
longest([1, 2], [3])        // ✅ number[] (length 있음)
longest({ length: 3 }, { length: 1 }) // ✅
longest(10, 20)             // ❌ number는 length 없음

keyof + 제네릭 — 타입 안전한 프로퍼티 접근

CODE
// K는 반드시 T의 키여야 함
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

const user = { id: 1, name: '홍길동', age: 30 }

const name = getProperty(user, 'name')  // ✅ 반환 타입: string
const id   = getProperty(user, 'id')    // ✅ 반환 타입: number
getProperty(user, 'email')              // ❌ 'email'은 키가 아님

조건부 타입 (Conditional Types)

CODE
// T가 string이면 'text', 아니면 'other'
type TypeName<T> = T extends string ? 'text' : 'other'

type A = TypeName<string>  // 'text'
type B = TypeName<number>  // 'other'

// 배열 요소 타입 추출
type ElementType<T> = T extends (infer U)[] ? U : never

type E1 = ElementType<string[]>   // string
type E2 = ElementType<number[][]> // number[]
type E3 = ElementType<string>     // never

infer — 타입 내부에서 추출

CODE
// Promise 내부 타입 추출
type Unwrap<T> = T extends Promise<infer U> ? U : T

type A = Unwrap<Promise<string>>  // string
type B = Unwrap<Promise<number[]>> // number[]
type C = Unwrap<string>           // string (Promise 아님)

// 함수 첫 번째 파라미터 타입 추출
type FirstParam<T extends (...args: any) => any> =
  T extends (first: infer F, ...rest: any) => any ? F : never

type F1 = FirstParam<(a: string, b: number) => void>  // string
type F2 = FirstParam<(x: boolean) => void>            // boolean

실전: 깊은 중첩 Readonly

CODE
type DeepReadonly<T> = T extends (infer U)[]
  ? ReadonlyArray<DeepReadonly<U>>
  : T extends object
    ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
    : T

interface Config {
  server: {
    host: string
    ports: number[]
  }
}

const cfg: DeepReadonly<Config> = {
  server: { host: 'localhost', ports: [3000, 8080] }
}

cfg.server.host = 'prod'  // ❌ 읽기 전용
cfg.server.ports.push(9000)  // ❌ ReadonlyArray

실전: 객체 키로 함수 매핑

CODE
// API 핸들러 맵 타입
type HandlerMap<T extends Record<string, any>> = {
  [K in keyof T]: (payload: T[K]) => void
}

interface Events {
  userCreated: { id: number; name: string }
  userDeleted: { id: number }
  postPublished: { postId: string; title: string }
}

const handlers: HandlerMap<Events> = {
  userCreated:   ({ id, name }) => console.log(id, name),  // ✅ 타입 추론
  userDeleted:   ({ id }) => console.log(id),
  postPublished: ({ postId, title }) => console.log(postId, title),
}

제네릭 제약과 infer를 조합하면 라이브러리 수준의 재사용 가능한 타입 유틸리티를 직접 만들 수 있습니다.

공유
S

SHIN

.NET 개발자입니다

GitHub
TypeScript 팁 #3 — 판별 유니온으로 타입 안전한 상태 관리

이전 포스트

TypeScript 팁 #3 — 판별 유니온으로 타입 안전한 상태 관리

다음 포스트

TypeScript 팁 #5 — 타입 가드로 런타임 타입 좁히기

같은 카테고리 포스트

TypeScript 팁 #10 — 타입 성능 최적화와 실무 패턴

TypeScript 팁 #10 — 타입 성능 최적화와 실무 패턴

2025년 5월 3일· 3분
TypeScript 팁 #9 — 타입 안전한 에러 처리 패턴

TypeScript 팁 #9 — 타입 안전한 에러 처리 패턴

2025년 5월 1일· 2분
TypeScript 팁 #8 — 선언 병합과 모듈 보강으로 타입 확장하기

TypeScript 팁 #8 — 선언 병합과 모듈 보강으로 타입 확장하기

2025년 4월 29일· 2분

댓글