Slide 1

Slide 1 text

TypeScript 6.0で非推奨化され るオプションたち 2025-11-23 TSKaigi Hokuriku

Slide 2

Slide 2 text

発表者紹介 uhyo 株式会社カオナビ フロントエンドエキスパート TypeScriptにtype宣言が無かったころから 使っている。 2

Slide 3

Slide 3 text

TypeScriptの最近の動き TypeScriptのネイティブ版(tsgo)の準備は 着々と進んでいる。 ネイティブ版はバージョン7.0としてリリース される見込み。 3

Slide 4

Slide 4 text

次のTypeScriptバージョン 7.0とは別に、次期バージョンとして6.0も準備 されている。 (現在の最新版は5.9) 6.0は、ネイティブ版に向けた準備のための バージョンという側面が大きく、機能追加などは 限定的。 4

Slide 5

Slide 5 text

次のTypeScriptバージョン 最新情報では、6.0は2026年1月~2月リリースを 目指しており、7.0はその約1か月後。 参考: https://github.com/microsoft/TypeScript/issues/62785 5

Slide 6

Slide 6 text

This Talk 6.0ではネイティブ版への準備として、 古いオプションの非推奨化が予定されている。 6.0で非推奨化されたオプションは、7.0では実装 されない(廃止)。 今回は、そんな将来廃止予定のオプションを 解説します。 6

Slide 7

Slide 7 text

でもその前に…… TypeScriptのバージョニング の歴史 7

Slide 8

Slide 8 text

大前提 TypeScriptはセマンティックバージョニング ではない。 5.0はなぜ5.0なのか? 4.9の次のバージョンだから。 8

Slide 9

Slide 9 text

大前提 「あ~TypeScript 5系が出たのか。 うちは4.9だから破壊的変更チェックしなきゃ」 → 違います。4.8→4.9と同じ感覚でアップデート していいです。 9

Slide 10

Slide 10 text

初期のX.0の扱い 初期では、X.0を「大きめの特別なリリース」とし て扱っていた。 2.0はstrictNullChecksなど大きな改善があり、 1.9を飛ばして1.8→2.0となった。 10

Slide 11

Slide 11 text

3.0以降は完全に普通のバージョン TypeScript 2.xの間に現在に近いリリースサイクル が確立。 リリースも定期的(3ヶ月に1回)になり、 X.0は単なる桁の繰り上がりでしかなくなった。 11

Slide 12

Slide 12 text

5.0: deprecationの登場 TypeScript 5.0は、deprecationサイクルの概念が 登場したリリース。 古いオプションの廃止が始まった。 サイクルの開始バージョンという若干の特別性。 参考: https://github.com/microsoft/TypeScript/issues/51000 12

Slide 13

Slide 13 text

5.0のdeprecationサイクル 5.0: 指定すると警告が発生。警告を無視するため にignoreDeprecationsを指定可能 5.5: 指定しても何も起きない(エラーにならないが、 オプションの効果はない) 6.0: 完全廃止(存在しないオプションを指定するとエ ラー) 13

Slide 14

Slide 14 text

そして6.0 6.0は、5.0のサイクルを完遂して次のサイクルが 始まるバージョン。 完遂は7.0となり、一応このサイクルに則って行う ことになっている。 (ただ、6.1~6.9を律儀に全部リリースするのかは不明。 しばらく6系もメンテするという情報はある) 14

Slide 15

Slide 15 text

本題: 6.0で非推奨化される オプションたち 15

Slide 16

Slide 16 text

なぜ非推奨化するのか もう需要がない古いオプションを非推奨化すると いう方針は変わらない。 さらに、ネイティブ版に移植するコードを減らす という意図もありそう。 参考: https://github.com/microsoft/TypeScript/issues/54500 16

Slide 17

Slide 17 text

ご注意 このトークの情報は、GitHubのissue等から得ら れる公式情報をもとにまとめています。 あくまで予定のため、実際のリリース時には内容 が変化する可能性があります。 17

Slide 18

Slide 18 text

①target: es5 •ES5へトランスパイルする機能の廃止 •型定義としての lib: [“es5”] は存続らしい •最小ターゲットはes2015に変更 18

