Microsoftは、オープンソースのプログラミング言語の最新版「TypeScript 3.9」を公開した。コンパイラの高速化やエディタ機能の強化、コーディング支援などの改良が行われている。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
Microsoftは2020年5月12日(米国時間)、オープンソースのプログラミング言語の最新版「TypeScript 3.9」を公開した。
TypeScriptは、静的型付けができる言語で、JavaScriptのスーパーセットだ。ECMA規格に従った最新のJavaScriptの機能を、古いWebブラウザやランタイムが扱えるようにコンパイルすることもできる。
TypeScript 3.9は、NuGetを使うか、次のコマンドラインのように、npmを使ってインストールできる。
npm install typescript
TypeScript 3.9は「Visual Studio 2019」「Visual Studio 2017」の他、「Visual Studio Code」と「Sublime Text」でも利用できる。TypeScript 3.9の主な特徴は次の通り。
TypeScriptの最近のバージョン(3.7など)では、「Promise.all」や「Promise.race」のような関数の宣言が更新された。だが、これに伴って幾つかの副作用が生じた。特に、nullまたはundefinedの値を組み合わせた場合が顕著だった。
interface Lion { roar(): void } interface Seal { singKissFromARose(): void } async function visitZoo(lionExhibit: Promise<Lion>, sealExhibit: Promise<Seal | undefined>) { let [lion, seal] = await Promise.all([lionExhibit, sealExhibit]); lion.roar(); // uh oh // ~~~~ // Object is possibly 'undefined'. }
これは奇妙な挙動だ。sealExhibitがundefinedを含むことで、lionの型にundefinedが含まれてしまっている。
TypeScript 3.9では、推論プロセスの改良によってこの問題が解決された。上のようなエラーは発生しなくなっている。
TypeScript 3.9では、速度の改善が多数行われた。「material-ui」や「styled-components」のようなパッケージで編集/コンパイルが極めて遅いことが分かったため、TypeScriptチームはパフォーマンス向上に重点的に取り組んだ。大きな結合、交差、条件型、マップ型に関わる問題がある特定のケースを最適化する一連のプルリクエストにより、踏み込んだ改善を図った。
各プルリクエストは、特定のコードベースのコンパイル時間を5〜10%程度短縮する。全体として、material-ui-stylesプロジェクトのコンパイル時間が25%程度短縮されたと、TypeScriptチームは考えている。
エディタによるファイル名変更に時間がかかる場合もあったが、この問題も解決された。
例えば、TypeScriptでライブラリを作成していて、パブリックAPIの一部として、「doStuff」という関数をエクスポートするとする。この関数の型は、他のTypeScriptユーザーが型チェックエラーを取得できるように、2つの文字列を取ると宣言するが、JavaScriptユーザーに有用なエラーを出力するため、ランタイムチェックも行う(開発ビルドにおいてのみかもしれない)。
function doStuff(abc: string, xyz: string) { assert(typeof abc === "string"); assert(typeof xyz === "string"); // do some stuff }
このため、TypeScriptユーザーはこの関数を誤用すると、有用な赤いチルダとエラーメッセージを取得する。JavaScriptユーザーもアサーションエラーを取得する。この挙動をテストするため、ユニットテストを作成する。
expect(() => { doStuff(123, 456); }).toThrow();
残念ながら、テストをTypeScriptで作成すると、TypeScriptはエラーを提供する。
doStuff(123, 456); // ~~~ // error: Type 'number' is not assignable to type 'string'.
そのため、TypeScript 3.9では、「// @ts-expect-error」コメントという新機能が導入された。行の前に// @ts-expect-errorというコメントを配置すると、TypeScriptは、そうしたエラーの報告を抑制する。だが、エラーがない場合は、// @ts-expect-errorが不要だったと報告する。
簡単な例でいえば、次のコードは問題ない。
// @ts-expect-error console.log(47 * "octopus");
だが、次のコードは、
// @ts-expect-error console.log(1 + 1);
次のエラーを発生させる。
Unused '@ts-expect-error' directive.
TypeScript 3.7で、呼び出されない関数のチェック機能が導入され、プログラマーが関数の呼び出しを忘れた場合、エラーが報告されるようになった。
function hasImportantPermissions(): boolean { // ... } // Oops! if (hasImportantPermissions) { // ~~~~~~~~~~~~~~~~~~~~~~~ // This condition will always return true since the function is always defined. // Did you mean to call it instead? deleteAllTheImportantFiles(); }
だが、このエラーが判定されるのは、if内の条件に限られていた。TypeScript 3.9では、この機能が三項条件式(「cond ? trueExpr : falseExpr」構文)でもサポートされるようになった。
declare function listFilesOfDirectory(dirPath: string): string[]; declare function isDirectory(): boolean; function getAllFiles(startFileName: string) { const result: string[] = []; traverse(startFileName); return result; function traverse(currentPath: string) { return isDirectory ? // ~~~~~~~~~~~ // This condition will always return true // since the function is always defined. // Did you mean to call it instead? listFilesOfDirectory(currentPath).forEach(traverse) : result.push(currentPath); } }
TypeScriptコンパイラは、ほとんどの主要なエディタでTypeScript編集エクスペリエンスを支えるだけでなく、「Visual Studio」ファミリーのエディタなどでJavaScriptエクスペリエンスも支える。TypeScript 3.9は、以下のような新しいTypeScript/JavaScript機能を提供する。
JavaScriptファイルでCommonJSモジュールを使った自動インポートが可能になった。
TypeScriptの従来バージョンは、ファイルの種類にかかわらず、ユーザーが以下のようなECMAScriptスタイルのインポートを望んでいると想定していた。
import * as fs from "fs";
だが、JavaScriptファイルの作成時に、誰もがECMAScriptスタイルのモジュールをターゲットにするとは限らない。以下のようなCommonJSスタイルのrequire(...)インポートを利用するユーザーも多い。
const fs = require("fs");
TypeScriptは、ユーザーが利用しているインポートの種類を自動的に検出し、ファイルのスタイルをクリーンな一貫したものに保つようになった。
TypeScriptのリファクタリングやクイック修正では、改行がうまく保持されない場合が多かった。非常に簡単な次の例を見てみよう。
const maxValue = 100; /*start*/ for (let i = 0; i <= maxValue; i++) { // First get the squared value. let square = i ** 2; // Now print the squared value. console.log(square); } /*end*/
エディタで/*start*/から/*end*/までを選択し、新しい関数を抽出すると、次のようなコードになる。
const maxValue = 100; printSquares(); function printSquares() { for (let i = 0; i <= maxValue; i++) { // First get the squared value. let square = i ** 2; // Now print the squared value. console.log(square); } }
これは、理想的なコードではない。forループの各ステートメントの間に空白行があったが、リファクタリングによってそれが削除されてしまっているからだ。TypeScript 3.9は次のように、元のコードの書き方を保持する。
const maxValue = 100; printSquares(); function printSquares() { for (let i = 0; i <= maxValue; i++) { // First get the squared value. let square = i ** 2; // Now print the squared value. console.log(square); } }
関数の最後のステートメントの値を返すのを忘れてしまう場合がある。アロー関数に波括弧(なみかっこ)を追加するときが特にそうだ。
// before let f1 = () => 42 // oops - not the same! let f2 = () => { 42 }
TypeScriptは、欠けているreturnステートメントを追加したり、波括弧を削除したり、オブジェクトリテラルのように見えるアロー関数本体にカッコを追加したりすることで、クイック修正を行えるようになった。
エディタは、ファイルがどの構成ファイルの対象に含まれるかを理解する必要がある。適切なオプションを適用するとともに、他のどのファイルが現在の“プロジェクト”に含まれるかを理解するためだ。デフォルトでは、TypeScriptの言語サーバを利用するエディタは、ファイルの親ディレクトリでtsconfig.jsonを見つけて、これを行う。
これが若干うまくいかない1つのケースがあった。それはtsconfig.jsonが、他のtsconfig.jsonファイルを参照するためにのみ存在する場合だった。
// tsconfig.json { "files": [], "references": [ { "path": "./tsconfig.shared.json" }, { "path": "./tsconfig.frontend.json" }, { "path": "./tsconfig.backend.json" }, ] }
実質的に他のプロジェクトファイルの管理しか行わないこのファイルは、一部の環境では多くの場合、“ソリューション”と呼ばれる。これらのtsconfig.*.jsonファイルは、言語サーバには検出されない。だが、現在の.tsファイルはおそらく、このルートtsconfig.jsonで言及されているプロジェクトの1つに含まれる。言語サーバが、このことを理解することが求められていた。
TypeScript 3.9は、こうした構成での編集シナリオをサポートするようになった。
Copyright © ITmedia, Inc. All Rights Reserved.