"함수 타입은 3가지에 대한 contract: 뭐 들어오고, 뭐 나가고, 뭐 optional 인가."
함수 typing 의 4가지 모양
함수에 타입 붙이는 4가지 방법 만나:
1. Inline parameter 와 return annotation. function greet(name: string): string { return `hi ${name}` }. Default 형태; 보통 원하는 거.
2. 함수-타입 alias. type Greeter = (name: string) => string. 전체 signature 를 들어내서 이름 줌. 같은 callback 모양 반복될 때 사용.
3. Interface 의 method 단축형. interface API { greet(name: string): string }. 함수-타입 값 가진 property 와 동등, 근데 "이 object 는 method 가짐" 으로 읽혀.
4. Type/interface 의 call signature. interface Greeter { (name: string): string }. 전체 모양 이 함수. 흔치 않음; callable object 의 .d.ts 선언에 주로 보임.
Optional, default, rest parameter
3개 modifier 가 거의 모든 필요한 parameter 모양 cover:
- Optional:
function f(name?: string)— name 생략되거나 undefined 가능. 안에선 타입string | undefined. - Default:
function f(name: string = 'anon')— name 에 fallback. 안에선 타입 그냥string(undefined 없음 — default 가 채움). - Rest:
function f(...names: string[])— name 이 배열로 모임. Annotation 이 배열에.
순서 중요: required parameter 먼저, 그다음 optional, 그다음 rest. Compiler 강제; 재배치는 에러.
함수-타입 호환성 — 필요한 하나의 규칙
TypeScript 가 두 별개 규칙으로 함수-타입 assignability 체크:
- Parameter 체크 (contravariant): assignable 한 함수가 자신이 채울 slot 보다 적은 parameter 나 더 넓은 parameter 타입 받아도 됨.
(a: string) => void가(a: string, b: number) => void에 assignable — 더 받는 거 괜찮음, 더 요구하는 거 안 됨. - Return 체크 (covariant): assignable 한 함수가 slot 이 기대하는 것보다 더 좁은 (더 구체적) 타입 return 해도 됨.
() => 'red'가() => string에 assignable — 더 좁은 return 괜찮음, 더 넓은 return 안 됨.
Parameter 의 contravariance 가 사람들 놀라게 하는 규칙. 안전한 이유: 일부 parameter 무시하는 함수는 그것들 쓰는 함수가 기대되는 곳에 호출 가능 — 추가 argument 가 그냥 버려지니까.