TypeScript ’not assignable to parameter of type never’ 오류 완벽 해결 — 빈 배열 타입 추론

1. 문제 정의

TypeScript 코드를 작성하다 보면 다음과 같은 의외의 컴파일 오류를 만날 수 있습니다.

1
2
3
4
const foo = (foo: string) => {
  const result = []
  result.push(foo)  // ❌ 에러 발생 지점
}

위 코드를 컴파일하면 두 번째 줄에서 아래와 같은 오류가 발생합니다.

1
[ts] Argument of type 'string' is not assignable to parameter of type 'never'.

result는 분명히 배열이고, push는 배열의 표준 메서드인데 왜 문자열을 넣을 수 없다는 오류가 나올까요? 겉보기에는 타입스크립트 버그처럼 느껴져서 “이건 버그 아냐?” 싶은 의문이 드는 것이 바로 이 문제의 시작점입니다.

오류 발생 상황 요약

항목내용
오류 메시지Argument of type 'string' is not assignable to parameter of type 'never'.
발생 위치빈 배열 리터럴 [] 선언 후 push() 호출 시점
발생 코드const result = [] + result.push(foo)
관련 타입never 타입, 배열 타입 추론

2. 원인 탐구 — 왜 이 오류가 발생하는가

오류를 이해하려면 TypeScript가 타입 주석 없이 선언된 빈 배열을 어떻게 추론하는지 알아야 합니다.

아래 두 줄을 비교해 봅시다.

1
2
// 상태 A: 빈 배열 리터럴만 존재 → 요소 타입을 알 수 없음
const empty = []

이 경우 TypeScript는 empty 배열에 어떤 타입의 요소가 들어올지 전혀 알 수 없습니다. push를 호출해서 요소를 넣기 전까지는 어떤 값이라도 들어올 수 있다는 신호가 없습니다.

그런데 TypeScript의 타입 시스템은 “무엇이든 될 수 있는” 상태를 그대로 두지 않고, bottom type(바닥 타입)인 never 를 사용하여 배열을 never[]로 추론합니다. never는 “어떤 값도 가질 수 없는 타입"이기 때문에, never[] 타입 배열에는 어떤 요소도 추가할 수 없습니다.

결국 result.push(foo)에서 foo(string)를 never[] 배열의 push 파라미터(never)에 넘기려다 보니 타입 불일치 오류가 발생합니다.

추측이 아니라 확인된 동작

이 원인은 StackOverflow의 채택 답변에서 명시적으로 확인됩니다.

“Without defining the array type, it by default will be ’never’. So when you tried to add a string to it, it was a type mismatch.”

즉, 배열 타입을 정의하지 않으면 기본적으로 never가 되어 문자열을 추가하려는 순간 타입 불일치가 발생한다는 뜻입니다. 이는 버그가 아니라 타입 안전성을 위한 의도된 동작입니다.

3. 근본 원인 분석 — never[]과 타입 추론 규칙

핵심을 명확히 하기 위해 타입 추론 과정을 단계별로 분해해 봅시다.

3-1. never 타입이란

개념설명
never어떤 값도 할당할 수 없는 bottom type
never[]요소가 절대 존재할 수 없는 배열
표현 의미“이 배열에는 아무것도 들어갈 수 없다”

never 타입 변수에는 어떤 값도 대입할 수 없고, never 타입 배열에는 어떤 요소도 push할 수 없습니다.

3-2. 빈 배열의 타입 추론 규칙

TypeScript는 타입 주석 없이 const result = []와 같이 빈 배열 리터럴을 만나면, 요소의 타입을 알 수 없으므로 never[]로 추론합니다. 이는 이후 result.push(foo)처럼 요소를 추가하는 코드가 있어도, 배열 변수 선언 시점의 빈 리터럴 정보를 기준으로 타입이 고정되기 때문입니다.

3-3. 왜 타입을 명시해야 하는가

never[]로 추론된 배열은 push뿐 아니라 요소가 필요한 모든 연산에서 타입 불일치를 일으킵니다. 따라서 배열에 담길 요소 타입을 개발자가 직접 알려줘야 TypeScript가 올바르게 요소를 허용합니다.

