타입스크립트를 쓴다는 건 값마다 어떤 종류인지 이름표를 붙이는 일이다. 그 이름표의 기본 어휘를 여기서 훑는다. 종류는 많아 보여도 실무에서 매일 쓰는 건 몇 개로 추린다. 아래 결과는 전부 실제 타입 검사기를 돌려 나온 그대로다.
표기법은 한 가지만 기억하면 된다. 이름 뒤에 콜론(:)을 찍고 타입을 적는다. 값을 넣는 등호와는 별개다.
기본 세 개는 문자열, 숫자, 참거짓. 가장 많이 만나는 세 타입은 string(문자열), number(숫자), boolean(참이나 거짓)이다. 자바스크립트에서 쓰던 그 값들에 이름만 붙인 것이다.
let title: string = "hello";
let age: number = 20;
let done: boolean = false;
title = 42;
앞 세 줄은 멀쩡하다. 문제는 마지막 줄이다. 문자열 자리인 title에 숫자를 넣으니 이 메시지가 뜬다.
error TS2322: Type 'number' is not assignable to type 'string'.
숫자는 문자열 자리에 못 들어간다는 뜻이다. 참고로 이 타입 표기는 대개 생략해도 된다. let title = "hello"라고만 써도 오른쪽 값이 문자열이니 타입스크립트가 알아서 string으로 정한다(추론). 그래서 표기는 값만 봐선 타입을 알기 어려운 곳, 이를테면 함수 매개변수 같은 데 주로 붙이고 나머진 추론에 맡긴다.
배열은 타입 뒤에 대괄호를 붙인다. 숫자만 담는 배열은 number[], 문자열만 담는 배열은 string[]다. "이 타입이 여러 개"라고 읽으면 된다.
let scores: number[] = [90, 80];
scores.push(70); // 통과
scores.push("70"); // 에러
error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
숫자 배열에 push로 문자열을 밀어넣으려다 걸렸다. 배열에 한 종류만 들어간다고 못박아 두니, 나중에 그 배열을 꺼내 계산할 때 문자열이 섞여 사고 나는 일이 사라진다. 자바스크립트 배열은 아무거나 섞어 담을 수 있어서 편한 만큼 위험했던 부분이다.
튜플은 자리마다 타입이 정해진 배열이다. 배열인데 길이가 고정이고 각 칸의 타입까지 정해진 것을 튜플이라 부른다. 대괄호 안에 칸별 타입을 순서대로 적는다.
let pair: [string, number] = ["age", 20];
pair = [20, "age"];
error TS2322: Type 'number' is not assignable to type 'string'.
error TS2322: Type 'string' is not assignable to type 'number'.
첫 칸은 문자열, 둘째 칸은 숫자로 못박았는데 순서를 바꿔 넣으니 두 칸 모두에서 에러가 났다. 좌표 [x, y]나 "키와 값" 짝처럼 자리마다 의미가 다른 데 쓴다. 자바스크립트로 치면 그냥 배열이지만, 타입스크립트는 칸의 순서와 개수까지 지켜준다.
유니온은 여러 타입 중 하나를 허용한다. 어떤 값이 문자열일 수도 숫자일 수도 있다면, 세로 막대(|)로 후보를 나열한다. 이걸 유니온 타입이라 부른다.
let id: string | number = "abc";
id = 7; // 통과
id = true; // 에러
error TS2322: Type 'boolean' is not assignable to type 'string | number'.
id에는 문자열이든 숫자든 들어갈 수 있지만, 후보에 없는 boolean은 거부당한다. 아이디가 "abc" 같은 문자열일 수도 7 같은 숫자일 수도 있는 실제 상황을 그대로 타입으로 옮긴 것이다. 아무거나 받는 대신 "이것 아니면 저것"으로 딱 좁혀둔다는 게 핵심이다.
리터럴 타입은 정해진 값 자체만 허용한다. 유니온의 후보로 타입 이름 대신 값을 직접 적을 수도 있다. 그러면 그 값들만 들어갈 수 있다.
let dir: "left" | "right" = "left";
dir = "right"; // 통과
dir = "up"; // 에러
error TS2322: Type '"up"' is not assignable to type '"left" | "right"'.
dir은 "left"나 "right" 둘 중 하나여야 한다. 오타로 "up"을 넣자 바로 걸렸다. 방향, 상태값, 옵션처럼 정해진 몇 개 중 하나만 와야 하는 자리에 쓰면, 편집기가 후보를 자동완성으로 띄워주기까지 한다. 문자열을 그냥 string으로 두면 오타든 뭐든 다 통과하지만, 이렇게 값으로 좁히면 오탈자 하나도 못 빠져나간다.
null과 undefined는 값이 없음을 나타낸다. 값이 아직 없거나 비어 있음을 뜻하는 두 타입이다. 엄격 모드(strict, 실무의 표준 설정)에서는 이 둘을 다른 타입 자리에 함부로 넣지 못한다.
let nick: string = null;
error TS2322: Type 'null' is not assignable to type 'string'.
문자열 자리에 null을 넣으려다 걸렸다. 비어 있을 수 있는 값이라면 유니온으로 그 가능성을 명시해야 한다. string | null로 잡아두는 식이다.
let box: string | null = null;
box.toUpperCase();
error TS18047: 'box' is possibly 'null'.
box가 null일 수도 있는데 곧바로 문자열 메서드를 부르니 막는다. if (box !== null)로 null이 아님을 확인한 뒤라야 그 안에서 box.toUpperCase()가 허용된다. 자바스크립트에서 그토록 자주 겪던 'null의 속성을 읽을 수 없다'는 그 런타임 에러를, 타입스크립트는 코드를 짜는 시점에 미리 걸러낸다.
any는 검사를 꺼버리는 비상구, unknown은 그 안전판이다. any는 "이 값은 검사하지 마라"는 뜻이다. 무슨 짓을 해도 통과된다.
let x: any = 5;
x.foo.bar(); // 에러 없이 통과
x는 숫자인데 없는 속성을 줄줄이 타고 들어가 호출까지 해도 타입스크립트는 눈감아준다. 이러면 실행 중에 그대로 터진다. 타입스크립트를 쓰는 이유를 스스로 내다버리는 셈이라, any는 정말 어쩔 수 없을 때만 쓴다. 타입을 진짜 모르는 값에는 대신 unknown을 쓴다.
let y: unknown = "hi";
y.toUpperCase();
error TS18046: 'y' is of type 'unknown'.
unknown은 "타입을 아직 모른다"는 뜻이라, 정체를 확인하기 전엔 아무 조작도 못 하게 막는다. 먼저 if (typeof y === "string")로 문자열임을 확인한 뒤라야 그 안에서 y.toUpperCase()가 허용된다(타입 좁히기). any가 검사를 통째로 끄는 거라면, unknown은 확인을 강제하는 안전한 쪽이다. 정체 모를 값이 들어오면 any 말고 unknown을 잡는 습관을 들이는 게 좋다.
여기까지가 값 하나하나에 붙이는 기본 어휘다. 이 타입들은 낱개로도 쓰지만, 진짜 힘은 여러 개를 묶어 "이 객체는 이런 모양"이라고 통째로 정의할 때 나온다.