TS 2.5 · 2부. 함수와 객체

타입 좁히기

여러 가지일 수 있는 값을 확인해서 하나로 좁힙니다.

예상 학습 시간 16분 실행 단추가 있는 예제는 고쳐서 실행해 볼 수 있습니다 난이도 중급
개념 설명

확인하면 그만큼 좁아집니다

1.4 에서 유니온을 받으면 모두가 가진 것만 사용할 수 있다는 것을 보았습니다. 그러면 어느 쪽인지 확인하면 됩니다.

TypeScript 는 확인하는 코드를 읽고 그 안쪽에서 타입을 좁힙니다. 따로 알려 주지 않아도 if 를 보고 알아냅니다.

if (typeof value === "string") {
	// 여기서 value 는 string 입니다
} else {
	// 여기서는 나머지입니다
}
최소 예제 · 편집 가능

typeof 로 좁히기

main.ts
function show(value: string | number): string {
	if (typeof value === "string") return value.toUpperCase();
	return value.toFixed(1);
}

console.log(show("a-1"));
console.log(show(3));
출력
A-1 3.0

return 뒤에는 else 를 적지 않았는데도 value.toFixed 가 됩니다. 글자인 경우는 이미 돌아갔으니 남은 것은 숫자뿐이라는 것을 압니다.

상세 사용법

값으로 갈라지는 유니온

리터럴 유니온(1.4)은 값을 견주어 좁힙니다. switch 와 잘 맞습니다.

main.ts
type Status = "pending" | "done" | "failed";

function label(status: Status): string {
	switch (status) {
		case "pending": return "기다리는 중";
		case "done": return "끝났습니다";
		case "failed": return "실패했습니다";
	}
}

console.log(label("pending"));
console.log(label("failed"));
출력
기다리는 중 실패했습니다

default 가 없는데도 오류가 나지 않았습니다. 세 가지를 모두 다뤘으니 빠진 경우가 없다는 것을 압니다. 그래서 Status 에 값을 하나 더하면 이 함수가 곧바로 오류가 되어, 고칠 자리를 알려 줍니다.

상세 사용법

객체를 갈라내는 표시

객체가 여러 모양일 때는 모양마다 다른 값을 담는 속성을 하나 두고 그것으로 갈라냅니다. 자주 kind 나 type 이라고 이름 붙입니다.

main.ts
type Circle = { kind: "circle"; radius: number };
type Rect = { kind: "rect"; width: number; height: number };

function area(shape: Circle | Rect): number {
	if (shape.kind === "circle") return Math.round(shape.radius * shape.radius * 3.14);
	return shape.width * shape.height;
}

console.log(area({ kind: "circle", radius: 10 }));
console.log(area({ kind: "rect", width: 3, height: 4 }));
출력
314 12

shape.kind 를 견주는 것만으로 radius 와 width 를 각각 사용할 수 있게 됩니다. 서버가 돌려주는 응답이 경우마다 다를 때 이 방식을 자주 사용합니다.

main.ts
type Circle = { kind: "circle"; radius: number };
type Rect = { kind: "rect"; width: number; height: number };

function area(shape: Circle | Rect): number {
	return shape.width * shape.height;
}
출력
5:15 TS2339 'Circle | Rect' 형식에 'width' 속성이 없습니다. 'Circle' 형식에 'width' 속성이 없습니다. 5:29 TS2339 'Circle | Rect' 형식에 'height' 속성이 없습니다. 'Circle' 형식에 'height' 속성이 없습니다.

좁히지 않으면 이렇게 막힙니다. 딸린 설명이 왜 없는지 짚어 줍니다 — Circle 에 width 가 없기 때문입니다.

흔한 실수

처음에 자주 걸리는 것

typeof null 은 "object" 입니다

JavaScript 의 오래된 규칙이 그대로 있습니다(JS 1.4). typeof x === "object" 로 좁힐 때는 x !== null 을 함께 확인해야 합니다.

배열은 typeof 로 갈라지지 않습니다

배열도 "object" 입니다. Array.isArray(x) 를 사용합니다 — 이것도 좁혀 줍니다.

함수로 빼면 좁힌 것이 풀립니다

if (isString(value)) 처럼 직접 만든 함수로 확인하면 그 안쪽에서 좁혀지지 않습니다. 좁혀 주게 하려면 반환 타입을 value is string 으로 적어야 합니다.

실습 문제

직접 해보기

1. 세 가지 값 설명하기 난이도 중

글자·숫자·참거짓을 받아 각각 다르게 설명하는 함수를 만드세요.

function describe(value: string | number | boolean): string {
	if (typeof value === "string") return `글자 ${value}`;
	if (typeof value === "number") return `숫자 ${value}`;
	return `참거짓 ${value}`;
}

console.log(describe("홍길동"));
console.log(describe(27));
console.log(describe(true));
2. 모양을 하나 더하기 난이도 상

위 area 에 삼각형을 더하고, 더하는 순간 어디가 오류로 잡히는지 확인하세요.

type Triangle = { kind: "triangle"; base: number; height: number } 를 만들고 매개 변수 타입에 이어 붙입니다. 마지막 return shape.width * shape.height 가 곧바로 막히므로 고쳐야 할 자리를 컴파일러가 알려 줍니다. 이것이 갈라내는 표시를 두는 값입니다.
요약

이 단원의 정리

  • 확인하는 코드를 읽고 그 안쪽에서 타입이 좁아집니다.
  • typeof 로 기본 타입을 갈라냅니다.
  • 리터럴 유니온은 값을 견주어 갈라냅니다.
  • 객체는 갈라내는 표시 속성을 하나 두고 그것으로 갈라냅니다.
  • 모든 경우를 다루면 default 가 없어도 됩니다.