1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

import typeを省くと何が残るか。tscとesbuildで副作用まで比較する

1
Posted at

import typeを省いても、型としてしか使わないimportが自動で消える設定はあります。一方、verbatimModuleSyntax: trueでは書き方によって副作用の実行まで変わりました。型検査に通るかと、読み込み先が実行されるかを分けて確認します。

特に注意したいのは、次の2つが常に同じ出力になるわけではない点です。

  • import type { Ticket } from "./registry.js"
  • import { type Ticket } from "./registry.js"

読み込み先に目印だけを置く

検証環境は2026年9月11日、macOS arm64、Node.js 24.15.0、TypeScript 7.0.2、esbuild 0.28.2です。コンパイラはstrict: true、target: ES2022、module: ESNext、moduleResolution: Bundler。esbuildはNode向けESM、bundle有効、tree shaking有効で実行しました。テストパッケージにsideEffects: falseは付けていません。

registry.tsには型と、評価されたことを示すログだけを置きます。外部アクセスや実アプリの初期化処理はありません。

export interface Ticket { title: string }
console.log("registry evaluated");

呼び出し元の基本形は次のコードです。

import { Ticket } from "./registry.js";
const ticket: Ticket = { title: "Fix timer" };
console.log(ticket.title);

このimportだけを書き換え、生成JavaScriptと実行ログを比較しました。実際の個人リポジトリでも、型共有パッケージはexport typeでAPIの型を公開しています。今回のTicketとログは、型の参照と実行時の読み込みを切り分けるために用意した最小例です。

書き方と設定で結果が分かれた

表の「評価」は、registry evaluatedが出力されたことを指します。どの成功例も、呼び出し元はFix timerを出力しました。

importの形 verbatim tsc tsc出力でregistry評価 esbuild bundleでregistry評価
import { Ticket } false 成功 なし なし
import type { Ticket } false 成功 なし なし
import { type Ticket } false 成功 なし なし
副作用import + import type false 成功 あり あり
import { Ticket } true TS1484 出力を止めた あり
import type { Ticket } true 成功 なし なし
import { type Ticket } true 成功 あり あり
副作用import + import type true 成功 あり あり

noEmitOnError: trueを指定しているため、TS1484の行ではtscのJavaScriptを実行していません。esbuildはその入力をbundleできましたが、型検査が通ったことにはなりません。

inlineのtypeは空のimportを残した

verbatimModuleSyntax: trueでimport { type Ticket }と書いたケースのtsc出力です。

import {} from "./registry.js";
const ticket = { title: "Fix timer" };
console.log(ticket.title);

Ticketという名前は消えても、import {} from "./registry.js"が残ります。モジュールを評価する依存はあるため、目印のログが出ました。文全体を型専用にしたimport type { Ticket }では、import文ごと消えてログも出ませんでした。verbatimModuleSyntaxの公式説明も、この2つの出力を区別しています。

型だけが必要なら文全体をimport typeにします。初期化も必要な場合は、それを別のimportとして明示できます。

import "./registry.js";
import type { Ticket } from "./registry.js";
const ticket: Ticket = { title: "Fix timer" };
console.log(ticket.title);

この形は今回の両設定と両ツールでログを出しました。ただし、依存パッケージが副作用なしと宣言している場合や、別のbundle設定でも同じになるとは限りません。ここで観測したのはローカルの2ファイルです。

bundleができても型は確かめられていない

export const count: number = "wrong";
console.log(typeof count);

これはtscでTS2322になりましたが、esbuildは成功し、実行結果はstringでした。esbuildのTypeScriptに関する制約でも、型注釈の検査はTypeScriptの型チェッカー側で行うことを説明しています。

型だけの依存を明示することと、型チェックをビルド工程で実行することは別の対策です。今回の再現では、importの削除だけを見れば動く入力でも、tscが検出する誤りをbundle成功だけでは拾えませんでした。

比較入力のT06ケースを実行スクリプトで処理すると、診断・生成JavaScript・ログをまとめて確認できます。アプリへ設定を導入するときも、型専用の参照と初期化目的の読み込みをそれぞれ確認します。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?