C.W.K.
Stream
Lesson 02 of 06 · published

`interface`: 이름이 중요할 때

~10 min · types-interfaces, interface, extends, declaration-merging

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"`interface` 는 `interface` 라는 단어를 사랑하는 사람을 위한 `type`. 거의."

`interface` 가 잘하는 거

`interface` 는 이름 붙은 object 모양 선언. interface User { id: number; name: string } 는 두 멤버 가진 `User` 타입 만들어. Consumer 관점에선 같은 모양의 `type` alias 와 거의 동일.

interface 가 이름값 하는 자리들:

1. `extends` 가 nominal-feel 계층에 자연스럽게 읽혀. interface AdminUser extends User { permissions: string[] } 가 class 계층처럼 의도 전달. 동등한 type 은 intersection (type AdminUser = User & { permissions: string[] }) 써 — 더 강력하지만 덜 명백하게 읽혀.

2. Declaration merging. 같은 scope 의 두 `interface User` 선언이 하나로 merge. 이게 library 가 global 타입 augment 하는 법 (TypeScript 자체가 `lib.dom.d.ts` 에 이렇게 함). `Window`, `Express.Request`, 또는 어떤 3rd-party interface 에든 커스텀 property 추가하는 법.

3. Class implement. Class 가 class MyUser implements User 선언 가능. `type` 도 `interface` 도 여기 작동, 근데 `implements` 가 `interface` 와 깨끗하게 읽히고 많은 developer 가 가진 Java/C# 근육 기억과 match.

Declaration merging — killer 기능

이게 `interface` 의 고유 힘. 같은 이름을 한 번 이상 선언 가능, 선언들 merge:

interface Box { width: number }
interface Box { height: number }
// Box 가 이제 width 와 height 둘 다 가짐

처음 보면 이상해. Library 타입 augment 하거나 global 확장할 때 use case 명백해. declare global { interface Window { myApp: MyAppGlobal } } 가 원래 `Window` 선언 안 가져도 `window` 에 `myApp` 추가. Type alias 못 함 — 중복 이름에 에러.

Interface 가 고생하는 곳

Object 모양 아닌 거: union, non-object 의 intersection, mapped 타입, conditional 타입, 단독 함수 타입. 이 모든 거에 type alias 가 유일한 선택. 함수-shaped interface 의 call-signature 문법 (interface F { (x: number): string }) 은 기술적으로 유효하지만 거의 안 보임.

실제 규칙: 이름 붙은, extensible, object-shaped 타입 원할 때 `interface` 써 — 특히 public API surface 와 다른 library 가 augment 할 수 있는 타입. 그 외엔 `type` 써. 둘이 경쟁 아냐; overlap 있지만 구별되는 sweet spot 가진 tool.

피파의 고백

cwkPippa 의 backend 타입은 Python 쪽 Pydantic 에서 옴; frontend `interface` 선언이 손으로 mirror (Pippa Coop System 에 codegen 으로 생성하는 future task 있어). Request/response 모양엔 `interface` 써 — 계층적이고 contract 처럼 읽혀, 정확히 `interface` 가 가장 잘하는 거. 다른 모든 거 (union, derived 타입, 함수 signature) 는 `type`.

Code

계층적, 이름 붙은 모양엔 interface·typescript
// interface — 이름 붙은, extensible object 모양.

interface User {
  id: number;
  name: string;
  email: string | null;
}

// Extends — 계층 만드는 자연스러운 방법.
interface AdminUser extends User {
  permissions: string[];
  canEditAll: boolean;
}

// 다중 extends.
interface Timestamped {
  createdAt: Date;
  updatedAt: Date;
}

interface AuditedAdmin extends AdminUser, Timestamped {
  auditTrail: string[];
}

// Interface 구현하는 class.
class MyUser implements User {
  constructor(
    public id: number,
    public name: string,
    public email: string | null,
  ) {}
}
Declaration merging — global 과 library 타입 augment·typescript
// Declaration merging — `type` 이 못 하는 유일한 것.

interface Box {
  width: number;
}
interface Box {
  height: number;
}

const b: Box = { width: 100, height: 200 };       // 두 필드 다 필수

// 실제 사용: global augment.
declare global {
  interface Window {
    myApp: {
      version: string;
      pippa: { hello: () => void };
    };
  }
}

window.myApp.pippa.hello();                       // ✅ 이제 타입 잡힘

// 그리고 Express.js 패턴: Request 확장.
// (프로젝트 root 의 .d.ts 파일에서.)
declare namespace Express {
  interface Request {
    user?: { id: number; role: 'admin' | 'user' };
  }
}

External links

Exercise

interface Vehicle { wheels: number; brand: string } 만들어. 그다음 interface Car extends Vehicle { doors: number } 만들어. 이제 별도 파일 (또는 아래) 에서 interface Car { color: string } 선언 — merge 돼? Car 의 전체 모양 이제 뭐? 그다음 같은 트릭을 type alias 로 해보고 뭐 일어나는지 봐.
Hint
Declaration merging 이 Car 가 4개 필드 다 요구하게 만들어. Type alias 면 두 번째 선언이 duplicate-identifier 에러. 그게 차이, 시연됨.

Progress

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

댓글 0

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

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