TypeScript
TypeScript 타입 에러가 자꾸 막힐 때
TypeScript에서 반복되는 타입 에러를 줄이고 근본 원인을 찾는 방법을 정리했다.
TypeScript 프로젝트를 하다 보면 같은 종류의 타입 에러가 계속 반복된다. 각 파일에서 다르게 해결하다 보니 코드도 일관성이 없고, 다음 프로젝트에서도 같은 실수를 반복한다.
반복되는 에러의 원인 찾기
# 빌드 로그에서 같은 패턴의 에러를 찾아본다
npm run build 2>&1 | grep "error TS" | sort | uniq -c
# 예시 출력
# 5 error TS2345: Argument of type '...' is not assignable to...
# 3 error TS7006: Parameter '...' implicitly has an 'any' type
# 2 error TS2533: Object is possibly 'null' or 'undefined'
에러 코드별로 개수를 센다. 5개가 나오는 에러가 있다면, 그것부터 해결하면 효율적이다.
타입 에러 분류
1. null/undefined 관련 에러
// ❌ error TS2533
const user = users[0]; // undefined일 수 있음
user.name; // Object is possibly 'undefined'
// ✅ 해결
const user = users[0];
if (user) {
user.name;
}
// 또는
const name = users[0]?.name;
이 에러가 반복되면 strictNullChecks 설정을 다시 확인한다:
// tsconfig.json
{
"compilerOptions": {
"strict": true, // 또는
"strictNullChecks": true
}
}
2. any 타입 사용
// ❌ Parameter implicitly has an 'any' type
function process(data) {
return data.value;
}
// ✅ 타입 명시
function process(data: unknown) {
if (typeof data === 'object' && data && 'value' in data) {
return (data as { value: string }).value;
}
}
설정을 확인한다:
{
"compilerOptions": {
"noImplicitAny": true
}
}
3. 타입 호환성 문제
// ❌ error TS2322
const id: number = "123"; // string은 number에 할당 불가
// ✅ 변환
const id: number = parseInt("123");
// 또는 타입 자체를 맞춘다
const id: string = "123";
타입 정의 중앙화
같은 타입을 계속 정의하지 말고, 한 곳에서 관리한다:
// types/index.ts - 모든 타입을 여기서 정의
export interface User {
id: number;
name: string;
email: string | null;
}
export interface ApiResponse<T> {
data: T;
error?: string;
}
// components/User.tsx - 타입을 import해서 쓴다
import { User, ApiResponse } from '../types';
function UserCard(props: { user: User }) {
return <div>{props.user.name}</div>;
}
tsconfig로 엄격성 조절
프로젝트 시작 시 설정을 정하면 처음부터 일관성 있게 진행할 수 있다:
{
"compilerOptions": {
"strict": true, // 모든 엄격성 옵션 활성화
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true
}
}
ESLint와 함께 사용
# ESLint 설정
npm install --save-dev @typescript-eslint/eslint-plugin
// .eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
rules: {
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/explicit-function-return-types': 'warn',
'@typescript-eslint/no-unused-vars': 'error',
},
};
코드를 작성하면서 실시간으로 에러를 잡을 수 있다.
빌드 전 타입 체크
# 타입만 체크 (빌드하지 않음)
npx tsc --noEmit
# 더 자세한 정보
npx tsc --noEmit --pretty false | head -20
CI/CD에서도 타입 체크를 실행하도록 설정한다:
// package.json
{
"scripts": {
"type-check": "tsc --noEmit",
"test": "npm run type-check && jest"
}
}
패턴 정리
같은 에러가 반복된다면:
- 에러 코드를 분류한다 (TS2533, TS2322 등)
- 가장 많이 나오는 에러부터 고친다
- 그 에러에 대한 규칙을 정한다 (예: null 체크 필수)
- 타입 정의를 중앙화한다
- 팀 전체가 따를 esconfig와 규칙을 설정한다
이렇게 하면 타입 에러로 인한 시간 낭비를 크게 줄일 수 있다.