TypeScript ’not assignable to parameter of type never’ 오류 완벽 해결 — 빈 배열 타입 추론
1. 문제 정의
TypeScript 코드를 작성하다 보면 다음과 같은 의외의 컴파일 오류를 만날 수 있습니다.
| |
위 코드를 컴파일하면 두 번째 줄에서 아래와 같은 오류가 발생합니다.
| |
result는 분명히 배열이고, push는 배열의 표준 메서드인데 왜 문자열을 넣을 수 없다는 오류가 나올까요? 겉보기에는 타입스크립트 버그처럼 느껴져서 “이건 버그 아냐?” 싶은 의문이 드는 것이 바로 이 문제의 시작점입니다.
오류 발생 상황 요약
| 항목 | 내용 |
|---|---|
| 오류 메시지 | Argument of type 'string' is not assignable to parameter of type 'never'. |
| 발생 위치 | 빈 배열 리터럴 [] 선언 후 push() 호출 시점 |
| 발생 코드 | const result = [] + result.push(foo) |
| 관련 타입 | never 타입, 배열 타입 추론 |
2. 원인 탐구 — 왜 이 오류가 발생하는가
오류를 이해하려면 TypeScript가 타입 주석 없이 선언된 빈 배열을 어떻게 추론하는지 알아야 합니다.
아래 두 줄을 비교해 봅시다.
| |
이 경우 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)으로 지정하는 것입니다.
| |
string[]로 선언하면 never[]가 아니라 string[]로 추론되므로, push 파라미터 타입이 string이 되어 foo를 문제없이 추가할 수 있습니다.
해결책 2: Array<T> 제네릭 문법 사용
동일한 의미를 제네릭 문법으로도 표현할 수 있습니다.
| |
해결책 3: 다른 타입의 배열일 때
result에 담을 요소가 문자열이 아니라면 해당 타입으로 지정하면 됩니다.
| |
해결 방법 비교
| 방법 | 코드 | 특징 |
|---|---|---|
| 명시적 배열 타입 | const result: string[] = [] | 가장 직관적, 권장 |
| 제네릭 문법 | const result: Array<string> = [] | 동일 의미, 스타일 취향 |
| 타입 별칭 배열 | const users: User[] = [] | 객체 타입 배열에 유용 |
최종 수정 코드
| |
이로써 string을 never에 넣으려다 발생하던 타입 불일치 오류가 사라집니다.
5. 향후 예방 조치 — 같은 오류를 피하는 방법
5-1. 빈 배열 선언 시 타입 주석 습관화
요소가 나중에 추가되더라도 빈 배열을 선언하는 순간 요소 타입을 명시하는 습관을 들이면 이 오류를 원천 차단할 수 있습니다.
| |
5-2. 초기값으로 타입을 유추하게 하기
가능하면 빈 배열 대신 타입을 유추할 수 있는 초기값을 넣어 배선 타입 추론이 올바르게 되도록 합니다.
| |
5-3. 빈 배열을 반환하거나 전달할 때도 타입 지정
함수 반환값이나 파라미터로 빈 배열을 사용할 때도 동일한 원리가 적용되므로 명시적으로 타입을 지정합니다.
| |
5-4. ESLint / Lint 규칙 활용
팀 코딩 컨벤션에 따라 “빈 배열 선언 시 타입 주석을 요구"하는 Lint 규칙을 적용하면 이 유형의 실수를 구조적으로 방지할 수 있습니다.
| 예방 습관 | 설명 |
|---|---|
| 타입 주석 | const result: string[] = [] 형태로 선언 |
| 초기값 | [] 대신 요소를 가진 초기 배열 사용 |
| 반환 타입 | 함수 반환 타입에도 배열 요소 타입 명시 |
| Lint 규칙 | 빈 배열 타입 주석 요구 규칙 적용 |
핵심 정리
TypeScript는 타입 주석 없이 선언한 빈 배열을
never[]로 추론하며, 이 때문에 어떤 요소도 push할 수 없다. 빈 배열 변수에 요소를 추가하려면 반드시string[]처럼 배열 요소 타입을 명시적으로 지정해야 한다. 이는 타입스크립트의 버그가 아니라 타입 안전성을 위한 의도된 동작이다.
출처
- StackOverflow: What is “not assignable to parameter of type never” error in TypeScript?
- URL: https://stackoverflow.com/questions/52423842/what-is-not-assignable-to-parameter-of-type-never-error-in-typescript
- 채택 답변: 빈 배열에
string[]타입을 명시적으로 지정하는 해결책