객체를 타입이 정해진 자리에 바로 새로 쓰면, 검사기는 그 타입에 없는 속성(남는 속성)을 짚습니다. 같은 객체라도 이름을 거쳐 넣으면 짚지 않습니다. 모양으로 맞추는 규칙(검사 규칙 4) 위에 새로 쓴 객체에만 얹은 오타 잡기이기 때문입니다.
앞 레슨에서 줄이 더 있는 값도 Cup 자리에 든다고 했습니다. 그런데 Cup 자리에 바로 쓴 객체는 그 뒤로 Cup으로만 쓰여, 남는 줄을 꺼낼 길이 없습니다. 아무도 쓰지 못할 줄은 대개 오타이거나 잘못 적은 것입니다. 그래서 검사기는 이런 줄을 따로 짚어 줍니다.
1바로 쓴 객체와 이름을 거친 객체
shot 줄이 남는 같은 모양의 객체를 두 가지로 Cup 자리에 넣습니다. 9번 줄은 이름 extra를 거쳐 넣고, 10~13번 줄은 Cup 자리에 객체를 바로 씁니다. 검사기는 어느 줄을 짚을까요? Node는 무엇을 찍을까요? 답을 정한 뒤 펼쳐 보세요.
interface Cup {
size: string;
}
const extra = {
size: 'tall',
shot: 2,
};
const a: Cup = extra;
const b: Cup = {
size: 'tall',
shot: 2,
};
console.log(a);
console.log(b);
검사 결과와 실행 결과 보기
main.ts(12,3): error TS2353: Object literal may only specify known properties, and 'shot' does not exist in type 'Cup'.{ size: 'tall', shot: 2 }
{ size: 'tall', shot: 2 }예상과 다르면 그곳이 배울 곳입니다.
Object literal은 중괄호로 그 자리에 새로 쓴 객체, 곧 객체 리터럴입니다. 문장 전체는 '객체 리터럴에는 알려진 속성만 적을 수 있는데, shot은 Cup 타입에 없다'는 뜻입니다.
검사기는 바로 쓴 객체의 남는 줄 shot(12번 줄)만 짚습니다. 이름을 거친 9번 줄은 지나갑니다.
Node는 두 객체를 모두 shot까지 찍습니다. 검사기는 짚기만 할 뿐 객체에서 shot을 지우지 않습니다(검사 규칙 1).
두 줄의 차이는 '남는 줄을 누가 쓸 수 있는가'에 있습니다.
| 이름을 거친 객체(9번 줄) | 바로 쓴 객체(10~13번 줄) | |
|---|---|---|
| shot을 꺼낼 길 | extra.shot으로 꺼낼 수 있다 | 없다 — b는 Cup으로만 쓰인다 |
| 검사기가 보기에 | Cup의 모음에 드는 값 | 아무도 못 쓸 줄이 붙은 값 — 오타일 수 있다 |
| 검사 결과 | 지나감(검사 규칙 4) | 남는 줄을 짚음 — TS2353 |
남는 속성 검사(excess property check)는 모양 규칙을 바꾸지 않습니다. 줄이 더 있는 값은 여전히 Cup의 모음에 들고, 검사기는 그 자리에 새로 쓴 객체에만 오타 잡기를 한 번 더 겁니다.
2남는 줄 검사가 잡는 것 — 오타
Cup에 선택 속성 color를 더했습니다. 손님이 고른 색을 적으려다 color를 영국식 철자 colour로 썼습니다.
interface Cup {
size: string;
color?: string;
}
const cup: Cup = {
size: 'tall',
colour: 'red',
};
console.log(cup);
main.ts(8,3): error TS2561: Object literal may only specify known properties, but 'colour' does not exist in type 'Cup'. Did you mean to write 'color'?{ size: 'tall', colour: 'red' }TS2353과 같은 남는 속성 오류인데, 비슷한 이름이 있어 번호가 TS2561이고 끝에 짐작이 붙었습니다. Did you mean to write 'color'?는 '혹시 color라고 쓰려던 것인가요?'입니다.
같은 객체를 이름을 거쳐 넣으면 어떻게 될까요?
interface Cup {
size: string;
color?: string;
}
const order = {
size: 'tall',
colour: 'red',
};
const cup: Cup = order;
console.log(cup.color ?? '없음');
없음이번에는 아무 오류도 없습니다. color는 선택 속성이라 빠져도 되고, colour는 이름을 거친 남는 줄이라 짚지 않습니다. 오타가 조용히 지나가 손님이 고른 red 대신 '없음'이 찍힙니다.
그래서 객체는 타입이 정해진 자리에 바로 쓸 때 검사기가 가장 많이 짚어 줍니다. 객체를 만들 때부터 const cup: Cup = {처럼 타입을 적어 두면 오타를 그 줄에서 잡습니다.
예제
인자로 객체를 바로 넘기기
함수 인자 자리에 객체를 바로 쓰는 것도 Cup 자리에 새로 쓰는 일입니다.
interface Cup {
size: string;
}
function serve(cup: Cup) {
console.log(cup.size, '한 잔');
}
serve({
size: 'tall',
shot: 2,
});
main.ts(11,3): error TS2353: Object literal may only specify known properties, and 'shot' does not exist in type 'Cup'.tall 한 잔앞 레슨의 예제에서는 이름 order를 같은 객체에 붙여 넘겨서 지나갔습니다. 여기서는 인자 자리에 바로 써서, serve 안에서 쓸 길이 없는 shot을 짚습니다.
자주 틀리는 곳
틀린 생각: 남는 속성이 있는 객체는 그 타입의 값이 아니다.
TS2353을 보면 shot이 붙은 객체는 Cup이 될 수 없다고 읽기 쉽습니다. 1절의 extra는 shot이 있어도 Cup 자리에 들어갔습니다.
interface Cup {
size: string;
}
const extra = {
size: 'tall',
shot: 2,
};
const a: Cup = extra;
console.log(a.shot);
main.ts(10,15): error TS2339: Property 'shot' does not exist on type 'Cup'.29번 줄은 지나갑니다. a의 값에는 shot이 있어 Node는 2를 찍습니다. 다만 a의 선언 타입 Cup에는 shot 줄이 없어서, 검사기는 a로 shot을 꺼내는 10번 줄을 짚습니다.
맞는 규칙: 줄이 더 있는 값도 Cup의 모음에 들고, 검사기는 그 값의 줄 가운데 Cup의 줄만 보여 줍니다. TS2353은 그 자리에 새로 쓴 객체에만 거는 오타 잡기입니다(검사 규칙 4).
틀린 생각: 남는 속성 오류는 타입에 그 속성을 더해서 없앤다.
오류 문장이 'Cup 타입에 colour가 없다'고 하니, Cup에 colour를 더하면 될 것 같습니다.
interface Cup {
size: string;
color?: string;
colour?: string;
}
const cup: Cup = {
size: 'tall',
colour: 'red',
};
console.log(cup.color ?? '없음');
없음TS2561은 사라졌지만 red는 colour에 들어가고, color를 읽는 11번 줄은 '없음'을 찍습니다. 오타가 고쳐지지 않고 타입 안으로 옮겨 갔을 뿐입니다.
맞는 규칙: 남는 속성 오류는 객체 쪽 이름이 틀렸다는 신호일 때가 많습니다. 짐작을 보고 객체의 속성 이름을 고치고, 오류만 지우는 길(타입에 더하기 · 검색에서 만나는 as)은 쓰지 않습니다. as를 쓰지 않는 까닭은 6단원에서 봅니다.
틀린 생각: 남는 속성 검사는 실행할 때 그 속성을 지운다.
검사기가 shot과 colour를 짚었으니, 실행되는 객체에서도 빠질 것 같습니다. 1절과 2절의 실행 결과를 다시 보면, 두 줄이 Node가 찍은 객체에 그대로 있습니다.
맞는 규칙: 검사기는 오류를 알릴 뿐 코드를 바꾸지 않고, Node는 쓴 그대로의 객체를 만들어 돕니다. 남는 속성을 없애려면 코드에서 그 줄을 지웁니다(검사 규칙 1).
확인 문제
1. 1절 코드에서 10번 줄의 const b: Cup = {를 const b = {로 바꿨습니다. 검사 결과는 어떻게 될까요?
interface Cup {
size: string;
}
const extra = {
size: 'tall',
shot: 2,
};
const a: Cup = extra;
const b = {
size: 'tall',
shot: 2,
};
console.log(a);
console.log(b);
힌트
남는 속성 검사는 객체를 '타입이 정해진 자리'에 바로 쓸 때 겁니다. 10번 줄에 아직 그런 자리가 있는지 봅니다.
정답
검사 오류가 없습니다.
{ size: 'tall', shot: 2 }
{ size: 'tall', shot: 2 }: Cup을 지우자 10번 줄은 타입이 정해진 자리가 아니게 됐습니다. b의 타입은 객체에서 추론되어 shot 줄까지 가진 모양이 되고, 견줄 Cup이 없으니 남는 줄도 없습니다.
흔한 오답은 '12번 줄이 여전히 막힌다'입니다. 객체를 중괄호로 새로 쓰면 늘 짚는다고 본 것입니다. 짚는 것은 새로 쓴 객체가 타입이 정해진 자리에 놓일 때뿐입니다.
2. 빈칸에 속성 이름을 써서 검사를 지나게 하세요. 다 채운 코드는 red를 찍어야 합니다.
interface Cup {
size: string;
color?: string;
}
const cup: Cup = {
size: 'tall',
_____: 'red',
};
console.log(cup.color);
힌트
Cup의 속성 줄 이름을 글자 하나하나 봅니다.
정답
color입니다.
interface Cup {
size: string;
color?: string;
}
const cup: Cup = {
size: 'tall',
color: 'red',
};
console.log(cup.color);red흔한 오답은 colour입니다. 그러면 8번 줄에서 TS2561이 나고, 짐작이 color를 알려 줍니다. 검사를 무시하고 돌리면 undefined가 찍힙니다.
3. 걸음 수를 찍는 코드입니다. 검사를 지나갈까요? 막힌다면 몇 번 줄일까요? Node로 돌리면 무엇이 찍힐까요?
interface Walk {
steps: number;
}
function log(w: Walk) {
console.log(`${w.steps}걸음`);
}
const today = {
steps: 8000,
km: 5,
};
log(today);
log({
steps: 6000,
km: 4,
});
힌트
두 번 부르는 log에 넘긴 객체가 각각 이름을 거쳤는지, 인자 자리에 바로 썼는지 봅니다.
정답
검사는 14번 줄에서 막힙니다.
main.ts(14,3): error TS2353: Object literal may only specify known properties, and 'km' does not exist in type 'Walk'.Node는 두 줄을 모두 찍습니다.
8000걸음
6000걸음11번 줄은 이름 today를 거쳐 넘겨 km가 남아도 지나가고, 12~15번 줄은 인자 자리에 객체를 바로 써서 남는 줄 km를 짚습니다. Node는 검사하지 않으므로 두 번 모두 걸음 수를 찍습니다.
흔한 오답은 '11번 줄도 막힌다'입니다. 남는 속성은 어디서나 오류라고 본 것입니다(남는 속성은 늘 오류라는 생각). 실행 결과에서 둘째 줄이 빠진다는 답은 검사 오류가 있는 줄을 Node가 건너뛴다고 본 것입니다(검사 오류가 실행을 막는다는 생각).
컴퓨터에서 해 보기(선택) — 점수에 들지 않습니다. 폰으로만 보고 있다면 건너뛰세요.
1절 코드를 main.ts로 저장해 npx tsc로 검사하고 node main.ts로 돌립니다. 그다음 9번 줄을 객체를 바로 쓰는 꼴로, 10~13번 줄을 이름을 거쳐 넣는 꼴로 서로 바꾸면 검사 결과가 어떻게 바뀔지 먼저 적고, 다시 검사해서 맞춰 봅니다.
검사하고 돌리는 법은 내 컴퓨터에서 검사하고 돌리기에 있습니다.