본문 바로가기
C.W.K.
Stream
Lesson 01 of 06 · published

type 별칭: 복잡한 모양에 이름 붙이기

~10 min · types-interfaces, type-alias, naming

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"타입 별칭은 긴 표기를 줄이는 도구이기 전에, 도메인의 개념을 코드 안에 세우는 이름이야."

어떤 타입에도 이름을 붙여

type은 객체뿐 아니라 원시 타입, 리터럴 유니언, 튜플, 함수 시그니처, 조건부 타입 등 어떤 타입 표현에도 이름을 붙일 수 있어. type UserId = number처럼 단순하게 시작할 수도 있고, 여러 변형을 합친 복잡한 유니언을 하나의 도메인 이름으로 묶을 수도 있지.

객체 모양도 자연스럽게 표현해

type User = { id: number; name: string }처럼 객체의 속성을 적을 수 있어. 선택 속성에는 ?, 읽기 전용 속성에는 readonly를 붙이고, 다른 타입과 &로 결합할 수 있어. 평범한 객체만 놓고 보면 interface와 겹치는 부분이 크다.

type이 확실히 필요한 자리는 유니언이야. type Result = Success | Failure처럼 여러 가능성을 하나의 이름 아래 묶는 표현은 interface 하나로 만들 수 없어. 조건부 타입이나 매핑된 타입 같은 계산에도 type 별칭이 자연스러운 그릇이 돼.

이름은 의도를 압축해

string이라고만 적으면 값의 기술적 모양만 보이지만 EmailAddress, ConversationId라고 적으면 쓰임이 드러나. 구조적으로 같더라도 이름이 읽는 사람에게 도메인 문맥을 건네. 다만 의미 없는 별칭을 층층이 쌓으면 정의를 따라가느라 오히려 흐려지니, 개념이나 반복을 실제로 줄이는 곳에만 붙여.

재귀와 파생 타입에도 이름을 붙여

type Tree<T> = { value: T; children: Tree<T>[] }처럼 별칭은 자기 자신을 참조하는 재귀 구조도 표현해. Awaited<Promise<string>>처럼 다른 타입을 계산한 결과에도 이름을 붙일 수 있어.

값의 모양보다 개념이 먼저 보이게 이름 붙여. 유니언과 계산된 타입에는 type을 쓰고, 단순 반복을 줄이기 위해서는 원본에서 파생해.

피파의 고백

실제 코드에서는 BrainName 같은 리터럴 유니언과 ChatMessage = Awaited<ReturnType<typeof fetchMessage>> 같은 파생 타입이 긴 설명을 짧고 검증 가능한 이름으로 바꿔 줘.

Code

`type` 이 유일한 선택인 곳·typescript
// `type` 이 유일한 선택인 자리들.

// Union
type Color = 'red' | 'green' | 'blue';

// Intersection
type WithTimestamp<T> = T & { createdAt: Date };

// 함수 타입
type Comparator<T> = (a: T, b: T) => number;

// 다른 타입에서 derived
type UserPartial = Partial<User>;
type UserKeys = keyof User;
type UserEmail = User['email'];

// 재귀
type JSONValue =
  | string
  | number
  | boolean
  | null
  | JSONValue[]
  | { [key: string]: JSONValue };

// Conditional
type IsString<T> = T extends string ? true : false;
type A = IsString<'hi'>;  // true
type B = IsString<42>;    // false
겹침, 그다음 차이·typescript
// Interface 와의 overlap, 그다음 차이.

// 일반 object 모양 — 둘 다 작동, 스타일로 선택.
type UserA = {
  id: number;
  name: string;
};

interface UserB {
  id: number;
  name: string;
}

// 둘 다 extend 가능.
type UserAWithEmail = UserA & { email: string };
interface UserBWithEmail extends UserB { email: string }

// 이제 차이: `type` 만 union 가능.
type Status = 'idle' | 'loading' | 'done';   // ✅
// interface Status = 'idle' | 'loading' | 'done';  // ❌ 문법 에러

// 그리고 `interface` 만 두 번 선언되고 merge 가능.
interface Box { width: number }
interface Box { height: number }   // height 추가 — 같은 Box 에 merge
// type Box = { width: number };
// type Box = { height: number };   // ❌ Duplicate identifier

External links

Exercise

id: number, body: string, replies: Comment[]를 가진 재귀적인 Comment 타입을 만들고, 루트와 모든 하위 댓글을 세는 countAll(c: Comment): number를 작성해. 같은 모양을 interface로도 표현해 보고 무엇이 달라지는지 확인해.
Hint
재귀 참조는 type과 interface 모두에서 작동해. Comment 모양을 다른 종류와 유니언으로 묶고 싶다면 차이가 드러나. 예를 들어 type Node = Comment | Reaction 같은 유니언은 type으로 만들어야 해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고
💛 by 똘이warm

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.