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

Excess Property 체크: TypeScript 가 너의 typo 잡아

~9 min · types-interfaces, excess-property, object-literal, structural-typing

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"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). 드물게 써 — 가장 안 안전.
경험 법칙: Excess property checking 은 너의 친구. 발동하면 90% 가 typo 잡은 거. 다른 10%, interface 조정하거나 변수에 저장해서 고쳐 — `as` 손 안 뻗어.

피파의 고백

Excess property checking 이 cwkPippa 에서 최소 12번 살렸어. TypeScript 의 필드 rename 이 literal 에 옛 이름 쓴 모든 call site 잡아 — 정확히 plain JavaScript 면 조용히 ship 됐을 typo. as 로 우회한 몇 번 각각 의도 설명 주석 있어. Default 입장은 "compiler 가 맞아".

Code

규칙이 발동할 때 — 그리고 안 할 때·typescript
interface Box {
  width: number;
  height: number;
}

function render(b: Box) {}

// Literal 직접 전달 — excess property 체크 발동.
render({ width: 100, height: 200, depth: 50 });
//                                  ^ Object literal may only specify known properties

// 같은 값을 변수 통해 — structural 규칙, 체크 스킵.
const withExtras = { width: 100, height: 200, depth: 50 };
render(withExtras);                                  // ✅ 받아들임

// Typo 잡기:
render({ width: 100, hieght: 200 });
//                    ^ Object literal may only specify known properties
//                      Did you mean to write 'height'?

// 이 typo 가 순수 structural 규칙에선 빠져나갔을 거.
// Excess property 체크가 literal 용 안전망.
3개 escape — 하나만 good·typescript
// 정당하게 추가 원할 때 escape hatch 3개.

interface Config {
  port: number;
}

function setup(c: Config) {}

// 1. Index signature — 추가가 설계상 허용.
interface OpenConfig {
  port: number;
  [key: string]: unknown;
}

function setupOpen(c: OpenConfig) {}
setupOpen({ port: 5173, host: 'localhost' });   // ✅

// 2. 먼저 변수 — 의도의 암묵 신호.
const c = { port: 5173, host: 'localhost' };
setup(c);                                        // ✅ (추가 'host' 받아들임)

// 3. As-cast — 아껴 써, 주석과 함께만.
setup({ port: 5173, host: 'localhost' } as Config);  // ✅ 근데 sloppy
// 더 나음: Config 에 'host' 추가하거나, Config 에 index signature 쓰게.

External links

Exercise

interface ApiResponse { status: number; data: unknown } 만들어. handle({ status: 200, data: 'hi', cached: true }) 호출 시도. Compiler 가 뭐라고? 이제 object 를 변수에 먼저 저장하고 그걸 전달 — 같은 에러 나타나? TypeScript 의 reasoning 에서 뭐 바뀌었어?
Hint
Literal 전달 버전이 excess property 체크 발동. 변수 전달 버전은 structural 규칙만 발동 (추가 조용히 받아들임). 교훈: literal 이 typo 숨는 곳, 그래서 TypeScript 가 거기 추가 체크 더 함.

Progress

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

댓글 0

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

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