Slide 19

Slide 19 text

target: es5 why? ES2015をサポートしていない環境はもうあまり 実用されていない。 どうしてもES5が必要なら他のツールもある。 19

Slide 20

Slide 20 text

ES5へのトランスパイルは大変 最近の構文に比べると、ES2015は大がかりな トランスパイルが必要になる機能が多め。 (最近でもusingとかはトランスパイルが大変だが……) 20

Slide 21

Slide 21 text

ES5へのトランスパイルは大変 トランスパイル前: function sum(nums: readonly number[]) { let sum = 0; for (const v of nums) { sum += v; } return sum; } 21

Slide 22

Slide 22 text

ES5へのトランスパイルは大変 トランスパイル後: function sum(nums) { var e_1, _a; var sum = 0; try { for (var nums_1 = __values(nums), nums_1_1 = nums_1.next(); !nums_1_1.done; nums_1_1 = nums_1.next()) { var v = nums_1_1.value; sum += v; } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (nums_1_1 && !nums_1_1.done && (_a = nums_1.return)) _a.call(nums_1); } 22

Slide 23

Slide 23 text

ES5へのトランスパイルは大変 トランスパイル前: function* getMenuItems(user: User) { yield "Home"; yield "Search"; if (user.isAdmin) { yield "Admin"; } yield "Settings"; } 23

Slide 24

Slide 24 text

ES5へのトランスパイルは大変 トランスパイル後: function getMenuItems(user) { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, "Home"]; case 1: _a.sent(); return [4 /*yield*/, "Search"]; case 2: _a.sent(); if (!user.isAdmin) return [3 /*break*/, 4]; return [4 /*yield*/, "Admin"]; case 3: _a.sent(); _a.label = 4; case 4: return [4 /*yield*/, "Settings"]; case 5: _a.sent(); return [2 /*return*/]; } }); 24

Slide 25

Slide 25 text

target: es5に関連するオプション downlevelIteratorもあわせて非推奨化。 (ES5へのトランスパイル時の動作を指定する オプションのため) 25

Slide 26

Slide 26 text

targetの新しいデフォルト値 従来のデフォルト値もes5だったが、 リリース時点の最新版が新しいデフォルト値に。 (今リリースしたらes2025) ただし、tsc --initで作成されるtsconfig.jsonの初期状 態は”target”: “esnext”になっている。 26

Slide 27

Slide 27 text

domとdom.iterable lib target: es5の廃止に合わせ、libで指定される domとdom.iterableは実質的に統合される。 (dom.iterable: DOMの型定義のうち、 イテレータ関連のものを抜き出したもの) 27

Slide 28

Slide 28 text

②outFile •outDirではなくoutFileを指定すると、ファイル ごとに.jsを出力するのではなくプロジェクトの ファイルを1つに統合した.jsを出力する。 •原始的なバンドラみたいな機能 •CommonJSやESMとは組み合わせられない 28

Slide 29

Slide 29 text

outFile why? バンドラでいい。 CommonJSやESMと組み合わせられない時点で、 需要もあまり無い。 実装も削減できる。 29

Slide 30

Slide 30 text

③いくつかのmodule値 •module: amd, umd, systemjs •ES Modulesの記法をこれらのモジュールシステ ムに変換して出力する 引き続き指定できる値: none, commonjs, es2015, es2020, es2022, esnext, node16, node18, node20, nodenext, preserve 30

Slide 31

Slide 31 text

いくつかのmodule値 why? TypeScriptの開発当初に比べてAMDやSystemJS 自体の需要が減少している。 変換先が減ることはやはり実装の削減に貢献。 31

Slide 32

Slide 32 text

④moduleResolution: classic •モジュール解決の方式を指定する。 •classicはCommonJSより前の何か 引き続き指定できる値: node10 (node), node16, nodenext, bundler 32

Slide 33

Slide 33 text

