"Structural typing 이 너무 많이 통과시켜. Excess property 체크가 정확히 한 자리에서 안전을 조용히 다시 넣어."
Typo 잡는 surprise
Structural typing 이 추가 허용하는 거 배웠어. 그러면 이거 컴파일 돼야 해:
interface Box { width: number }
function take(b: Box) {}
take({ width: 100, height: 200 }); // 'height' 가 추가 — 모양은 여전히 일치
근데 안 돼. Compiler 가 불평: "Object literal may only specify known properties, and 'height' does not exist in type 'Box'." 이게 excess property checking, object literal 을 직접 전달할 때만 발동하는 special-case 규칙.
이 규칙이 존재하는 이유
순수 structural typing 에 문제 있어: { widht: 100 } (typo!) 같은 literal 쓸 때, 그 literal 이 어떤 interface — 빈 거나 다른 거 — 의 유효 모양이 우연히 돼. Structural 규칙이 그걸 더 넓은 타입으로 받아들여. 버그가 빠져나가.
Excess property checking 이 이걸 해결. Object literal 이 typed slot 에 직접 assign / pass 될 때, compiler 가 추가로 알 수 없는 key 안 나타나는지 체크. Typo 잡혀. 추가 거부. 비용: parameter 에 타입 붙인 함수에 추가 막 전달 못 함.
왜 literal 에만 발동
같은 object 를 변수 통해 먼저 assign 하면, structural typing 정상 적용 — excess 체크 없음.
const extras = { width: 100, height: 200 };
take(extras); // ✅ structural — 받아들여짐
이유: Literal 은 typo 가능성 높은 fresh 코드. 변수는 다른 이유로 이미 존재 — 추가들이 아마 literal 시점의 네가 interface 에 넣을 생각 못 한 뭔가 의미.
정당하게 추가 원할 때 escape hatch
그걸 의미할 때 excess 체크 우회하는 3가지 흔한 패턴:
- 먼저 변수에 저장. 추가에 대해 안다는 가장 명확한 신호.
- Index signature 추가 같은
[key: string]: unknown을 interface 에. "추가가 명시적으로 허용됨" 의미. - `as` 로 cast:
take({ width: 100, height: 200 } as Box). 드물게 써 — 가장 안 안전.
피파의 고백
as 로 우회한 몇 번 각각 의도 설명 주석 있어. Default 입장은 "compiler 가 맞아".