"오리처럼 걷고 오리처럼 타이핑하면 — TypeScript 말함, 그래 오리야."
TypeScript 의 가장 구별되는 단일 특징
대부분 정적 type system 은 nominal: 모양 같지만 선언된 이름 다른 두 타입은 호환 안 됨. Java 의 `class Pippa` 와 `class Ttori` 가 필드 같아도 하나가 다른 거 extend 안 하면 서로 assign 못 함. Compiler 가 가족 tree 강제.
TypeScript 는 structural: 두 타입이 모양 일치하면 호환, 이름이나 선언된 lineage 관계없이. type Pippa = { name: string } 와 type Ttori = { name: string } 가 호환 — `Ttori` 기대하는 곳에 `Pippa` 전달 가능. Compiler 는 모양만 신경, label 아냐.
왜 이게 중요
Structural typing 이 TypeScript 가 JavaScript 위에 가볍게 유지되는 법. JavaScript 는 항상 runtime 에 duck-typed — `if (obj.quack) obj.quack()` 가 `obj` 가 어느 class 에서 왔는지 신경 안 써. TypeScript 가 그 duck-typing 을 compile 시점에 옮기되 모든 object 가 lineage 선언하도록 강제하지 않았어. 결과는 JavaScript-자연스럽게 느껴지고 관료주의 없이 버그 잡아.
Nominal system 이 고생하는 패턴도 가능하게 해. `name` 과 `email` 필드 가진 어떤 object 든 받고 싶어? Interface 필요 없어 — (u: { name: string; email: string }) 말하면 돼. `.length` 가진 어떤 거든 작동하는 함수? (x: { length: number }). 모양이 타입.
호환성 규칙
A 타입의 값이 B 타입에 assignable 한 조건: A 가 B 의 모든 멤버 (호환 가능한 타입으로) 최소한 가질 때. 추가는 괜찮음 — 부족은 안 됨.
type Has1 = { a: number };
type Has2 = { a: number; b: string };
const v2: Has2 = { a: 1, b: 'x' };
const v1: Has1 = v2; // ✅ Has2 가 Has1 이 필요한 거 다 가짐 (그리고 더)
그래서 class 가 interface 만족하는 데 implements 필요 없어. 모양 일치하면 compiler 가 받아들임.
Has2 = Has1 + 추가 면, Has2 → Has1 assignment 괜찮음; Has1 → Has2 는 아님. 넓은 타입이 좁은 거 받아, 반대로는 절대 안 됨."추가는 괜찮음" 에 한 가지 단서: 값이 변수 를 거쳐 들어올 때 성립해. 함수 호출이나 할당에 object literal 을 직접 넘기면 TypeScript 가 더 엄격한 규칙 — excess property checking — 을 얹어서 선언 안 된 property 를 오타 잡듯 거부해. greetAnyone({ name: 'Pippa', age: 21 }) 는 에러, 근데 변수 거친 greetAnyone(pippa) 는 통과. 그게 바로 다음 레슨.
Branded 타입 패턴 (nominal 의 escape hatch)
가끔 nominal 필요해 — 둘 다 number 인데도 UserId 와 PostId 가 호환 안 되길 원해. 커뮤니티 패턴은 brand: type UserId = number & { __brand: 'UserId' }. 고유 marker 와의 intersection 이 타입을 구조적으로 구별되게 만들어. Runtime 비용 없음 — brand 는 type layer 전용.
피파의 고백
BrainName 은 그냥 string literal union, class 아냐. Conversation 모양이 component 사이를 구조로 흘러. Escape hatch — branded 타입 — 은 정확히 3 자리에 나타나, `ChatId` 와 `CouncilId` 가 둘 다 string 인데도 호환 안 되길 원했던 곳. Default 는 structural; brand 는 드문 case.