moduleResolution: classic why? npm文化となり、大体のランタイムがNode.jsと同様の モジュール解決をするようになった。 (“react” → ./node_modules/react のような基本ルール、 package.jsonのexports フィールドのサポートなど) ただ、Node.jsのESMは拡張子が必要でNode.js 以外と異なるので、そちらに合わせたbundlerが 登場している。 33

Slide 34

Slide 34 text

⑤esModuleInterop, allowSyntheticDefaltImports •__esModuleに対応したemitをするオプション • ESMからトランスパイルされたCommonJSモジュー ルを示すマーカー •esModuleInterop: true の挙動に統一され、 オプションは廃止 34

Slide 35

Slide 35 text

esModuleInterop why? •__esModuleが十分にデファクトスタンダードに なったから •デメリット(余計なランタイムコード)の減少 • ネイティブESMの利用拡大 • TS側でmodule: nodenext等によるCJS/ESM サポートの解像度が上がった 35

Slide 36

Slide 36 text

⑥alwaysStrict: false (?) •“use strict” と書かなくてもソースコードを常に strict modeとして扱うオプション •今はデフォルトfalseだが、“strict”: true に含ま れるのでtrueにしているプロジェクトが多い (デフォルト値をtrueにするだけなのか、alwaysStrict オプション自体を消すのかははっきり決まっていないが、 多分オプションを消す) 36

Slide 37

Slide 37 text

alwaysStrict: false why? パフォーマンス上の理由が説明されている。 • falseだとパース時の先読みが必要なケースが増える • ASTのキャッシュを阻害する要因になる 元々Module形式のファイルでは全てstrict modeなので、 非strict modeの挙動は混乱の元になるという事情もあ るか。 37

Slide 38

Slide 38 text

オプションではないけど 非推奨化されるものたち 38

Slide 39

Slide 39 text

⑦一部のmodule構文 •namespaceは昔moduleという構文だった •その名残で今でもmoduleと書くことができたが、 ついに非推奨化 39 module Foo { export type Bar = number; export const bar: Bar = 0; } namespace Foo { export type Bar = number; export const bar: Bar = 0; }

Slide 40

Slide 40 text

一部のmodule構文: 注意 引き続きmoduleを使う場面もある。 (アンビエントモジュール宣言) 40 declare module “my-foo-module” { type Bar = number; const bar: Bar; }

Slide 41

Slide 41 text

一部のmodule構文 why? そもそもかなり昔にもうnamespaceに変えていた。 加えて、JavaScript側で似たようなmodule構文を 追加する機運がある。 その際TSの独自構文が邪魔にならないように、 独自構文は廃止したい。 41

Slide 42

Slide 42 text

⑧import … asserts •Import AssertionsとしてassertsがTypeScriptなど に実装された後、仕様がwithに変更された •assertsの使用は非推奨に 42 import json from “./foo.json” asserts { type: “json” }; import json from “./foo.json” with { type: “json” };

Slide 43

Slide 43 text

import … asserts why? 仕様上非推奨になったとはいえ一度実装したもの なので互換性が大丈夫か危惧されていたが、 Google ChromeやNode.jsからも実装が削除され、 仕様からもassertsが消されることになり、 さすがに大丈夫そうなので削除される見通し。 43

Slide 44

Slide 44 text

もはや非推奨化でもないけど 挙動が変わるオプションたち 44

Slide 45

Slide 45 text

⑨typesのデフォルト挙動 • “types”: [“node”] のようにどの@typesパッケージを 自動で読み込むか決めるオプション •デフォルトでは「@typesパッケージ全て」 •新しいデフォルトは [] 45

Slide 46

Slide 46 text

typesのデフォルト挙動 why? 本当に全てを読み込む必要のあるプロジェクトは まず無い。大抵はnodeを読み込めば十分。 (あとはjestとか) 気付かないうちにパフォーマンスが落ちることを 防ぐためにデフォルトを変える。 46

Slide 47

Slide 47 text

typesのデフォルト挙動 補足 「型定義の無いパッケージの型定義を補う」系の @typesパッケージはそもそもtypesオプションで 指定する必要がない。(importすれば自動で読み込まれる) typesオプションで指定すべきなのは、主にグロー バル変数を生やす系。 47

Slide 48

