📚

node v22.7.x で TypeScript をそのまま実行する

2024/08/29に公開

node の最新に近いバージョンで TypeScript の実験的なサポートが入っている。ts-node や tsx に頼らず typescript のスクリプトを実行できる。

https://github.com/nodejs/node/pull/53725

ロードマップ

https://github.com/nodejs/loaders/issues/217

実験的な機能であることは承知の上で、動かしてみる。

tl;dr

# ~/.zshrc
alias nodets="node --experimental-strip-types --experimental-transform-types --experimental-detect-module --no-warnings=ExperimentalWarning"

# run
nodets run.ts

--experimental-strip-types

もっとも単純な実装で、型注釈を除去する。

node --experimental-strip-types run.ts

これはこういうコードを実行できる。

const x: number = 1;
console.log(x);

--experimental-transform-types

--experimental-strip-types では単純な型注釈の除去に留まらない enum, namespace, module が実行できない。これを変形するのが --experimental-transform-types

node --experimental-strip-types --experimental-transform-types run.ts
const enum E {
	A,
	B,
	C = SubEnum.X,
}
namespace xxx {
	export namespace yyy {
		export const zzz = 123;
	}
}

module a {
	export module b {
		export const c = 456;
	}
}

// See https://zenn.dev/uhyo/articles/typescript-module-option
console.log(E.A, xxx.yyy.zzz, a.b.c);

これらは TypeScript の過去の遺物というか、正直対応したからといって積極的に使いたい機能ではないのだが、過去資産を移植していると高い頻度で遭遇するわけで、サポートがないよりはいい。

モジュール解決

型を除去するだけなので、node.js としてのファイル解決ルール等は変わっていない。
なので、モジュールコンテキストでは拡張子省略ができない。

// NG
import { foo } from "./foo";

// OK
import { foo } from "./foo.mts";

なので、 tsconfig.json 側で "moduleResolution": "Bundler""allowImportingTsExtensions": true で拡張子を省略しないよう、辻褄を合わせる必要はある。(ただし node の TSサポートは現状 tsconfig.json を無視している点に注意)

一応 .mts.cts にも対応しているが、挙動の解説は以下の記事に譲る

https://zenn.dev/uhyo/articles/typescript-module-option

自分のユースケース

nodets のエイリアスを作って、これらのオプションを仕込んでおいた。

# ~/.zshrc
alias nodets="node --experimental-strip-types --experimental-transform-types --experimental-detect-module --no-warnings=ExperimentalWarning"

とはいえ、さすがにこれでプロジェクト全体を動かすといった暴挙はしないが、zx の script/ci.ts のようなタスク用のスクリプト書くのにはいいんじゃないだろうか。

// nodets script/ci.ts
import { $ } from 'zx';

$.verbose = true;
await $`echo "Hello, world!"`;

(自分はこういうのは deno で書きがち)

動かなくなっても自分で治す。実験的な機能なので、だめになっても文句は言わない。

実装は swc 実装の amaro 。引数が複雑になるが loader として差し替えることはできる。

https://github.com/nodejs/amaro

動機や目的等の詳細 https://github.com/nodejs/node/blob/main/doc/api/typescript.md

現状、 .tsx や sourcemap は対応していないし、node_modules の他のモジュールの .ts を解決できるわけではないから使い所は限られるが、 zx スクリプトが無茶苦茶書きやすくなったんじゃないだろうか。

Discussion