Microsoftは、オープンソースのプログラミング言語の最新版「TypeScript 4.1」を公開した。さまざまな機能強化が施されており、パフォーマンスも向上している。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
Microsoftは2020年11月19日(米国時間)、オープンソースのプログラミング言語の最新版「TypeScript 4.1」を公開した。さまざまな新機能や新しいチェックフラグが導入され、エディタの生産性を高めるアップデートも施されており、速度も向上している。
TypeScriptは静的型付けができる言語であり、JavaScriptのスーパーセットだ。ECMA規格に従った最新のJavaScriptの機能を、古いWebブラウザやランタイムが扱えるようにコンパイルすることもできる。
TypeScript 4.1は、NuGetを使うか、次のコマンドラインのように、npmを使ってインストールできる。
npm install -D typescript
TypeScript 4.1は「Visual Studio 2019」と「Visual Studio 2017」のエディタでサポートされており、「Visual Studio Code」(以下、VS Code)でも利用できる。TypeScript 4.1の主な特徴は次の通り。
テンプレートリテラル型は、テンプレートリテラル表現と同等の型空間だ。テンプレートリテラル表現と同様に、テンプレートリテラル型はバックティックデリミタで閉じられ、「${T}」という形のプレースホルダーを含むことができる。「T」は、「string」や「number」「boolean」「bigint」に代入可能な型を指す。
テンプレートリテラル型は、リテラル文字列を連結したり、非文字列プリミティブ型のリテラルを文字列表現へ変換したり、文字列の大文字小文字を変換したりできる。さらに、テンプレートリテラル型は型推論により、単純な形式の文字列パターンマッチングと文字列分解も可能だ。
テンプレートリベラル型は、次のように変換される。
例えば、`[${A|B|C}]`は、`[${A}]` | `[${B}]` | `[${C}]`に変換される。複数のプレースホルダーに含まれるユニオン型は、クロス積に変換される。例えば、`[${A|B},${C|D}]`は、`[${A},${C}]` | `[${A},${D}]` | `[${B},${C}]` | `[${B},${D}]`に変換される。
例えば、`[${'abc'}]`は`[abc]`に変換され、`[${42}]`は`[42]`に変換される。
幾つかの例を次に示す。
type EventName<T extends string> = `${T}Changed`; type Concat<S1 extends string, S2 extends string> = `${S1}${S2}`; type ToString<T extends string | number | boolean | bigint> = `${T}`; type T0 = EventName<'foo'>; // 'fooChanged' type T1 = EventName<'foo' | 'bar' | 'baz'>; // 'fooChanged' | 'barChanged' | 'bazChanged' type T2 = Concat<'Hello', 'World'>; // 'HelloWorld' type T3 = `${'top' | 'bottom'}-${'left' | 'right'}`; // 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' type T4 = ToString<'abc' | 42 | true | -1234n>; // 'abc' | '42' | 'true' | '-1234'
これまでマップ型は、開発者が提供したキーで新しいオブジェクト型を生成することしかできなかった。だが、入力に基づいて新しいキーを作成したり、キーを除去したりしたい場合も多い。
そこでTypeScript 4.1では、新しい「as句」によって、マップ型のキーをリマッピングできるようになった。
type MappedTypeWithNewKeys<T> = { [K in keyof T as NewKeyType]: T[K] // // This is the new syntax! }
この新しいas句により、テンプレートリテラル型のような機能を利用して、古いプロパティ名を基にプロパティ名を簡単に作成できるようになった。
type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] }; interface Person { name: string; age: number; location: string; } type LazyPerson = Getters<Person>;
また、neverを作成してキーを除去することも可能だ。つまり、場合によってはOmitヘルパー型を使わずに済む。
// Remove the 'kind' property type RemoveKindField<T> = { [K in keyof T as Exclude<K, "kind">]: T[K] }; interface Circle { kind: "circle"; radius: number; } type KindlessCircle = RemoveKindField<Circle>; // same as // type KindlessCircle = { // radius: number; // };
TypeScript 4.1では、条件型の一部の制限が緩和された。これにより、条件型が分岐内で自身を直接参照できるようになり、再帰的な型エイリアスを作成しやすくなった。
例えば、ネストされた配列の要素型を取得する型を作成したい場合、次のようなdeepFlatten型を作成できる。
type ElementType<T> = T extends ReadonlyArray<infer U> ? ElementType<U> : T; function deepFlatten<T extends readonly unknown[]>(x: T): ElementType<T>[] { throw "not implemented"; } // All of these return the type 'number[]': deepFlatten([1, 2, 3]); deepFlatten([[1], [2, 3]]); deepFlatten([[1], [[2]], [[[3]]]]);
同様に、Promiseを深くアンラップするために、Awaited型を作成することも可能だ。
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T; /// Like `promise.then(...)`, but more accurate in types. declare function customThen<T, U>( p: Promise<T>, onFulfilled: (value: Awaited<T>) => U ): Promise<Awaited<U>>;
TypeScript 4.1では、「--noUncheckedIndexedAccess」という新しいフラグが導入された。この新しいモードでは、全てのプロパティアクセス(foo.barのような)やインデックス付きアクセス(foo["bar"]のような)が、潜在的に未定義と見なされる。
interface Options { path: string; permissions: number; // Extra properties are caught by this index signature. [propName: string]: string | number; } function checkOptions(opts: Options) { opts.path // string opts.permissions // number // These are all allowed too! // They have the type 'string | number'. opts.yadda.toString(); opts["foo bar baz"].toString(); opts[Math.random()].toString(); }
上の例では「opts.yadda」が「string | number型」ではなく、「string | number | undefined型」を持つということだ。そのプロパティにアクセスする必要がある場合は、まずその存在をチェックするか、非nullアサーションオペレーター(後置の「!」)を使う必要がある。
// Checking if it's really there first. if (opts.yadda) { console.log(opts.yadda.toString()); } // Basically saying "trust me I know what I'm doing" // with the '!' non-null assertion operator. opts.yadda!.toString();
--noUncheckedIndexedAccessを使った場合、配列へのインデックス付きアクセスが、境界チェックループでも、より厳密にチェックされる。
function screamLines(strs: string[]) { // this will have issues for (let i = 0; i < strs.length; i++) { console.log(strs[i].toUpperCase()); // ~~~~~~~ // error! Object is possibly 'undefined'. } }
インデックスが不要な場合は、for-ofループやforEach呼び出しを使って、個々の要素を繰り返すことができる。
function screamLines(strs: string[]) { // this works fine for (const str of strs) { console.log(str.toUpperCase()); } // this works fine strs.forEach(str => { console.log(str.toUpperCase()); }); }
このフラグは、境界外エラーを捉えるのに便利だが、多くのコードではノイジーかもしれない。そのため、--strictフラグでは自動的に有効化されない。
パスマッピングを有効にするためにpathsを指定するには、baseUrlオプションも指定する必要があったが、baseUrlなしでpathsオプションを使えるようになった。
これまではcheckJsとともにallowJsを設定する必要があったが、checkJsがデフォルトでallowJsを含意するようになった。
「react-jsx」と「react-jsxdev」とう新しいjsxコンパイラオプションにより、JavaScriptライブラリ「React 17」のjsxとjsxsファクトリー関数がサポートされた。これらのオプションはそれぞれ、本番用コンパイルと開発用コンパイルに対応している。
TypeScriptとJavaScript用エディタにおけるJSDocの@seeタグのサポートが強化され、このタグに続くドット名で定義へのジャンプのような機能を利用できるようになった。
// @filename: first.ts export class C { } // @filename: main.ts import * as first from './first'; /** * @see first.C */ function related() { }
Copyright © ITmedia, Inc. All Rights Reserved.