5단원 '갈래가 있는 데이터'의 세 레슨에서 아홉 문제를 내고, 앞 단원(3 · 4단원)에서 세 문제를 섞었습니다. 앞 단원 문제가 어느 레슨인지는 정답을 펼치면 보입니다. 검사 결과는 지나가는지와 막히는 줄을, 지금 타입은 검사기가 찍는 글자를, 실행 결과는 찍히는 글자를 종이에 끝까지 쓴 뒤 정답을 펼치세요. 실행이 오류로 멈추면 오류 이름이 든 줄까지만 써도 됩니다. 막히면 정답 앞의 힌트를 먼저 펼칩니다. 점수는 매기지 않습니다.
한 번에 다 풀 필요는 없습니다. 문제 1~10은 폰으로도 풀 수 있고(약 30~40분), 끝의 만들어 보기 둘은 컴퓨터에서 tsc와 Node로 직접 검사하고 돌려 봅니다. 이 단원을 건너뛸지 보려면 진단 문항인 문제 1 · 2 · 5 · 8 넷을 먼저 풀어 보세요(약 15분). 진단 문항은 힌트를 펴지 않고 풉니다. 힌트를 펴고 맞힌 문제나 짐작으로 맞힌 문제는 틀린 것으로 칩니다. 모두 맞으면 이 단원 레슨은 건너뛰고, 틀린 문제가 있으면 그 정답 끝의 레슨만 봅니다. 끝의 만들어 보기 둘은 골라서 풉니다.
타입 선언이 긴 문제는 코드를 두 블록으로 나눴습니다. 둘째 블록은 첫째 블록에 이어 쓴 것이고 줄 번호도 이어서 셉니다. 끝의 두 문제는 갈래마다 처리하는 함수를 직접 만들어 봅니다. 컴퓨터가 있으면 블록을 차례로 main.ts에 이어 붙여 npx tsc로 검사 오류가 0인지 본 뒤 node main.ts로 돌려 맞춰 봅니다. 플레이그라운드에서는 맨 위에 export {}; 한 줄을 더합니다. 폰으로만 본다면 문제에 적힌 값으로 내 코드를 한 줄씩 검사하고 따라가 나와야 할 화면과 맞춰 봅니다. 검사 결과는 tsc 7.0.2로, 실행 결과는 Node 26으로 돌린 것입니다.
문제 1
달리기와 수영 기록을 한 타입으로 받습니다.
interface Run {
kind: 'run';
km: number;
}
interface Swim {
kind: 'swim';
laps: number;
}
type Workout = Run | Swim;
위 타입 선언에 이어 씁니다. 검사를 지나갈까요? 지나가지 못하면 막히는 줄을 모두 쓰세요.
function entry(w: Workout) {
const n = w.laps;
if (w.kind === 'run') {
return w.km + 'km';
}
return w.laps + '바퀴';
}
힌트
11 · 13 · 15번 줄마다 그 줄에서 w가 어느 갈래일 수 있는지 봅니다.
정답
main.ts(11,15): error TS2339: Property 'laps' does not exist on type 'Workout'. Property 'laps' does not exist on type 'Run'.11번 줄에서만 막힙니다.
- 11번 줄: 아직 갈래 속성을 확인하기 전이라 w는
Workout(Run이거나 Swim)입니다. laps는 Swim에만 있어 막힙니다. 들여 쓴 줄이 'Run에는 없다'고 짚습니다. - 13번 줄:
w.kind === 'run'확인 뒤의 갈래라 w가 Run으로 좁혀져 km를 씁니다. - 15번 줄: run 갈래는 return으로 끝나 이 줄에 오지 않습니다. 오는 길은 거짓 길 하나라 w는 Swim이고 laps를 씁니다(검사 규칙 5).
흔한 오답 하나는 '지나감'입니다. Workout에 laps를 가진 갈래가 있으니 쓸 수 있다고 본 것입니다. 유니언에서는 모든 갈래가 함께 가진 속성만 확인 없이 씁니다. 15번 줄까지 막힌다고 쓰면 run 갈래를 지나 15번 줄에 오면 w가 다시 Workout이라고 본 것입니다(갈래를 나오면 선언 타입이라는 생각).
다시 볼 레슨: 판별 유니언 · 좁히기 — typeof와 타입 추적 표
문제 2
포장과 배달 주문을 한 타입으로 받습니다.
interface Pickup {
kind: 'pickup';
wait: number;
}
interface Delivery {
kind: 'delivery';
fee: number;
memo?: string;
}
type Ship = Pickup | Delivery;
위 타입 선언에 이어 씁니다. 14번 줄과 16번 줄에서 s의 지금 타입은 각각 무엇일까요?
function note(s: Ship) {
switch (s.kind) {
case 'pickup':
return s.wait + '분';
case 'delivery':
return '배달비 ' + s.fee;
}
}
힌트
14번 줄과 16번 줄이 각각 어느 case 아래에 있는지 봅니다.
힌트 둘
switch의 case마다 s.kind가 어느 값인지 정해집니다. 그 값을 가진 갈래가 무엇인지 봅니다.
정답
검사 오류 없이 지나가고, 14번 줄의 s는 Pickup, 16번 줄의 s는 Delivery입니다. switch의 case는 갈래 속성 확인과 같아서, case 'pickup': 아래에서는 kind가 'pickup'인 갈래 Pickup으로 좁혀집니다(검사 규칙 5). 그래서 14번 줄은 wait를, 16번 줄은 fee를 씁니다.
흔한 오답은 둘 다 Ship으로 쓴 것입니다. case로 나눠도 검사기가 s를 좁히지 않는다고 본 것입니다. 그러면 14번 줄의 wait가 막혀야 하는데, 검사 결과에 오류가 없습니다.
다시 볼 레슨: 판별 유니언
문제 3
포장 주문을 Ship 타입 자리에 넣습니다. 가게의 번호표 타입 Ticket도 함께 적었습니다.
interface Pickup {
kind: 'pickup';
wait: number;
}
interface Delivery {
kind: 'delivery';
fee: number;
memo?: string;
}
type Ship = Pickup | Delivery;
interface Ticket {
kind: 'pickup';
wait: number;
no: number;
}
위 타입 선언에 이어 씁니다. 검사를 지나갈까요? 지나가지 못하면 막히는 줄을 모두 쓰세요.
const t: Ticket = {
kind: 'pickup',
wait: 5,
no: 12,
};
const a: Ship = t;
const b: Ship = {
kind: 'pickup',
wait: 5,
fee: 0,
};
힌트
21번 줄과 22~26번 줄에서 Ship 자리에 넣는 값을 하나씩 봅니다.
힌트 둘
21번 줄의 t는 이름을 거친 값이고, 22~26번 줄은 Ship 자리에 바로 새로 쓴 객체입니다. 새로 쓴 객체는 23번 줄의 kind 값으로 갈래가 정해지고, 그 갈래가 가진 속성 줄과 견줍니다.
정답
main.ts(25,3): error TS2353: Object literal may only specify known properties, and 'fee' does not exist in type 'Pickup'.25번 줄에서만 막힙니다.
- 21번 줄은 지나갑니다. t의 타입 이름은 Ticket이지만, Pickup 갈래가 요구하는 kind · wait 줄을 다 가졌습니다. no 줄이 더 있어도 이름 t를 거친 값이라 오류가 아닙니다. 검사기는 타입 이름이 아니라 모양으로 맞춥니다(검사 규칙 4).
- 22~26번 줄의 객체는 Ship 자리에 바로 새로 쓴 것입니다. kind가 'pickup'이라 Pickup 갈래로 맞춰지고, Pickup에는 fee 줄이 없어 남는 속성 fee(25번 줄)를 오타일 수 있다고 잡습니다(검사 규칙 4).
흔한 오답 하나는 21번 줄도 막힌다고 쓴 것입니다. Ticket이 Ship의 갈래 이름이 아니라서 넣을 수 없다고 본 것입니다(이름이 같아야 맞는다는 생각). no 줄이 남아서 막힌다고 봤다면 남는 속성을 어디서나 오류로 본 것입니다(남는 속성은 늘 오류라는 생각). 다른 흔한 오답은 '지나감'입니다. Ship의 다른 갈래(Delivery)에 fee가 있으니 넣어도 된다고 본 것입니다. 남는 속성은 그 객체가 맞춰진 갈래를 기준으로 봅니다.
다시 볼 레슨: 모양으로 맞추기 · 새로 쓴 객체의 남는 속성 · 판별 유니언
문제 4
수영이면 바퀴 수를 먼저 찍고 거리를 돌려주려 합니다.
interface Run {
kind: 'run';
km: number;
}
interface Swim {
kind: 'swim';
laps: number;
}
type Workout = Run | Swim;
위 타입 선언에 이어 씁니다. 14번 줄에서 w의 지금 타입은 무엇일까요? 검사를 지나갈까요? 지나가지 못하면 막히는 줄을 모두 쓰세요.
function entry(w: Workout) {
if (w.kind === 'swim') {
console.log(w.laps + '바퀴');
}
return w.km + 'km';
}
힌트
타입 추적 표에 10 · 11 · 12 · 14번 줄의 w를 적어 봅니다.
힌트 둘
11번 줄의 갈래가 return으로 끝나는지 봅니다. 14번 줄로 오는 길이 몇 개인지 셉니다.
정답
14번 줄에서 w의 지금 타입은 Workout이고, 검사는 14번 줄에서 막힙니다.
main.ts(14,12): error TS2339: Property 'km' does not exist on type 'Workout'. Property 'km' does not exist on type 'Swim'.| 줄 | 이름 | 지금 타입 |
|---|---|---|
| 10 | w | Workout |
| 11 | w | Workout → 참 Swim 거짓 Run |
| 12 | w | Swim |
| 14 | w | 만나는 줄 — 참 Swim + 거짓 Run → Workout |
11번 줄의 갈래는 return 없이 끝나, Swim인 길도 14번 줄로 옵니다. 두 길이 합쳐져 w는 다시 Run이거나 Swim이고, Swim에는 km가 없어 막힙니다(검사 규칙 5).
같은 답으로 치는 꼴: Run | Swim(Workout이 그 이름입니다).
흔한 오답은 Run으로 쓰고 '지나감'이라고 쓴 것입니다. 수영 갈래를 지나면 남는 것은 달리기라고 본 것입니다(좁힘이 끝까지 간다는 생각). 그렇게 되는 것은 갈래가 return으로 끝날 때뿐입니다.
다시 볼 레슨: 좁히기 — typeof와 타입 추적 표 · 판별 유니언
문제 5
결제 방법마다 수수료를 돌려줍니다.
type Pay =
| 'card'
| 'cash'
| 'point';
위 타입 선언에 이어 씁니다. 12번 줄에서 p의 지금 타입은 무엇일까요? 검사를 지나갈까요? 지나가지 못하면 막히는 줄을 모두 쓰세요.
function fee(p: Pay) {
switch (p) {
case 'card':
return 100;
case 'cash':
return 0;
default:
const left: never = p;
return left;
}
}
console.log(fee('card'));
힌트
Pay의 값 셋 가운데 case로 처리하지 않은 값이 있는지 봅니다.
정답
12번 줄에서 p의 지금 타입은 "point"이고, 검사는 12번 줄에서 막힙니다.
main.ts(12,13): error TS2322: Type '"point"' is not assignable to type 'never'.case 'card'와 case 'cash'를 지나면 default로 오는 값은 'point' 하나뿐입니다. never는 값이 하나도 없는 모음이라 "point"를 넣을 수 없습니다. 이 오류가 '처리하지 않은 갈래 point가 남았다'는 알림입니다.
같은 답으로 치는 꼴: 'point'처럼 작은따옴표로 쓴 것.
흔한 오답은 '지나감'입니다. 16번 줄은 'card'만 넘기니 default는 실행되지 않고, 그래서 검사기도 읽지 않는다고 본 것입니다(검사기가 실행을 따라간다는 생각). 검사기는 값을 모르니 default로 오는 길까지 읽습니다. p를 never로 쓰면 point가 빠진 것을 놓친 것입니다.
다시 볼 레슨: 빠짐없이 — never로 갈래 검사
문제 6
도서관 자리마다 쓸 수 있는 시간(분)을 돌려줍니다. 스터디 룸(room)은 120분이어야 합니다.
type Seat =
| 'bar'
| 'table'
| 'room';
위 타입 선언에 이어 쓴 코드가 검사에서 막힙니다.
function minutes(s: Seat) {
switch (s) {
case 'bar':
return 60;
case 'table':
return 90;
default:
const left: never = s;
return left;
}
}
console.log(minutes('room'));
main.ts(12,13): error TS2322: Type '"room"' is not assignable to type 'never'.아래 고치기 가운데 검사 오류를 없애는 것을 모두 고르세요. 그 가운데 minutes('room')이 120을 찍는 것도 모두 고르세요.
- ① 11번 줄 앞에
case 'room':과return 120;두 줄을 더한다 - ② 11~13번 줄(default와 그 아래 두 줄)을 지운다
- ③ 12번 줄을
const left: string = s;로 고친다
힌트
고친 코드마다 room이 어느 줄로 가서 무엇을 돌려주는지 따라가 봅니다.
정답
검사 오류를 없애는 것은 ①②③ 셋 모두이고, 120을 찍는 것은 ①뿐입니다.
type Seat =
| 'bar'
| 'table'
| 'room';①로 고친 코드를 위 타입 선언에 이어 씁니다.
function minutes(s: Seat) {
switch (s) {
case 'bar':
return 60;
case 'table':
return 90;
case 'room':
return 120;
default:
const left: never = s;
return left;
}
}
console.log(minutes('room'));120never 줄의 오류는 빠진 갈래를 알려 주는 것이라, 빠진 case를 채우는 것이 고치기입니다. 고친 뒤에도 never 줄이 남아, 다음에 자리 종류가 늘면 또 알려 줍니다.
- ②는 오류를 내던 줄을 지운 것입니다. 검사는 지나지만 room은 어느 case에도 걸리지 않아 아무것도 돌려주지 않고 undefined를 찍습니다. 다음에 갈래가 늘어도 알려 줄 줄이 없습니다.
- ③은 left의 타입을 넓혀 오류만 지운 것입니다. room이 default로 가 글자 'room'을 그대로 돌려줘
room을 찍습니다.
흔한 오답은 ②나 ③까지 고른 것입니다. never 줄의 오류를 없애야 할 걸림돌로 본 것입니다. 오류만 지우면 검사는 지나도 결과는 틀립니다(검사를 지나면 맞다는 생각).
다시 볼 레슨: 빠짐없이 — never로 갈래 검사
문제 7
배달 주문이면 메모를 다듬어 돌려줍니다.
interface Pickup {
kind: 'pickup';
wait: number;
}
interface Delivery {
kind: 'delivery';
fee: number;
memo?: string;
}
type Ship = Pickup | Delivery;
위 타입 선언에 이어 씁니다. 검사 결과와 실행 결과를 각각 쓰세요.
function note(s: Ship) {
if (s.kind === 'delivery') {
return s.memo.trim();
}
return '포장';
}
const d: Ship = {
kind: 'delivery',
fee: 3000,
};
console.log(note(d));
힌트
13번 줄에서 s가 어느 갈래로 좁혀지는지, 그 갈래의 memo 줄에 무엇이 붙어 있는지 봅니다.
정답
main.ts(13,12): error TS18048: 's.memo' is possibly 'undefined'.main.ts:13 return s.memo.trim(); ^ TypeError: Cannot read properties of undefined (reading 'trim') at note (main.ts:13:19) at main.ts:21:13검사는 13번 줄에서 막힙니다. 갈래 확인으로 s는 Delivery로 좁혀지지만, Delivery의 memo는 선택 속성이라 지금 타입이 string | undefined입니다. 이 TS18048은 memo가 undefined일 수 있다는 뜻입니다.
Node는 그대로 돌다 d에 memo가 없어 13번 줄에서 멈춥니다.
흔한 오답은 '지나감'과 실행 결과 '포장' 또는 빈 문자열입니다. 갈래를 좁히면 그 갈래의 속성은 다 있다고 본 것입니다(꺼낸 값은 늘 있다는 생각). 좁혀도 선택 속성은 따로 거릅니다 — 예를 들어 s.memo ?? ''.
다시 볼 레슨: 선택 속성과 readonly · null·undefined 거르기 · 판별 유니언
문제 8
차와 케이크를 갈래로 나눠 값을 셈합니다. 케이크는 5000원에 상자 값 500원을 더해 5500원이어야 합니다.
interface Tea {
kind: 'tea';
won: number;
}
interface Cake {
kind: 'cake';
won: number;
box: number;
}
type Item = Tea | Cake;
위 타입 선언에 이어 씁니다. 검사 결과와 실행 결과를 각각 쓰세요.
function price(i: Item) {
switch (i.kind) {
case 'tea':
return i.won;
case 'cake':
return i.won - i.box;
}
}
const cake: Item = {
kind: 'cake',
won: 5000,
box: 500,
};
console.log(price(cake));
힌트
cake를 넘긴 price가 어느 case의 return에 닿는지 따라가 봅니다.
힌트 둘
손으로 5000과 500을 16번 줄대로 셈해 봅니다.
정답
4500검사는 오류 없이 지나지만 뜻한 5500이 아니라 4500이 찍힙니다. 이렇게 다르게 찍히면 처음 어긋나는 줄을 찾습니다 — 여기서는 16번 줄입니다. 상자 값을 더해야 하는데 뺐습니다. return i.won + i.box;로 고치면 5500입니다. i.won과 i.box가 둘 다 number라 빼기도 타입이 맞아, 검사기는 알려 주지 않습니다(검사 규칙 1).
흔한 오답은 실행 결과를 5500으로 쓴 것입니다. 검사를 지났으니 합계도 맞다고 본 것입니다(검사를 지나면 맞다는 생각). 검사 오류가 0이 된 뒤에도 작은 값으로 손으로 셈한 값과 맞춰 봅니다.
다시 볼 레슨: 작은 주문표 만들기 · 타입 검사기가 하는 일
문제 9
하고 싶은 일마다 쓸 타입이나 낱말을 쓰세요.
- (가) 값이 하나도 들지 않는 타입
- (나) Order 객체 여럿을 담는 배열의 타입
- (다) switch에서 앞의 case 어디에도 걸리지 않은 값이 가는 자리를 여는 낱말
- (라) 갈래 속성 값을 확인할 때 쓰는, 값의 종류까지 같은지 견주는 연산자
힌트
(나)는 2단원에서 number[]을 적은 꼴과 같습니다.
정답
받는 답: (가) never · (나) Order[] · (다) default · (라) ===
틀리는 꼴:
- (가)
void— 돌려주는 값이 없는 함수의 타입입니다. never는 값이 하나도 없는 모음이라 어떤 값도 넣을 수 없습니다. - (나)
[Order]— Order 한 칸짜리 튜플입니다.Order만 쓰면 주문 하나입니다. - (라)
==— 견주기 전에 값의 종류를 바꿀 수 있어 갈래 확인에는===를 씁니다.
다시 볼 레슨: 빠짐없이 — never로 갈래 검사 · 작은 주문표 만들기
문제 10
차 한 잔의 값을 찍으려 합니다. 실행하면 3500이 찍히지만 검사에서 막힙니다.
interface Tea {
kind: 'tea';
won: number;
}
interface Cake {
kind: 'cake';
won: number;
box: number;
}
type Item = Tea | Cake;
위 타입 선언에 이어 쓴 코드와 검사 결과입니다. 몇 번 줄의 무엇을 고쳐야 할까요? 고친 줄을 쓰세요.
function price(i: Item) {
switch (i.kind) {
case 'tea':
return i.won;
case 'cake':
return i.won + i.box;
}
}
const tea = {
kind: 'tea',
won: 3500,
};
console.log(price(tea));
main.ts(23,19): error TS2345: Argument of type '{ kind: string; won: number; }' is not assignable to parameter of type 'Item'. Type '{ kind: string; won: number; }' is not assignable to type 'Tea'. Types of property 'kind' are incompatible. Type 'string' is not assignable to type '"tea"'.힌트
들여 쓴 줄을 끝까지 읽고, 23번 줄에서 넘기는 tea의 타입이 몇 번 줄에서 정해졌는지 거슬러 갑니다.
힌트 둘
tea의 kind가 string이 된 줄을 찾습니다.
정답
19번 줄을 const tea: Item = {으로 고칩니다.
interface Tea {
kind: 'tea';
won: number;
}
interface Cake {
kind: 'cake';
won: number;
box: number;
}
type Item = Tea | Cake;고친 코드를 위 타입 선언에 이어 씁니다.
function price(i: Item) {
switch (i.kind) {
case 'tea':
return i.won;
case 'cake':
return i.won + i.box;
}
}
const tea: Item = {
kind: 'tea',
won: 3500,
};
console.log(price(tea));3500오류는 23번 줄에서 났지만 까닭은 19번 줄입니다. 타입을 적지 않고 만든 객체는 속성 kind가 'tea'에서 string으로 넓어집니다(검사 규칙 3). 들여 쓴 마지막 줄이 그 string을 Tea의 kind 자리 "tea"에 넣을 수 없다고 짚습니다. 만드는 줄에 Item을 적으면 kind가 그 자리에서 'tea'로 검사됩니다.
- 다르게 고쳐도 되는 곳:
const tea: Tea = {도 같습니다. 23번 줄에서 이름 tea 대신 객체를 price의 괄호 안에 바로 써서 넘겨도 지나갑니다. - 흔한 오답은 Tea의 kind를
kind: string;으로 바꾸는 것입니다. 그러면 갈래를 가르지 못해 16번 줄의 box가 막힙니다(TS2339). 11번 줄의 price 매개변수를 any로 바꾸면 오류는 사라지지만 검사를 끈 덮는 길입니다.
넓어진 타입이 생긴 줄 대신 오류가 난 줄에서 고칠 곳을 찾은 것입니다(오류 줄이 고칠 줄이라는 생각). 객체 속성의 'tea'도 값 그대로의 타입이라고 보면 이 오류가 왜 나는지 놓칩니다(넓어지지 않는다는 생각).
다시 볼 레슨: 판별 유니언 · 작은 주문표 만들기 · 타입 오류 읽기
문제 11 (만들어 보기)
교통 요금을 갈래마다 셈하는 함수 pay를 만들어 봅니다. 버스는 늘 1500원, 택시는 1km마다 1000원입니다. 다 채워 돌리면 이렇게 나와야 합니다.
1500
3000
interface Bus {
kind: 'bus';
}
interface Taxi {
kind: 'taxi';
km: number;
}
type Ride = Bus | Taxi;
위 타입 선언에 이어 쓴 뼈대입니다. 주석 셋 아래를 채우세요. 검사 오류는 0이어야 하고, 갈래가 새로 생기면 검사기가 알려 주도록 '3. 나머지는 never로'도 채웁니다.
function pay(r: Ride) {
// 1. 갈래 속성으로 나누기
// 2. 갈래마다 처리
// 3. 나머지는 never로
}
const rides: Ride[] = [
{ kind: 'bus' },
{ kind: 'taxi', km: 3 },
];
for (const r of rides) {
console.log(pay(r));
}
다 쓰면 택시의 km를 5로 바꿔 따라가 봅니다. 둘째 줄이 5000이어야 합니다.
힌트
'1. 갈래 속성으로 나누기'는 switch (r.kind), '2. 갈래마다 처리'는 case 둘입니다. km는 택시 갈래 안에서만 쓸 수 있습니다.
정답
모범 답입니다. 타입 선언은 그대로 두고, 함수를 채운 뒤 뼈대의 아래 일곱 줄을 이어 붙입니다.
interface Bus {
kind: 'bus';
}
interface Taxi {
kind: 'taxi';
km: number;
}
type Ride = Bus | Taxi;채운 함수를 위 타입 선언에 이어 씁니다.
function pay(r: Ride) {
// 1. 갈래 속성으로 나누기
switch (r.kind) {
// 2. 갈래마다 처리
case 'bus':
return 1500;
case 'taxi':
return r.km * 1000;
// 3. 나머지는 never로
default:
const left: never = r;
return left;
}
}뼈대의 아래 일곱 줄을 앞 코드에 이어 씁니다.
const rides: Ride[] = [
{ kind: 'bus' },
{ kind: 'taxi', km: 3 },
];
for (const r of rides) {
console.log(pay(r));
}1500
3000km를 5로 바꾸면 택시 갈래가 5 * 1000을 돌려 둘째 줄이 5000입니다.
- 다르게 써도 되는 곳:
if (r.kind === 'bus')로 버스를 먼저 돌려보내고 아래에서 택시를 셈해도 같은 화면이 나옵니다. 다만 그 꼴에는 never 줄이 없어, 갈래가 늘었을 때 검사기가 알려 주지 않습니다. - 검사 오류가 0이고, 찍은 화면이 위와 같고, km 5도 맞으면 맞습니다.
- 흔한 오답은 case 'taxi'를 빠뜨린 것입니다. never 줄이 TS2322(Taxi를 never 자리에 넣을 수 없다)로 짚습니다. 그대로 돌리면 택시 객체가 default에서 그대로 돌아와 요금 대신 객체가 찍힙니다.
다시 볼 레슨: 빠짐없이 — never로 갈래 검사 · 판별 유니언
문제 12 (만들어 보기)
이번에는 빈 데서 씁니다. 음료 크기는 S · M · L 셋뿐이고 값은 차례로 3500 · 4000 · 4500원입니다. 크기 타입 Size와, 크기를 받아 값을 돌려주는 함수 price를 쓰세요. 갈래마다 처리하기 틀로 쓰고, price('M')을 찍으면 이렇게 나와야 합니다. 검사 오류는 0이어야 합니다.
4000
다 쓰면 price('L')로 바꿔 따라가 봅니다(4500). Size에 'XL'을 더하면 검사가 never 줄에서 막혀야 합니다.
힌트
하위 목표 셋(1. 갈래 속성으로 나누기 · 2. 갈래마다 처리 · 3. 나머지는 never로)을 따릅니다. 여기서는 크기 값 자체로 나눕니다.
정답
모범 답입니다.
type Size = 'S' | 'M' | 'L';
function price(s: Size) {
switch (s) {
case 'S':
return 3500;
case 'M':
return 4000;
case 'L':
return 4500;
default:
const left: never = s;
return left;
}
}
console.log(price('M'));40003번 줄이 '1. 갈래 속성으로 나누기', 4~9번 줄이 '2. 갈래마다 처리', 10~12번 줄이 '3. 나머지는 never로'입니다. price('L')이면 8번 줄로 가 4500입니다. Size에 'XL'을 더하면 처리하지 않은 'XL'이 default로 와서 11번 줄이 TS2322('XL'을 never 자리에 넣을 수 없다)로 막힙니다.
- 다르게 써도 되는 곳: case 차례는 바꿔도 됩니다. 매개변수 이름은 달라도 됩니다.
- 검사 오류가 0이고, 찍은 화면이 위와 같고,
price('L')도 4500이면 맞습니다. - 흔한 오답은 default와 never 줄을 빼고 쓴 것입니다. S · M · L만 넘길 때는 같은 값이 나오지만, 'XL'을 더해도 검사는 조용하고
price('XL')이 undefined를 돌려줍니다. default를 두지 않아도 새 갈래가 빠지면 어디선가 검사 오류가 난다고 본 것입니다. 새 갈래를 알려 주는 것은 never 줄입니다.
다시 볼 레슨: 빠짐없이 — never로 갈래 검사