타입스크립트를 처음 켜면 없던 빨간 줄이 사방에서 튀어나온다. 자바스크립트일 땐 조용하던 코드가 갑자기 잔소리를 해대니, 처음엔 이게 도움인지 훼방인지 헷갈린다. 근데 실무에서 마주치는 에러는 사실 몇 종류로 정해져 있다. 코드 번호(TS2322 같은 것)와 문장만 눈에 익히면, 대부분은 보자마자 뭘 고쳐야 할지 안다.


그래서 자주 만나는 순서대로, 실제 에러 메시지와 고치는 법을 같이 보여주겠다. 전부 내가 직접 밟아본 지뢰들이다.


TS2322: 이 타입은 저 타입에 넣을 수 없다. 대입할 때 왼쪽이 기대하는 타입과 오른쪽 값의 타입이 안 맞으면 이게 뜬다. 제일 기본이자 제일 자주 보는 에러다.


let count: number = 0;
count = "3";
// error TS2322: Type 'string' is not assignable to type 'number'.


메시지를 그대로 읽으면 된다. stringnumber 자리에 못 넣는다는 뜻이다. 서버에서 받은 값이 문자열인데 숫자처럼 쓰려다 이걸 자주 만난다. 고치는 건 둘 중 하나다. 진짜 숫자를 넣거나, Number("3")으로 바꿔서 넣거나. 십중팔구 타입이 틀린 게 아니라 내 값이 틀린 것이다.


TS2345: 인자 타입이 매개변수와 안 맞는다. 위와 사실상 같은 문제인데, 이번엔 함수에 값을 넘길 때다. 함수가 숫자를 받기로 했는데 문자열을 넘기면 이렇게 잡힌다.


function double(x: number) {
return x * 2;
}
double("5");
// error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.


Argument(넘긴 인자)가 parameter(받는 매개변수)와 안 맞는다는 말이다. TS2322가 대입 상황이라면 이건 함수 호출판이라고 보면 된다. 나는 입력창에서 받은 값을 그대로 계산 함수에 넘겼다가 이걸 봤다. input.value는 항상 문자열이라, 숫자 함수에 넣기 전에 변환부터 했어야 했다.


TS2339: 그 속성은 그 타입에 없다. 객체에 없는 속성을 찍으면 뜬다. 오타이거나, 타입을 잘못 알고 있거나 둘 중 하나다.


const user = { id: 1, name: "kim" };
console.log(user.email);
// error TS2339: Property 'email' does not exist on type '{ id: number; name: string; }'.


메시지가 객체의 실제 모양까지 { id: number; name: string; }로 보여준다. email은 거기 없다. 자바스크립트였으면 undefined가 나오고 넘어갔다가 어디선가 런타임에 터졌을 거다. 오타면 고치고, 진짜 있어야 할 필드면 타입 정의에 그 필드를 추가하면 된다.


TS18048 / TS18047: possibly undefined, possibly null. 초보가 제일 자주, 제일 오래 헤매는 에러다. 값이 없을 수도 있는데 확인 없이 바로 썼다는 뜻이다. undefined일 수 있으면 18048, null일 수 있으면 18047이 뜬다.


function greet(name?: string) {
return name.toUpperCase();
}
// error TS18048: 'name' is possibly 'undefined'.


name?: string은 사실 string | undefined다. 안 넘길 수도 있으니 undefined일 가능성이 열려 있고, 그 상태로 .toUpperCase()를 찍으니 막힌다. if (name)로 걸러주거나 name ?? "손님"으로 기본값을 채우면 통과한다.


배열의 find도 단골이다. 조건에 맞는 게 없으면 undefined를 주기 때문이다.


const nums = [1, 2, 3];
const found = nums.find(n => n > 1);
console.log(found.toFixed(2));
// error TS18048: 'found' is possibly 'undefined'.


