← 전체 글로 돌아가기

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"
  }
}

패턴 정리

같은 에러가 반복된다면:

  1. 에러 코드를 분류한다 (TS2533, TS2322 등)
  2. 가장 많이 나오는 에러부터 고친다
  3. 그 에러에 대한 규칙을 정한다 (예: null 체크 필수)
  4. 타입 정의를 중앙화한다
  5. 팀 전체가 따를 esconfig와 규칙을 설정한다

이렇게 하면 타입 에러로 인한 시간 낭비를 크게 줄일 수 있다.