TS LAB
TS 4.2 · 4부. 실무 준비

JavaScript 에서 옮기기

이미 있는 코드에 타입을 조금씩 붙이는 순서입니다.

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

한꺼번에 옮기지 않습니다

이미 돌아가고 있는 JavaScript 코드에 타입을 붙이는 일입니다. 전부 한 번에 바꾸려 하면 오류가 수백 개씩 쏟아집니다. 순서가 있습니다.

  1. 설정을 느슨하게 열고 시작합니다(strict: false).
  2. 파일 이름을 .ts 로 바꿉니다. 한 번에 하나씩입니다.
  3. 막히는 자리를 any 로 잠시 막습니다.
  4. 바깥에서 들어오는 값부터 제대로 된 타입을 붙입니다.
  5. 설정을 하나씩 엄격하게 올립니다.

4번이 순서상 중요합니다. 가장 자주 터지는 자리가 거기이기 때문입니다 — 서버 응답, 사용자 입력, 저장소에서 꺼낸 값.

최소 예제 · 편집 가능

any 로 막아 두면 어떻게 되나

main.ts
let data: any = JSON.parse('{"name":"홍길동"}');

console.log(data.name);
console.log(data.nmae);
console.log(data.age.toFixed(1));
출력 · 크롬
홍길동 undefined TypeError: Cannot read properties of undefined (reading 'toFixed')
출력 · 파이어폭스
홍길동 undefined TypeError: can't access property "toFixed", data.age is undefined

타입 오류가 하나도 나지 않았습니다. 오타도, 없는 속성을 파고드는 것도 그대로 지나가 실행할 때 터집니다 — TypeScript 를 쓰기 전과 같습니다.

그래서 any잠시 막아 두는 것이지 끝이 아닙니다. 옮기는 동안 남겨 두고, 하나씩 걷어냅니다.

출력을 둘로 적어 둔 것은 실행할 때 나는 오류의 문구가 브라우저마다 다르기 때문입니다(JS 7.1). 타입 검사가 내는 문구는 어느 브라우저에서든 같습니다 — 같은 컴파일러가 내는 것이기 때문입니다.

상세 사용법

바깥에서 들어오는 값 다루기

JSON.parse 가 무엇을 돌려줄지 코드는 모릅니다. unknown 으로 받고(1.5) 확인하는 함수를 하나 두는 것이 흔한 방법입니다.

main.ts
const raw: unknown = JSON.parse('{"name":"홍길동"}');

interface User {
	name: string;
}

function isUser(value: unknown): value is User {
	return typeof value === "object" && value !== null && "name" in value;
}

if (isUser(raw)) console.log(raw.name);
else console.log("모양이 다릅니다");
출력
홍길동

반환 타입이 boolean 이 아니라 value is User 입니다. 2.5 에서 "함수로 빼면 좁힌 것이 풀린다" 고 했던 자리인데, 이렇게 적으면 좁혀 줍니다.

if 안쪽에서 raw.name 이 되는 것이 그 증거입니다. 이런 함수를 타입 가드라고 부릅니다.

상세 사용법

붙이고 나면

main.ts
interface User {
	name: string;
	age: number;
}

function adults(users: User[]): string[] {
	return users.filter(u => u.age >= 19).map(u => u.name);
}

console.log(adults([
	{ name: "홍길동", age: 27 },
	{ name: "김철수", age: 15 }
]).join(", "));
출력
홍길동

filtermap 안의 u 에 타입을 적지 않았습니다. 배열의 타입을 한 번 적어 두면 안쪽은 따라옵니다. 이것이 붙이고 난 뒤에 얻는 것입니다.

흔한 실수

처음에 자주 걸리는 것

as 로 넘기는 것

JSON.parse(text) as User 라고 적으면 오류가 사라집니다. 확인한 것이 아니라 그렇다고 우긴 것이라, 실제 모양이 다르면 그대로 터집니다. 1.6 의 ! 와 같은 성격입니다.

한 번에 strict 를 켜는 것

오류가 수백 개 나오면 어디부터 손댈지 알 수 없습니다. noImplicitAny 부터, 그다음 strictNullChecks 처럼 하나씩 켭니다.

any 를 남겨 두고 잊는 것

옮기는 동안 넣은 any표시를 남겨 두세요. // TODO: 타입 붙이기 같은 주석 하나면 나중에 찾을 수 있습니다.

실습 문제

직접 해보기

1. 타입 가드 만들기 난이도 상

{ title: string; pages: number } 모양인지 확인하는 타입 가드를 만들고, 맞으면 제목을 출력하세요.

interface Book {
	title: string;
	pages: number;
}

function isBook(value: unknown): value is Book {
	if (typeof value !== "object" || value === null) return false;
	return "title" in value && "pages" in value;
}

const raw: unknown = JSON.parse('{"title":"입문","pages":320}');

if (isBook(raw)) console.log(raw.title, raw.pages);
else console.log("모양이 다릅니다");
2. any 를 걷어내기 난이도 중

out-any 예제의 any 를 제대로 된 타입으로 바꾸면 어느 줄이 오류로 잡히는지 확인하세요.

let data: { name: string } = … 로 바꾸면 data.nmaedata.age 두 줄이 모두 잡힙니다. 실행하지 않고도 잡힙니다 — 그것이 옮기는 값입니다.
요약

이 단원의 정리

  • 느슨한 설정으로 열고 파일을 하나씩 옮깁니다.
  • any 는 잠시 막아 두는 것이지 끝이 아닙니다.
  • 바깥에서 들어오는 값부터 제대로 된 타입을 붙입니다.
  • value is User 로 적으면 그 함수가 타입을 좁혀 줍니다.
  • as 는 확인이 아니라 우기는 것입니다.