[TypeScript] 2.7¶
TypeScript2.7のリリース内容まとめ
Constant-named properties¶
HAD BETTER EASY
Symbolを含む、constで宣言された変数のプロパティ名に対応した。
const hoge = Symbol("hogehoge");
const obj = {
[hoge]: "value"
}
// obj.で補完候補が出現し、obj[hoge]はstringと推論される
const str = obj[hoge]
stringやnumberの場合も同様だが、以前は対応していなかったのかは謎。。
unique symbol¶
HAD BETTER NORMAL
symbol
のサブタイプとしてunique symobl
型が追加された。
一意性が保証されており、代入や比較ができないという特徴がある。
利用できるシーン/できないシーンは以下の通り。
シーン | unique symbol? | コード例 |
---|---|---|
const で明示的に宣言 |
unique symbol | declare const x: unique symbol; |
const でSymbol() を代入 |
unique symbol | const x = Symbol(); |
明示的にSymbol() を代入 |
unique symbol | let x: unique symbol = Symbol(); |
明示的にSymbol.for() を代入 |
unique symbol | let x: unique symbol = Symbol.for("key"); |
static readonly のプロパティにSymbol() を代入 |
unique symbol | class Inf { static readonly x = Symbol() } |
const でSymbol.for() を代入 |
symbol | const x = Symbol.for("key"); |
static readonly のプロパティにSymbol.for("key") を代入 |
symbol | class Inf { static readonly x = Symbol.for("key") } |
let で明示的に宣言 |
エラー | declare const x: unique symbol; |
利用例として、Nominal Typingを実現するために付与する一意なプロパティなどが考えられる。
class Dog {
constructor(public name: string) { }
}
class Cat {
constructor(public name: string) { }
}
function nyan(cat: Cat) {
console.log(cat.name);
}
const dog = new Dog("pochi");
// 引数はCatだがdog(: Dog)はCatと同じプロパティを持つため動作する
nyan(dog);
const DogType = Symbol();
class Dog {
private [DogType]: void;
constructor(public name: string) { }
}
const CatType = Symbol();
class Cat {
private [CatType]: void;
constructor(public name: string) { }
}
function nyan(cat: Cat) {
console.log(cat.name);
}
const dog = new Dog("pochi");
// プロパティが違うのでちゃんとエラーにしてくれる
// TS2345: Argument of type 'Dog' is not assignable to parameter of type 'Cat'.
// Property '[CatType]' is missing in type 'Dog' but required in type 'Cat'.
nyan(dog);
Strict Class Initialization¶
SHOULD EASY
--strictPropertyInitialization
フラグが追加された。
有効にすると、クラスのコンストラクタで初期化されていないプロパティがエラーになる。
class Human {
id: number; // <--- これがエラーになる. number | undefined にするか初期化すべき
name: string;
constructor(name: string) {
this.name = name
}
}
Injectionライブラリなどで上記挙動が好ましくない場合、!
を付けることで回避できる。
class Human {
id!: number; // これはエラーにならない
name: string;
constructor(name: string) {
this.name = name
}
}
--strictPropertyInitialization
フラグは--strict
モードであれば自動で有効になる。
--strict
モードでも、--strictPropertyInitialization
フラグを明示的にOFFにすると、このエラーチェックを外すことができる。
Definite Assignment Assertions¶
SHOULD EASY
プロパティ名や変数名のあとに!
をつけると、コードから判別できなくても『値が代入されている』とみなせる。
別の言い方をするとnon-null
であることを保証できる。
function sum(x: number, y: number): number {
return x + y;
}
const x = 1;
let y: number;
// `y` だと代入前の利用エラーになるが、`y!`だとOK
sum(x, y!)
Fixed Length Tuples¶
HAD BETTER EASY
要素の型が包含されている長さの違うTupleに代入できなくなった。
以前は[A, B]
を[A, B, C]
のサブタイプと見なしていたが、length
プロパティの違いからそう見なさなくなった。
バージョン | [A, B, C] <- [A, B] | [A, B] <- [A, B, C] |
---|---|---|
v2.6以前 | エラー | OK |
v2.7以降 | エラー | エラー |
const child: [number, string, string] = [1, "one", "ichi"];
// 以前はOKだったがエラーになる
const parent: [number, string] = child;
Improved type inference for object literals¶
HAD BETTER EASY
オブジェクトリテラルの型推論が改善された。
const obj = test ? { text: "hello" } : {};
v2.6以前だとobj.text
はエラーになったが、v2.7以降はエラーにならない。
Improved handling of structurally identical classes and instanceof expressions¶
HAD BETTER NORMAL
構造的に一致するクラスの判定とinstanceof
式の挙動が改善された。
クラス判定を行うとき、構造的にsubtypeかどうかではなくクラスの継承関係を見るようになった。
以下の4クラスについて
class A {
id!: number
}
class AChild extends A {
name!: string
}
class AChild2 extends A {
name!: string
}
class ALike {
id!: number
}
式 | v2.6以前の推論型 | v2.7以降の推論型 | 備考 |
---|---|---|---|
!true ? new A() : new AChild() |
A |
A |
親子なら親へ |
!true ? new AChild() : new AChild2() |
AChild |
AChild ┃ AChild2 |
親子関係はない |
!true ? new Alike() : new A() |
ALike |
ALike ┃ A |
親子関係はない |
また、関数の中でinstanceof
でTypeGuardを行うと..
function ff(a: AChild | AChild2) {
if (a instanceof AChild) {
// v2.6以前だと a は `AChild | AChild2`
// v2.7以降だと a は `AChild`
}
}
Type guards inferred from in operator¶
HAD BETTER EASY
Objectに特定プロパティを持つかチェックするin
式が、Type Guardに対応した。
interface Human {
myNo: number;
name: string;
}
interface Dog {
name: string
}
function getMyNo(entity: Human | Dog): number {
if ("myNo" in entity) {
// entityはHuman型として扱われる
return entity.myNo;
}
// entityはDog型として扱われる
throw new Error(`${entity.name} に マイナンバーはありません.`)
}
Support for import d from "cjs" from CommonJS modules with --esModuleInterop¶
--esModuleInterop
フラグを使うとCommonJS moduleをimport d from "cjs"
の形でインポートできるようになった。
import * as foo from "foo"
はエラーになる- CommonJS/AMD/UMDからのデフォルトインポートが許可されるようになった
デフォルトエクスポートって最近では非推奨だったような気も。。🤔
Numeric separators¶
HAD BETTER EASY
数字をアンダースコアで区切れるようになった。
10進数以外を使う場合は分かりやすい。
1_000_000
1_0_0_0_0
0xFF_AA_BB_3C
0b0001_0010_0100_1000
Cleaner output in --watch mode¶
NOT NECESSARY EASY
--watch
コマンド実行時、再コンパイルされたら画面がクリアされるようになった。
Prettier --pretty output¶
NOT NECESSARY EASY
エラーメッセージが見やすくなる。
※ 後のバージョンでデフォルト有効になるので気にしなくてもよい
main.ts(5,1): error TS2554: Expected 2 arguments, but got 0.
main.ts:5:1 - error TS2554: Expected 2 arguments, but got 0.
5 sum()
~~~~~
main.ts:1:14
1 function sum(x: number, y: number): number {
~~~~~~~~~
An argument for 'x' was not provided.
Found 1 error.
※ 実際は色がつく