타입 추론(Type Inference)이란 TypeScript가 코드의 값과 문맥을 분석 해서 자동으로 타입을 결정하는 것입니다.

모든 변수에 : string, : number를 붙이지 않아도 TypeScript가 알아서 타입을 잡아 줍니다. 오히려 불필요한 타입 명시는 코드를 장황하게 만들 수 있습니다.

변수 초기화에서의 추론

TYPESCRIPT
// TypeScript가 알아서 타입을 추론
let message = 'hello';   // string
let count = 42;           // number
let isReady = true;       // boolean
let items = [1, 2, 3];   // number[]

// const는 리터럴 타입으로 추론
const greeting = 'hello'; // 'hello' (리터럴 타입)
const limit = 100;         // 100 (리터럴 타입)

letconst의 추론 결과가 다르다는 점이 포인트입니다. let은 값이 바뀔 수 있으니 넓은 타입(string)으로, const는 값이 고정이니 좁은 타입('hello')으로 추론합니다.

함수 반환 타입 추론

TYPESCRIPT
// 반환 타입을 명시하지 않아도 추론됨
function add(a: number, b: number) {
  return a + b; // 반환 타입: number
}

// 여러 경로가 있으면 유니온으로 추론
function getResult(success: boolean) {
  if (success) {
    return { data: '성공' };       // { data: string }
  }
  return { error: '실패' };        // { error: string }
}
// 반환 타입: { data: string } | { error: string }

그런데 반환 타입은 명시하는 게 좋다

공부하다 보니 "함수 반환 타입은 추론에 맡겨도 될까?"라는 질문이 자주 나옵니다.

TYPESCRIPT
// ❌ 추론에만 의존 — 리팩터링 시 반환 타입이 의도치 않게 변할 수 있음
function fetchUser(id: number) {
  // 나중에 누군가 return 문을 빼먹으면 반환 타입이 바뀜
  if (id > 0) {
    return { name: '홍길동' };
  }
  // undefined가 반환될 수 있음
}

// ✅ 명시적 반환 타입 — 의도를 명확히 하고 실수를 방지
function fetchUserSafe(id: number): { name: string } | null {
  if (id > 0) {
    return { name: '홍길동' };
  }
  return null;
}

면접 포인트: 변수 초기화에서는 타입 추론에 맡기고, 함수의 반환 타입과 공개 API는 명시하는 것이 좋은 관례입니다.

Best Common Type (최적 공통 타입)

배열처럼 여러 값이 모일 때 TypeScript는 최적 공통 타입 을 계산합니다.

TYPESCRIPT
// 모든 요소가 같은 타입
const numbers = [1, 2, 3]; // number[]

// 여러 타입이 섞이면 유니온
const mixed = [1, 'hello', true]; // (number | string | boolean)[]

// 클래스 계층에서의 추론
class Animal { name = '' }
class Dog extends Animal { bark() {} }
class Cat extends Animal { meow() {} }

const pets = [new Dog(), new Cat()]; // (Dog | Cat)[]
// Animal[]이 아니라 (Dog | Cat)[]로 추론됨

Animal[]로 추론되길 원한다면 명시적으로 타입을 지정해야 합니다.

TYPESCRIPT
const pets: Animal[] = [new Dog(), new Cat()]; // Animal[]

Contextual Typing (문맥적 타이핑)

TypeScript는 값이 사용되는 문맥 을 보고 타입을 추론하기도 합니다.

TYPESCRIPT
// addEventListener의 콜백에서 event 타입이 자동 추론됨
document.addEventListener('click', (event) => {
  // event: MouseEvent — 명시하지 않아도 추론됨
  console.log(event.clientX, event.clientY);
});

// 배열 메서드에서도 동작
const numbers = [1, 2, 3];
numbers.forEach((num) => {
  // num: number — 자동 추론
  console.log(num.toFixed(2));
});

문맥적 타이핑이 깨지는 경우

TYPESCRIPT
// 함수를 별도로 선언하면 문맥을 잃음
const handler = (event) => {
  // event: any — 문맥이 없으므로 추론 불가
  console.log(event.clientX); // any 접근은 타입 검사 안 됨
};
document.addEventListener('click', handler);

// 해결: 타입을 명시
const typedHandler = (event: MouseEvent) => {
  console.log(event.clientX); // OK
};

타입 넓히기(Type Widening)

let으로 선언하면 TypeScript가 타입을 넓혀서 추론합니다.

TYPESCRIPT
const x = 'hello';  // 타입: 'hello' (리터럴)
let y = 'hello';    // 타입: string (넓혀짐)

const n = 42;       // 타입: 42 (리터럴)
let m = 42;         // 타입: number (넓혀짐)

이는 let 변수는 나중에 다른 값이 할당될 수 있기 때문에 합리적인 동작입니다.

TYPESCRIPT
let status = 'loading'; // string

// 나중에 다른 문자열을 할당해도 OK
status = 'success';
status = 'error';

넓히기를 방지하려면 as const를 사용합니다.

TYPESCRIPT
let status = 'loading' as const; // 타입: 'loading'
// status = 'success'; // ❌ Error

타입을 명시해야 하는 경우 정리

상황추론에 맡겨도 될까?
변수 초기화대부분 OK
함수 매개변수** 반드시 명시** (추론 불가)
함수 반환 타입공개 API는 명시 권장
빈 배열 초기화** 명시 필요** (never[]로 추론됨)
콜백 매개변수문맥에서 추론 가능하면 생략 OK
TYPESCRIPT
// 빈 배열은 반드시 타입 명시
const items: string[] = [];
items.push('hello'); // OK

// 타입 명시 없으면
const items2 = []; // never[] — 아무것도 push 불가

정리

  • TypeScript는 변수 초기화, 함수 반환, 문맥 등에서 자동으로 타입을 추론한다
  • let은 넓은 타입, const는 리터럴 타입으로 추론된다
  • 여러 타입이 섞이면 유니온으로 최적 공통 타입을 계산한다
  • 함수 매개변수는 반드시 타입을 명시해야 하고, 반환 타입은 공개 API에서 명시를 권장한다
  • 불필요한 타입 명시는 피하되, 추론이 의도와 다를 때는 반드시 명시하자
댓글 로딩 중...