노트

tsc --noEmit과 타입 검사 분리

noEmit

프런트엔드#ts#tooling · 연결된 개념 5개

쉽게 말하면

tsc --noEmit은 원고를 맞춤법 검사만 하고 인쇄는 하지 않게 하는 옵션이에요. 인쇄는 Vite 같은 빠른 도구에 맡기는데, 그 도구들은 타입을 검사하지 않으니 검사만 따로 돌리는 거죠.

비유가 깨지는 곳 인쇄를 안 한다고 검사가 빨라지진 않아요. 검사 비용은 그대로라 incremental이나 watch로 줄이고, 테스트 러너도 타입을 안 보니 CI나 테스트 스크립트에 따로 넣어야 해요.

--noEmit은 tsc에게 타입 검사만 하고 JavaScript 파일은 만들지 말라고 하는 옵션이다. 오류가 있으면 0이 아닌 종료 코드로 실패하고, 없으면 조용히 끝난다.

  • 왜 필요한가: Vite, esbuild, SWC(Speedy Web Compiler), Babel, Vitest 같은 요즘 도구는 TS의 타입 표기를 지우기만 하고 검사하지 않는다. 그래서 타입 오류가 있어도 개발 서버, 빌드, 테스트가 그대로 돌아간다
  • 그래서 역할을 나눈다. 산출물은 번들러가 만들고, tsc는 검사기로만 쓴다. tsconfig.json에 "noEmit": true를 두는 템플릿이 많다
  • 흔한 함정: 테스트는 통과했는데 build 스크립트(tsc --noEmit && vite build)에서만 실패하는 경우다. 테스트 러너도 타입을 안 보기 때문이다. 테스트 스크립트 앞에 tsc --noEmit을 붙이거나 CI(Continuous Integration)에서 따로 돌려 간극을 없앤다
  • 검사 범위는 tsconfig의 include가 정한다. 테스트 파일도 포함되면 함께 검사된다
  • import './a.ts'처럼 확장자를 쓰게 해 주는 allowImportingTsExtensions는 noEmit(또는 emitDeclarationOnly)일 때만 켤 수 있다. JS를 내보내지 않으니 경로를 고칠 일이 없기 때문이다
  • 파일을 안 쓸 뿐 검사 비용은 그대로다. 자주 돌린다면 --incremental이나 --watch로 줄인다

파일 단위 변환 도구와 맞추기 위한 다른 옵션은 verbatimModuleSyntax와 import type, 전체 옵션은 tsconfig 핵심 옵션. CI에 넣는 방법은 GitLab CI/CD 파이프라인 같은 파이프라인 노트 참고.

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 타입 추론과 타입 검사

    타입 추론(Type Inference)은 표기가 없어도 컴파일러가 값의 타입을 정하는 단계이고, 타입 검사(Type Checking)는 정해진(추론됐거나 명시된) 타입을 코드가 지키는지 확인하는 단계다. 보통 추론이 먼저 일어나고, 그 결과를 기준으로 검사한다.

  • 타입 좁히기와 타입 가드

    타입 좁히기(Narrowing)는 선언된 넓은 타입(유니언, unknown)을 실행 경로를 따라 더 구체적인 타입으로 줄여 가는 과정이다. 그 근거가 되는 런타임 검사를 타입 가드(Type Guard)라고 한다.

  • 템플릿 리터럴 타입

    템플릿 리터럴 타입(Template Literal Types)은 문자열 리터럴 타입을 템플릿 문자열 문법으로 조합해 새 문자열 유니언을 만드는 기능이다. 유니언을 넣으면 가능한 모든 조합이 나온다.

  • 테스트 냄새

    테스트 코드나 테스트 습관에서 나는, 더 깊은 문제를 알리는 신호. 제라드 메스자로스의 xUnit 테스트 패턴 정리가 이름을 붙였다.

  • 매핑된 타입과 keyof

    매핑된 타입(Mapped Types)은 기존 타입의 키를 하나씩 돌며 새 타입을 만드는 문법([K in keyof T]: …)이다. keyof T는 객체 타입의 키 유니언을, T[K](인덱스 접근 타입, Indexed Access Type)는 그 키의 값 타입을 꺼낸다.

보기 옵션