import { foo } from "./foo.ts";
のように .ts
を付けてimportするためにはtsconfig.jsonでallowImportingTsExtensionsがtrueになっている必要がある。しかしこのオプションはnoEmitも同時にtrueにしなければならない。その理由はuhyo氏が解説している。トランスパイルしたら.tsファイルじゃなくなるのでimportできなくなって意味ない(そこの辻褄合わせまでtscはやりません)のでトランスパイルしないでくださいということ。
じゃあトランスパイル後に消えていいimport、つまりimport typeならよくね?と思って調べたら、実際そうだった。
// allowImportingTsExtensionsがfalseでもエラーにならない
import type { foo } from "./foo.ts";
https://github.com/microsoft/TypeScript/pull/54746
これに気づいたのは、vibe codingで軽めに作っているアプリケーションにテストを追加しようとしたときだ。現状nodeのビルトインテストフレームワークと--experimental-strip-typesを使うとjestやらvitestやらがなくてもそれっぽいテストが作れる(jestのためにトランスパイルとか、結構複雑だったよね)。
https://blog.koh.dev/2024-10-23-nodejs-builtin-test-typescript/
しかしnodeとして実行するならimportには拡張子が必要であり
拡張子をつけると今度は「allowImportingTsExtensionsをtrueに旋回!」と怒られる。うーん、このルールの意図を考えればimport typeにすれば通るんじゃね?と思ったら通ったという次第。import typeにできる箇所で良かった。というかそうでなければそもそもtscだけではビルドできなかったんだけどな。
nodeのドキュメントにもimport typeはstripすると読める説明が書いてあった。
https://nodejs.org/api/typescript.html#importing-types-without-type-keyword
Due to the nature of type stripping, the type keyword is necessary to correctly strip type imports.
TypeScriptとの微妙な距離感が面白かった。
https://nodejs.org/api/typescript.html#importing-types-without-type-keyword
As in JavaScript files, file extensions are mandatory in import statements and import() expressions: import './file.ts', not import './file'.
The tsconfig.json option allowImportingTsExtensions will allow the TypeScript compiler tsc to type-check files with import specifiers that include the .ts extension.
あくまで拡張子付きのimportという文法が先にあり、それをTypeScriptが許可するかはオプション次第ですね〜というスタンスが感じられ、なるほど言語を作っている方からはそういう見方になるんだなあと思った。