[[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;
}
```