# Never 타입

## Never

> [Youtube: Never 타입에 대한 동영상 강의](https://www.youtube.com/watch?v=aldIFYWu6xc)
>
> [Egghead: Never 타입에 대한 동영상 강의](https://egghead.io/lessons/typescript-use-the-never-type-to-avoid-code-with-dead-ends-using-typescript)

프로그래밍 언어 디자인에는 *코드 흐름 분석* 을 수행할 때 **자연스럽게** 결과로 나오는 *바닥* 타입이라는 개념이 있습니다. TypeScript도 *코드 흐름 분석* 을 하기 때문에 (😎) 발생 가능성이 없는 것들을 표현할 방법이 필요합니다.

TypeScript가 이 *바닥* 타입을 나타내기 위해 사용하는 것이 `never` 타입입니다. 이 타입이 발생하는 경우들:

* 리턴하지 않는 함수 (e.g. 함수 내용에 `while(true){}`가 들어 있는 경우)
* 항상 예외를 던지는 함수 (e.g. `function foo(){throw new Error('Not Implemented')}` 에서 `foo`의 리턴 타입이 `never`)

당연히 이 어노테이션을 프로그래머가 직접 사용할 수도 있습니다

```typescript
let foo: never; // 오케이
```

그렇지만, *`never` 타입에는 다른 never 타입만* 할당 가능합니다. 예를 들면:

```typescript
let foo: never = 123; // 오류: number 타입은 never에 할당할 수 없음

// 오케이, 함수의 리턴 타입이 `never`
let bar: never = (() => { throw new Error(`Throw my hands in the air like I just don't care`) })();
```

좋습니다. 그러면 핵심 사용 방법으로 넘어가죠 :)

## 사용 사례: 빠짐없는 검사(Exhaustive Check)

발생 불가능한 상황(never 컨텍스트)에서 never 함수를 호출할 수 있습니다.

```typescript
function foo(x: string | number): boolean {
  if (typeof x === "string") {
    return true;
  } else if (typeof x === "number") {
    return false;
  }

  // never 타입이 없다면 오류 발생 :
  // - 코드 경로 중에 값을 반환하지 않는 경로 존재 (strictNullChecks)
  // - 또는 접근 불가능한 코드 검출
  // 하지만 TypeScript는 `fail` 함수는 `never` 리턴임을 알 수 있고
  // 런타임 안전 / 빠짐없는 검사를 위해 이런 함수를 호출할 수 있음.
  return fail("Unexhaustive!");
}

function fail(message: string): never { throw new Error(message); }
```

그리고 `never`에는 `never`만 할당할 수 있기 때문에 *컴파일 시간*의 빠짐없는 검사 목적으로 사용할 수 있습니다. 이 내용은 [*구별된 유니온* 단원](/typescript-deep-dive/type-system/discriminated-unions.md)에 나와 있습니다.

## `void`와 혼동

함수가 매끄럽게 종료하지 않을 때 `never`가 반환된다고 하면 직관적으로 이것은 `void` 같은 것이라고 생각하기 쉽습니다. 그렇지만 `void`는 하나의 단위이고 `never` 모순(falsum)입니다.

아무것도 *반환*하지 않는 함수는 단위 `void`를 반환하는 것입니다. 하지만 *영원히 리턴하지 않는* 함수 (또는 항상 throw하는 함수)는 `never`입니다. `void`는 할당이 가능한 타입이지만 (`strictNullCheckings`를 끄면) `never`는 절대 `never` 이외에는 할당할 수 없습니다.

## never 반환 함수의 타입 추론

함수 선언시 TypeScript는 기본으로 `void`를 가정합니다, 아래 처럼:

```typescript
// 추론된 리턴 타입: void
function failDeclaration(message: string) {
  throw new Error(message);
}

// 추론된 리턴 타입: never
const failExpression = function(message: string) {
  throw new Error(message);
};
```

물론 명시적인 어노테이션을 추가하면 고칠 수 있습니다:

```typescript
function failDeclaration(message: string): never {
  throw new Error(message);
}
```

이렇게 하는 핵심 이유는 실제 JavaScript 코드와의 호환성 유지입니다:

```typescript
class Base {
    overrideMe() {
        throw new Error("You forgot to override me!");
    }
}

class Derived extends Base {
    overrideMe() {
        // Code that actually returns here
    }
}
```

`Base.overrideMe` 호출의 경우.

> 실제 TypeScript에서는 `abstract` 함수를 써서 이 문제를 해결할 수 있지만 이 타입 추론은 호환성을 위해 유지되고 있습니다.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://radlohead.gitbook.io/typescript-deep-dive/type-system/never.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
