"지울 수 있는 TypeScript 문법만 썼다면 지금 Node는 별도 flag 없이 그 파일을 실행해. 다만 타입을 지우는 것과 타입을 검사하는 것은 전혀 다른 일이야."
Node는 타입을 지운 뒤 JavaScript를 실행해
Type stripping은 Node 22.6에 들어왔고 22.18부터 기본으로 켜졌어. 24.12와 25.2에서 안정화됐고, Node 26에서는 예전 experimental transform flag도 사라졌어. 지금은 실행 흔적을 남기지 않는 타입 문법만 쓴 .ts 파일이라면 그대로 실행할 수 있어.
// hello.ts
type Greeting = { msg: string; from: string };
function greet(g: Greeting): string {
return `${g.from} says: ${g.msg}`;
}
console.log(greet({ msg: 'hi', from: 'Pippa' }));
node hello.ts
# Pippa says: hi
Node가 하는 일은 파일을 읽고 타입 annotation을 걷어 낸 다음 남은 JavaScript를 실행하는 것뿐이야. 타입이 맞는지 검사하지 않고, runtime code가 필요한 TypeScript 기능을 대신 변환하지도 않아. 이름 그대로 type erasure야.
지우기만 해도 되는 문법과 변환이 필요한 문법
그대로 실행할 수 있는 예
- 타입 annotation:
const x: number = 5 - Interface:
interface Foo { x: number } - Type alias:
type Foo = { x: number } - Generic:
function id<T>(x: T): T { return x; } satisfies,asassertion, 명시적인import type
단순 삭제만으로 실행할 수 없는 예
enum X { a, b }처럼 runtime object를 만들어야 하는 enum- 값을 가진 namespace, parameter property, import alias
- 다른 의미의 JavaScript로 변환해야 하는 decorator
- 별도 transformer가 필요한 JSX
Enum과 decorator를 피하는 일반적인 application code라면 type stripping의 범위에 잘 들어와. 반대로 예전 Angular나 NestJS처럼 이런 문법을 적극적으로 쓰는 codebase는 여전히 진짜 TypeScript compiler나 runner가 필요해.
실행은 Node, 검사는 tsc에 맡겨
가벼운 구성은 두 책임을 나누면 돼.
- 지울 수 있는 TypeScript는 plain
node로 실행해. Runtime은 Node가 맡아. - CI와 pre-commit에서는
tsc --noEmit을 돌려 타입을 검사해. JavaScript file은 만들지 않아.
타입 오류는 검사 단계에서 실패하고, runtime 오류는 실행 중에 실패해. 이 범위 안에서는 ts-node, tsx, esbuild-register 같은 import-time 변환 도구를 두지 않아도 돼. 추가 process와 build output이 사라지지만 type safety는 tsc --noEmit으로 그대로 지켜.
확장자가 module 방식을 정해
.mts는 언제나 ESM이야..cts는 언제나 CJS야..ts는 가장 가까운 package.json의type필드를 따라.
새 프로젝트라면 package.json에 "type": "module"을 적고 plain .ts를 쓰는 구성이 단순해. Node와 type checker가 같은 ESM 경계를 보게 되고, 파일마다 module 방식을 다시 추측할 일도 줄어.
내장 기능을 겹치면 개발 command도 짧아져
node \
--env-file=.env \
--watch \
server.ts
이 한 줄이 프로젝트에 따라 ts-node, dotenv, nodemon, esbuild-register, source-map-support가 맡던 흔한 역할을 대신할 수 있어. 물론 그 도구의 추가 기능을 실제로 쓴다면 유지해야 해. 중요한 건 오래전에 필요했던 이유를 지금도 자동으로 상속하지 않는 거야.