TypeScript Deep Dive
  • README
  • 시작하기
    • 왜 타입스크립트인가
  • 자바스크립트
    • 비교 연산자
    • 참조 연산자
    • Null vs. Undefined
    • this
    • 클로저
    • Number
    • Truthy
  • 미래의 자바스크립트
    • 클래스
      • 즉시실행함수
    • 화살표 함수
    • 나머지 연산자
    • let
    • const
    • 비구조화 할당
    • 전개 연산자
    • for...of
    • 이터레이터
    • 템플릿 리터럴
    • 프로미스
    • 제네레이터
    • Async Await
  • 프로젝트
    • 컴파일러 제어
      • tsconfig.json
      • 파일 경로 지정
    • 선언
    • 모듈화
      • 파일을 이용한 모듈화
      • globals.d.ts
    • 네임스페이스
    • 동적 표현식 가져오기
  • Node.js 시작하기
  • Browser 시작하기
  • 타입스크립트 타입 시스템
    • 자바스크립트 마이그레이션 가이드
    • @types
    • 주변 선언
      • 파일 선언
      • 변수
    • 인터페이스
    • 열거형(Enums)
    • lib.d.ts
    • 함수
    • 콜러블(Callable)
    • 타입 표명(Type Assertion)
    • 신선도(Freshness)
    • 타입 가드
    • 리터럴(Literal)
    • 읽기 전용(readonly)
    • 제네릭
    • 타입 인터페이스
    • 타입 호환성
    • Never 타입
    • 구별된 유니온
    • 인덱스 서명(Index Signature)
    • 타입 이동하기
    • 예외 처리
    • 믹스인(Mixin)
  • JSX
    • React
    • Non React JSX
  • Options
    • noImplicitAny
    • strictNullChecks
  • 타입스크립트 에러
    • 에러 메세지
    • 공통 에러
  • NPM
  • 테스트
    • Jest
    • Cypress
  • Tools
    • Prettier
    • Husky
    • ESLint
    • Changelog
  • 팁
    • 문자열 Enums
    • 타입 단언
    • 상태 저장 함수
    • 커링
    • 제네릭 타입 예시
    • 객체 타입 설정
    • 유용한 클래스
    • Import / Export
    • 속성 Setters
    • outFile 주의사항
    • 제이쿼리 팁
    • 정적 생성자
    • 싱글톤 패턴
    • 함수 파라미터
    • 토글 생성
    • Import 여러개 하기
    • 배열 생성
    • 생성자에서 타입정의
  • 스타일 가이드
  • 타입스크립트 컴파일러 구조
    • Program
    • AST
      • TIP: Visit Children
      • TIP: SyntaxKind enum
      • Trivia
    • Scanner
    • Parser
      • Parser Functions
    • Binder
      • Binder Functions
      • Binder Declarations
      • Binder Container
      • Binder SymbolTable
      • Binder Error Reporting
    • Checker
      • Checker Diagnostics
      • Checker Error Reporting
    • Emitter
      • Emitter Functions
      • Emitter SourceMaps
    • Contributing
Powered by GitBook
On this page
  • Never
  • 사용 사례: 빠짐없는 검사(Exhaustive Check)
  • void와 혼동
  • never 반환 함수의 타입 추론

Was this helpful?

  1. 타입스크립트 타입 시스템

Never 타입

Previous타입 호환성Next구별된 유니온

Last updated 3 years ago

Was this helpful?

Never

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

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

  • 리턴하지 않는 함수 (e.g. 함수 내용에 while(true){}가 들어 있는 경우)

  • 항상 예외를 던지는 함수 (e.g. function foo(){throw new Error('Not Implemented')} 에서 foo의 리턴 타입이 never)

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

let foo: never; // 오케이

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

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 함수를 호출할 수 있습니다.

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); }

void와 혼동

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

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

never 반환 함수의 타입 추론

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

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

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

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

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

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

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 함수를 써서 이 문제를 해결할 수 있지만 이 타입 추론은 호환성을 위해 유지되고 있습니다.

그리고 never에는 never만 할당할 수 있기 때문에 컴파일 시간의 빠짐없는 검사 목적으로 사용할 수 있습니다. 이 내용은 에 나와 있습니다.

Youtube: Never 타입에 대한 동영상 강의
Egghead: Never 타입에 대한 동영상 강의
구별된 유니온 단원