Slide 48 text

⑩rootDirのデフォルト値 •プロジェクトのルートディレクトリを決めるオプ ション(outDirがどこからの相対パスになるか等に影響) •従来は「全てのソースファイルを内包するディレク トリ」 •新デフォルトは「tsconfig.jsonがあるディレクトリ」 48

Slide 49

Slide 49 text

rootDirのデフォルト値 why? 全てのソースファイルを列挙しないとrootDirが定まら ないのはパフォーマンスに悪影響だから。 ソースファイルの列挙はimportを辿ることも含むので わりと大変。 49

Slide 50

Slide 50 text

⑪tscにファイル名を渡したときの挙動 • tsc foo.ts のようにすると…… • 従来挙動: tsconfig.jsonを無視してデフォルト設定 で実行 • 新挙動: tsconfig.jsonがある場合はエラーが発生。 --ignoreConfig(仮)を指定すると従来挙動 50

Slide 51

Slide 51 text

tscにファイル名を渡したときの挙動 why? 従来挙動は分かりにくく、意図が不明瞭で需要も ない。 AIがtsc foo.tsを実行して大量のエラーを発生させて 混乱することが多すぎるので、この修正はとても ありがたい…… 51

Slide 52

Slide 52 text

⑫それ以外のオプション修正 •libReplacementのデフォルト値がtrue→falseに • better-typescript-libを使う人は設定が必要(宣伝) •noUncheckedSideEffectImportsのデフォルト 値がfalse→trueに 52

Slide 53

Slide 53 text

もはや非推奨化でもオプショ ンでもないけど挙動が変わる やつら 53

Slide 54

Slide 54 text

⑬enum merging •実は同じファイル内で同じenumを複数回定義す るとマージされた •使われていないので廃止 54 enum E { Foo = 1 } enum E { Bar = 2 }

Slide 55

Slide 55 text

⑭複数namespace間での変数参照 •依然として複数のnamespace定義をマージする ことは可能 •しかし、他のnamespace定義内で定義された変 数を参照している場合の挙動が変わる 55

Slide 56

Slide 56 text

複数namespace間での変数参照: 例 namespace Tax { export const rate = 0.1; } const rate = 0.05; namespace Tax { function applyTax(price: number) { return price * rate; } } 56 このrateは何を指す?

Slide 57

Slide 57 text

複数namespace間での変数参照: 例 namespace Tax { export const rate = 0.1; } const rate = 0.05; namespace Tax { function applyTax(price: number) { return price * rate; } } 57 従来挙動: このrateを指す (別ファイルだとしても!)

Slide 58

Slide 58 text

複数namespace間での変数参照: 例 namespace Tax { export const rate = 0.1; } const rate = 0.05; namespace Tax { function applyTax(price: number) { return price * rate; } } 58 新挙動: このrateを指す

Slide 59

Slide 59 text

複数namespace間での変数参照 why? 変数が暗黙のうちに他のnamespaceの変数を参照 できる仕様だと、namespaceの全ての定義箇所を 調べないと変数参照を解決できない。 しかも、非モジュールでは同じnamespace定義が 他のファイルにある可能性がある。 パフォーマンス的な問題が大きい。 59

Slide 60

Slide 60 text

まとめ 60

Slide 61

Slide 61 text

非推奨化や挙動変更の傾向 •実装の削減(需要が少なくなった機能) •パフォーマンス 特に、「全ファイル見ないと決まらない系」の挙動が 複数廃止されている。 並列処理の妨げになるからかも? 61

Slide 62

Slide 62 text

今後の流れ TypeScript 6.0がリリース予定の数ヶ月後には、 このような非推奨化が来ることが予想される。 しばらくは「6系」のメンテナンスを継続すると されており、今回の非推奨化は6.5まで猶予がある。 ただし、Go版の速さを享受したければより素早い 対応が必要。 62

Slide 63

Slide 63 text

まとめ 対応が必要とはいっても、そんなに激しい破壊的 変更はなく、あくまでもう必要がない機能の 非推奨化にとどまる。 この辺りは互換性を重視するMicrosoftらしい ですね。 63