ついにTypeScriptの構文がJavaScriptに組み込まれ、ブラウザで直接TypeScriptが動くようになりそうです。
TypeScriptとは何か?
TypeScriptと言っても、指し示す範囲が文脈によって異なります。筆者が観測した範囲では、以下の3つが「TypeScript」だと言われているようです。
- 言語としてのTypeScript(
const a: number = 1
のような構文を持つ言語) - 型チェックをおこなうCLIツール(tscコマンド)
- 型チェックやトランスパイルを行うライブラリ(microsoft/TypeScriptリポジトリ)
この記事で触れるのは、言語としてのTypeScriptの話です。const a: number = 1
のような構文が、ブラウザで動くようになるという事です。
これまではどうだったか?
以下のようなTypeScriptプログラムがあります。
function add(a: number, b: number) { return a - 0 + b; }
このような型付きの構文を使うことで、「型」をもとに静的解析を行い診断情報を出してくれるツールを利用することができます。(tscやtypescript-eslintなど)
このコードを実行するには、事前にJavaScriptに「トランスパイル」(=変換)する必要がありました。
function add(a, b) { return a - 0 + b; }
最近ではトランスパイルを行うツールが多数出現しており、標準化されていない独自構文のサポートやNode.jsとブラウザの非互換性解消などをまとめてやってくれるものも存在しています。
大事なのが、現時点ではまだTypeScriptは1ライブラリが定義した構文でしかないという事です。JavaScriptはWebが続く限り50年でも100年でも続いていきますが(だから堅牢な言語仕様になっている)、TypeScriptはあくまでライブラリの1つであり、時代のトレンドによって流行り廃るものと認識されていました。
しかし、ここに来てTypeScript構文は標準化への道を歩み始めました。衝撃。
これからどうなるか
JavaScriptの仕様に型構文が追加されます。つまり、下のようなコードがブラウザで直接実行できるようになります。
function add(a: number, b: number) { return a - 0 + b; }
ここで注意したいのが、導入されるのはあくまで型構文だということです。
実行時には「型」の部分が単に読み飛ばされます。これはPythonに導入された型構文と同じ方式です。
いわゆる「型チェック」、ソースコードを静的解析して診断情報出す部分は言語仕様に入りません。型チェックは引き続き、3rd-partyのツール・ライブラリに任されます。
考えてみればこれは当然な話です。もし型チェックを実行時に行うことにした場合、ブラウザが型チェックを行うことになります。仮にブラウザからのアクセスが100万回あるとすると、同じコードに対する型チェックがユーザーPC上で100万回行われることになります。これは計算資源のムダです。
言語仕様では型チェック自体の挙動は定義せず、型構文のみを言語仕様に入れるという判断は、賢明でしょう。
導入される/されない構文
proposalを読むと、全てのTypeScript構文が導入されるわけではないようです。
導入される構文
主なものを挙げます。
// 型注釈
let x: string;
// 型宣言(interface)
interface Person {
name: string;
age: number;
}
// 型宣言(type)
type CoolBool = boolean;
// 関数の型注釈
function equals(x: number, y: number): boolean {
return x === y;
}
// オプション引数(`?`キーワード)
function split(str: string, separator?: string) {
// ...
}
// ジェネリクス
function foo<T>(x: T) {
return x;
}
// thisパラメータ
function sum(this: SomeType, x: number, y: number) {
// ...
}
// クラス内で使用される型構文
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
getGreeting(): string {
return `Hello, my name is ${this.name}`;
}
}
// 型のexport
export type SpecialBool = boolean;
export interface Person {
name: string;
age: number;
}
export type { SomeLocalType };
// 型のimport
import type { Person } from "schema";
// `as`キーワード
const point = JSON.parse(serializedPoint) as ({ x: number, y: number });
// 非nullアサーション(`!`キーワード)
document.getElementById("entry")!.innerText = "...";
導入されない構文
// JSX
<div></div>
// enum
enum Direction {
Up,
Down,
Left,
Right,
}
// namespace
namespace Foo {
export class Bar {}
}
// コンストラクタ内のプロパティ宣言
class Octopus {
constructor(readonly name: string) {}
}
// アンビエント宣言(いわゆる`d.ts`ファイル)
declare let x: string;
// 関数のオーバーロード
function foo(x: number): number
function foo(x: string): string;
function foo(x: string | number): string | number {
if (typeof x === number) {
return x + 1
}
else {
return x + "!"
}
}
// クラスフィールド修飾子(privateやreadonlyなど)
class Point {
public readonly x: number
protected x: number
private x: number
abstract foo(){}
override foo(){}
}
導入される構文は以下のような基準で選ばれているようです。
- コメント扱いし、読み飛ばすだけでよいもの
- 将来的な構文の追加にも対応できるもの
JSXやenumは関数やオブジェクトへの変換が必要なため不可、クラスフィールド修飾子は将来的な修飾子の追加に対応するため、別構文で導入が検討されています。
ここで紹介したものが全てではないので、詳しく知りたい方はproposalリポジトリをご覧ください。
また、現在は提案の初期段階のため、最終的にどの構文が導入されるかはまだわかりません。
いつ導入されるのか
実は過去にもJavaScriptに型構文を導入しようという動きはあったのですが、色々あって頓挫したらしいです。今回の提案も難しい議論になるのかもしれません。
しかし、今回提案を主導しているのは、TypeScriptチーム、つまりmicrosoft社の人たちです。またWeb仕様に積極的な提案を行ってきたDenoもこの提案を支持しているようで、(調整力的な意味で)今回の提案が通る可能性は十分あると思います。
ただ、例によって言語仕様への導入はStage0から始まり議論を経てState4まで上がる必要がある上、全モダンブラウザに実装されるまでにも時間がかかります。
そうなると、ブラウザで安心してTypeScript構文が使えるようになるには5年から10年くらいかかるんじゃないか?と勝手に思っています。
注意点
高速化されるわけではない
ここの勘違いが多いようなので一応書いておきます。
上で書いた通り、型構文はコメント扱いで、実行時には読み飛ばされます。型構文が導入されても実行速度は高速化されません。
そもそも静的型付け言語!==コンパイル言語です。TypeScriptは静的型付け言語ですが、機械語にコンパイルされて実行されるわけではありません。そのため高速化方面の話とは全く関係ありません。(ブラウザ上で動くコンパイル言語を作ろうという取り組みは、みなさんご存知WebAssemblyが該当します。)
ちなみに、実行速度が遅くなることもないと思います。TC39はこのあたりがかなり厳格なようで、パーサーが無限先読みする必要がある構文はまず導入されません。
あくまでTypeScriptとは別物
導入されるのはTypeScriptを元にした構文です。しかし、TypeScriptがそのまま動くようになるわけではありません。
拡張子は.js
になりそうですし、構文自体も「このタイミングでTS構文のイケてない所を修正してしまおう」という意見もあるのでTSとは別構文になる可能性があるくらいです。
そもそもこの提案はPythonやPHPなどの「動的型付け言語への型システム導入」という近年の流れにJavaScriptも乗っかった、くらいの位置づけでしょう。その時にたまたまTypeScriptが有名だったから構文の参考にされている、またはアクセス数稼ぎのバズワードにされている(この記事もそう)程度だなという認識です。(そういう意味ではタイトル詐欺ですね。)
なので、これを機に人気の拡張構文(JSXとか)が今後ビシバシ導入されていくことは考えにくいです。
導入後のJavaScriptエコシステム
今まではTypeScript(言語)の機能の大部分をTypeScript(ライブラリ)に依存してきました。しかし、このproposalが導入されるとエコシステムが大きく変わることが予想されます。
コード診断系ツールの再編
今までは型チェックを行うtsc、型情報をもとに追加の診断情報を出すtypescript-eslint、型情報なしで診断情報を出すeslintという役割分担がありました。しかし、rust/wasmの躍進により後発の型チェックツールが出てくると、「コードを解析して診断を出す」役割に対して、各種ツールの再編、廃統合が進んでいくと筆者は考えています。swc作者によるGo言語製型チェッカや、リンターなど各種ツールを統合するrome、TypeScriptを直接実行できるDenoにも注目です。
JavaScript世界の単純化
この10年でJavaScriptの世界は複雑化しました。Pythonで例えると
「python1とpython2とpython3とpython Aとpython Bとpython Cがあり、それぞれ構文やAPIが違う。歴史的な経緯でpython Cで書いたコードをpython Bに変換して動かすのが主流だが、その変換ツールは10種類くらいある。」
という状況です(Pythonで例えたことに特に意味はありません)。複雑なツールを使いこなしてこそ1人前のフロントエンドエンジニアだと言う人もいるかもしれませんが、僕は嫌です。
このような狂った状況が生まれた背景には、JSXやCSS Modulesやdecoratorなど、ライブラリが各々勝手に言語を拡張してきた過去があります。しかし、prototype拡張と同様、非標準の構文を拡張して使っていくのはもはや限界です。
一方最近では、decoratorやCSS Modulesなどの標準化作業が進行中で、JavaScriptの言語仕様が一つにまとまりつつあります。この記事で紹介した型構文の言語仕様入りも、こうした流れの一環と見ていいでしょう。
これからの10年は、複雑化したJavaScriptの世界が1つに統合されていく方向に向かっていくと思います。これはJavaScriptという言語が進む正しい道だと思います。
型システムの進化
この提案はいわば「構文は標準仕様で定義するから、型チェックは各自ライブラリでやってね」という形になっています。ということは、型チェックを行うツールがTypeScript以外にも出てくることが予想されます。そのようなツールが現在のTypeScriptより高度な診断を出せるとしたら、それを使った開発はもはやTypeScriptだとは言えず、新たなJavaScriptエコシステムになるでしょう。つまり、この提案はTypeScriptからの脱皮、JS型システムが新たなフェーズに入るという大きな可能性を秘めています。