나는 이걸 처음에 "분명히 있는데 왜?"라며 억지로 as number로 지워버렸다가, 나중에 조건이 바뀌어 진짜 undefined가 오면서 그대로 터졌다. 타입스크립트가 없을 수도 있다는데 우기지 말고, if (found)로 한 번 걸러주는 게 맞다.


null 쪽은 DOM에서 제일 많이 만난다. querySelector는 못 찾으면 null을 준다.


const el = document.querySelector(".btn");
el.addEventListener("click", () => {});
// error TS18047: 'el' is possibly 'null'.


반환 타입이 Element | null이라 곧바로 점을 찍으면 막힌다. 옛날에 셀렉터 오타 하나로 페이지가 통째로 죽은 적이 있는데, 이 검사가 켜져 있었으면 저장하기도 전에 알았을 일이다. if (el)로 감싸면 그 안에서 null이 빠진 걸로 좁혀져 통과한다.


TS7006: 매개변수가 암묵적으로 any다. 함수 매개변수에 타입을 안 적으면, 타입스크립트가 그걸 몰래 any(아무거나)로 두는 대신 에러로 잡는다. strict가 켜져 있으면 기본으로 걸린다.


function double(x) {
return x * 2;
}
// error TS7006: Parameter 'x' implicitly has an 'any' type.


x: number라고 타입을 적어주면 사라진다. 귀찮아 보여도 이 한 줄이 double("3") 같은 실수를 앞의 TS2345로 잡아주는 출발점이다. 타입을 안 적으면 그 뒤 검사가 통째로 꺼진다는 걸 기억하면, 이 잔소리가 오히려 고맙게 들린다.


TS7053: 문자열로 객체를 인덱싱하면 any가 샌다. 객체를 변수 키로 접근할 때 자주 튀어나오는데, 메시지가 길어서 처음 보면 당황한다.


const scores = { math: 90, eng: 80 };
const key: string = "math";
console.log(scores[key]);
// error TS7053: Element implicitly has an 'any' type because expression
// of type 'string' can't be used to index type '{ math: number; eng: number; }'.


풀어 읽으면 이렇다. key가 그냥 string이라 아무 문자열이나 될 수 있는데, scores에는 matheng밖에 없으니 결과 타입을 any로 흘려보내기 싫다는 것이다. key의 타입을 keyof typeof scores로 좁혀주면, 진짜 있는 키만 허용되면서 결과 타입도 number로 깔끔하게 잡힌다.


TS2367: 이 비교는 의미가 없다. 유니온 타입이나 리터럴을 비교할 때 오타를 내면, 겹치는 값이 없다며 이렇게 알려준다. 조용히 false로 굴러가던 자바스크립트 버그를 대놓고 잡아주는 고마운 에러다.


type Status = "loading" | "done";
const s: Status = "loading";
if (s === "loadnig") {}
// error TS2367: This comparison appears to be unintentional because the types
// '"loading"' and '"loadnig"' have no overlap.


loadnig라고 오타를 냈는데, s"loading" | "done"뿐이라 그 오타값과는 절대 같아질 수 없다. 자바스크립트였으면 저 조건은 영원히 false라 아무 일도 안 일어나고, 왜 안 되는지 몰라 한참 헤맸을 거다.


에러 코드는 겁내지 말고 그냥 읽어라. TS 뒤의 숫자는 검색용 꼬리표일 뿐이고, 진짜 정보는 그 뒤 영어 문장에 다 있다. not assignable이면 타입이 안 맞는 거고, does not exist면 없는 걸 찾는 거고, possibly undefined면 확인 없이 썼다는 거다. 처음엔 낯설어도 같은 문장을 몇 번 만나면 메시지만 보고 손이 먼저 움직인다. 빨간 줄은 나를 막는 벽이 아니라, 런타임에 터질 사고를 컴파일 단계로 앞당겨 주는 알림이다. 성가셔도 이게 결국 새벽 호출을 줄여준다.