4. 코드 해결책 — 단계별 해결 방법

해결책 1: 배열 선언 시 명시적 타입 지정 (권장)

가장 간단하고 명확한 해결책은 빈 배열에 요소 타입을 명시적(annotation)으로 지정하는 것입니다.

1
2
3
4
const foo = (foo: string) => {
  const result: string[] = []   // ✅ string[] 타입 명시
  result.push(foo)              // ✅ 정상 동작
}

string[]로 선언하면 never[]가 아니라 string[]로 추론되므로, push 파라미터 타입이 string이 되어 foo를 문제없이 추가할 수 있습니다.

해결책 2: Array<T> 제네릭 문법 사용

동일한 의미를 제네릭 문법으로도 표현할 수 있습니다.

1
2
const result: Array<string> = []
result.push(foo)   // ✅ 정상 동작

해결책 3: 다른 타입의 배열일 때

result에 담을 요소가 문자열이 아니라면 해당 타입으로 지정하면 됩니다.

1
2
3
4
5
6
const numbers: number[] = []
numbers.push(42)        // ✅ 정상 동작

type User = { id: number; name: string }
const users: User[] = []
users.push({ id: 1, name: "홍길동" })  // ✅ 정상 동작

해결 방법 비교

방법코드특징
명시적 배열 타입const result: string[] = []가장 직관적, 권장
제네릭 문법const result: Array<string> = []동일 의미, 스타일 취향
타입 별칭 배열const users: User[] = []객체 타입 배열에 유용

최종 수정 코드

1
2
3
4
5
const foo = (foo: string) => {
  const result: string[] = []
  result.push(foo)
  return result   // ✅ 반환 타입도 string[]로 추론됨
}

이로써 stringnever에 넣으려다 발생하던 타입 불일치 오류가 사라집니다.

5. 향후 예방 조치 — 같은 오류를 피하는 방법

5-1. 빈 배열 선언 시 타입 주석 습관화

요소가 나중에 추가되더라도 빈 배열을 선언하는 순간 요소 타입을 명시하는 습관을 들이면 이 오류를 원천 차단할 수 있습니다.

1
2
3
4
5
// ❌ bad — never[]로 추론되어 push 불가
const list = []

// ✅ good — 의도를 명확히
const list: string[] = []

5-2. 초기값으로 타입을 유추하게 하기

가능하면 빈 배열 대신 타입을 유추할 수 있는 초기값을 넣어 배선 타입 추론이 올바르게 되도록 합니다.

1
2
3
// ✅ 빈 배열 대신 초기값으로 요소 타입 유추
const list = ["첫 번째 항목"]
list.push("두 번째 항목")   // ✅ string[]으로 추론됨

5-3. 빈 배열을 반환하거나 전달할 때도 타입 지정

함수 반환값이나 파라미터로 빈 배열을 사용할 때도 동일한 원리가 적용되므로 명시적으로 타입을 지정합니다.

1
2
3
4
5
const getNames = (): string[] => {
  const result: string[] = []
  // ... 요소 누적
  return result
}

5-4. ESLint / Lint 규칙 활용

팀 코딩 컨벤션에 따라 “빈 배열 선언 시 타입 주석을 요구"하는 Lint 규칙을 적용하면 이 유형의 실수를 구조적으로 방지할 수 있습니다.

예방 습관설명
타입 주석const result: string[] = [] 형태로 선언
초기값[] 대신 요소를 가진 초기 배열 사용
반환 타입함수 반환 타입에도 배열 요소 타입 명시
Lint 규칙빈 배열 타입 주석 요구 규칙 적용

핵심 정리

TypeScript는 타입 주석 없이 선언한 빈 배열을 never[]로 추론하며, 이 때문에 어떤 요소도 push할 수 없다. 빈 배열 변수에 요소를 추가하려면 반드시 string[]처럼 배열 요소 타입을 명시적으로 지정해야 한다. 이는 타입스크립트의 버그가 아니라 타입 안전성을 위한 의도된 동작이다.

출처