[[TypeScript 3.9]]の破壊的変更について。 ## Parsing Differences in Optional Chaining and Non-Null Assertions [[オプショナルチェーン (JavaScript)|オプショナルチェーン]]と[[非nullアサーション演算子 (TypeScript)|非nullアサーション演算子]]の組み合わせに対する解釈が直感的になった。 以下の文がある。 ```ts foo?.bar!.baz ``` 直感的には`foo.bar`が`undefined`の場合に`undefined`となりそうだが、[[TypeScript 3.8]]では以下のように解釈していた。 ```ts (foo?.bar).baz ``` これでは`foo`が`undefined`のときにruntime errorとなってしまう。[[TypeScript 3.9]]では以下のように解釈が変わった。[[非nullアサーション演算子 (TypeScript)|非nullアサーション演算子]]は不要だったのだ。 ```ts foo?.bar.baz ``` ## } and > are Now Invalid JSX Text Characters [[JSX]]で`}`や`>`をテキストとして使うとエラーが出るようになった。 ```jsx let directions = <span>hoge > huga > huyo</span>; // -> Unexpected token. Did you mean `{'>'}` or `>`? ``` ## Stricter Checks on Intersections and Optional Properties [[オプショナルプロパティ]]を持つ[[交差型 (TypeScript)|交差型]]へのチェックが厳しくなった。 [[TypeScript 3.8]]では型`A`と型`B`どちらかが型`C`に代入可能であれば、型`A & B`は型`C`に代入可能だった。しかし、これは以下のケースで問題が発生する。 ```ts interface A { a: number; } interface B { b: string; } interface C { a?: boolean; b: string; } declare let x: A & B; declare let y: C; y = x; ``` 型`A`は型`C`に代入不可だが、型`B`が型`C`に代入可能であるため、型`A & B`は型`C`に代入可能とみなされる。しかし、型`A`のプロパティ`a`が存在するとき、型`C`のプロパティ`a`と異なる型を持つため問題が発生する。 [[TypeScript 3.9]]はこの点を考慮してエラーをみなせるようになった。 ## Intersections Reduced By Discriminant Properties あり得ない[[交差型 (TypeScript)|交差型]]を`nerver`型として判別できるようになった。たとえば以下のコードがあった場合。 ```ts declare function smushObjects<T, U>(x: T, y: U): T & U; interface Dog { kind: "dog"; } interface Cat { kind: "cat"; } declare let x: Dog; declare let y: Cat; let z = smushObjects(x, y); ``` [[TypeScript 3.8]]では`z`の型は`Dog & Cat`と推論される。その場合、`Dog`と`Cat`の[[交差型 (TypeScript)|交差型]]における`kind`の型は`"dog" & "cat"`と推論される。しかし、`"dog" & "cat"`はあり得ない型であるため矛盾が生じる。 [[TypeScript 3.9]]ではこれを`never`型として推論できるようになったので、早期に問題を検知できる。 ## Getters/Setters are No Longer Enumerable getterやsetterがEnumerableではなくなった。これは[[ECMAScript]]の仕様がそのようになっているため。 ```ts class Human { constructor(public id: number, public name: string) {} get full(): string { return `${this.id}: ${this.name}`; } } const taro = new Human(100, "taro"); for (const x in taro) { console.log(x); } ``` 出力されるプロパティはそれぞれ以下の通り。 | [[target (tsconfig)|target]] | [[TypeScript]]バージョン | 出力 | | ------------ | ------------------------ | ------------------ | | `es5` | [[TypeScript 3.8]] | `id` `name` `full` | | `es2015`以上 | [[TypeScript 3.8]] | `id` `name` | | `es5` | [[TypeScript 3.9]] | `id` `name` | | `es2015`以上 | [[TypeScript 3.9]] | `id` `name` | `es2015`以上で`full`が表示されないのは[[target (tsconfig)|target]]バージョンがClassに対応しているから。`es5`では[[トランスパイル]]後の[[JavaScript]]コードが[[Object.defineProperty]]を使うため、`enumerable`の`true/false`によって挙動が変わる。 ## Type Parameters That Extend any No Longer Act as any [[any型]]を継承したType Parametersが[[any型]]として扱われなくなった。 ```ts function foo<T extends any>(arg: T) { arg.hoge; // -> error TS2339: Property 'hoge' does not exist on type 'T'. } ``` [[TypeScript 3.8]]ではエラーにならなかった。 ## export * is Always Retained [[TypeScript 3.8]]では、`export *`が実際には何もexportしていない場合に[[トランスパイル]]した[[JavaScript]]コードから記述を削除していた。これは[[Babel]]を使う場合に問題となるため[[TypeScript 3.9]]では記述が残るようになった。 たとえば以下の[[TypeScript]]コードがあるとき。 `main.ts` ```ts console.log("hoge"); ``` `module.ts` ```ts export * from "./submodule"; ``` `submodule.ts` ```ts export {}; ``` [[TypeScript 3.8]]では`module.js`は以下のように[[トランスパイル]]されていた。 ```js "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); //# sourceMappingURL=module.js.map ``` [[TypeScript 3.9]]では以下のようになっている。 ```js "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __exportStar = (this && this.__exportStar) || function(m, exports) { for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); }; Object.defineProperty(exports, "__esModule", { value: true }); __exportStar(require("./submodule"), exports); //# sourceMappingURL=module.js.map ``` ## More libdom.d.ts refinements [[lib.dom.d.ts]]から一部の型が削除された。どうしても必要な場合はambient定義ファイル(`*.d.ts`)を作成すれば回避できる。 削除された型は以下3つ。 ```ts interface AudioTrackList { [Symbol.iterator](): IterableIterator<AudioTrack>; } interface HTMLVideoElement { readonly audioTracks: AudioTrackList msFrameStep(forward: boolean): void; msInsertVideoEffect(activatableClassId: string, effectRequired: boolean, config?: any): void; msSetVideoRectangle(left: number, top: number, right: number, bottom: number): void; webkitEnterFullScreen(): void; webkitEnterFullscreen(): void; webkitExitFullScreen(): void; webkitExitFullscreen(): void; msHorizontalMirror: boolean; readonly msIsLayoutOptimalForPlayback: boolean; readonly msIsStereo3D: boolean; msStereo3DPackingMode: string; msStereo3DRenderMode: string; msZoom: boolean; onMSVideoFormatChanged: ((this: HTMLVideoElement, ev: Event) => any) | null; onMSVideoFrameStepCompleted: ((this: HTMLVideoElement, ev: Event) => any) | null; onMSVideoOptimalLayoutChanged: ((this: HTMLVideoElement, ev: Event) => any) | null; webkitDisplayingFullscreen: boolean; webkitSupportsFullscreen: boolean; } interface MediaError { readonly msExtendedCode: number; readonly MS_MEDIA_ERR_ENCRYPTED: number; } ```