[TypeScript] 2.0¶
TypeScript2.0のリリース内容まとめ
Null and undefined-aware types¶
NOT NECESSARY EASY
null
とundefined
をそれぞれ型として認識できる
--strictNullChecks¶
HAD BETTER EASY
--strictNullChecks
でstrict null checking modeが有効になる
strict null checking mode ON | strict null checking mode OFF | |
---|---|---|
undefined が代入できる型 |
undefined any void |
全ての型 |
null が代入できる型 |
null any |
全ての型 |
T とT | undefined |
別の型とみなす | ほぼ同一の型とみなす |
T とT | null |
別の型とみなす | ほぼ同一の型とみなす |
Assigned-before-use checking¶
HAD BETTER EASY
strict null checking modeが有効のとき、変数の使用前に代入されていることをチェックする。
undefined
の場合は代入不要 (未代入がundefined
相当)null
の場合は代入が必要 (nullはObject)
Optional parameters and properties¶
SHOULD EASY
value?: T
と表現されたvalueは、型T | undefined
となる.
Non-null and non-undefined type guards¶
NOT NECESSARY EASY
ifや三項演算子などの条件式でnull/undefinedの可能性がなくなったとき
型T | null
や型T | undefined
は型T
となる. 逆も同じ.
let stringOrNull = string | null
if (stringOrNull != null) {
// この中ではstringOrNullはstring型
} else {
// この中ではstringOrNullはnull
}
Dotted names in type guards¶
NOT NECESSARY EASY
Type guardがドット表記名をサポートした.
arg
やparam
だけでなく、arg.x
やparam.y
、arg.x.y
などでもガードが可能.
strict null checking modeではなくてもType guardは作用する.
Expression operators¶
NOT NECESSARY EASY
オペランド(演算値)の型がnull
やundefined
を含むとしても、結果はそれらを含まない。
ただし、&&
や||
は例外.
左オペランド型 | オペレータ | 右オペランド型 | 結果の型 |
---|---|---|---|
T | null |
+ |
T | null |
T |
T | null |
&& |
U |
U | null |
T | null |
|| |
U |
T | U |
Type widening¶
NOT NECESSARY EASY
strict null checking modeの有無によって、null
やundefined
を代入した変数の型が変わる.
let x = null
のとき
strict null checking mode | xの型 |
---|---|
有効 | null |
無効 | any |
Non-null assertion operator¶
SHOULD EASY
!
をつけるとnull
やundefined
の可能性がある変数から、null
やundefined
の可能性を排除できる.
たとえばx: number | null
に対しては、x!.
Type guardsを使って型チェッカーが判断できるならその方がいい.
Control flow based type analysis¶
NOT NECESSARY EASY
文(return
やbreak
など)、式により型が限定される場合は絞り込む.
Tagged union types¶
SHOULD EASY
Discriminated union typesとも言う.
Union型で指定されたそれぞれのinterfaceやclassに固定の文字列リテラル(判別特性)を持たせる.
if文やswitch文を使い判別特性の値で条件分岐したあと、Union型の候補は絞られる.
以下はShapeというUnion型とkind
という判別特性を使った例.
interface Square {
kind: "square";
size: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Circle
Shapeのオブジェクトsh
に対してif(sh.kind === "square") {...}
が真の場合、...
の処理内でsh
はSquare
型として扱われる.
※ 2.0だと判別特性(discriminant properties)はstringリテラルしか使えない
The never type¶
HAD BETTER NORMAL
発生しない値の型 never
型 (primitive)
never
型の変数は、すべての型に割り当てることができる (すべてsubtype)never
型でない変数は、never
型に割り当てることができない
出現するケース
- 関数の最後に到達しない関数 (必ずthrowするなど)
関数がnever
以外を返す可能性があるとき、戻り値の型にnever
は出現しない.
なぜなら、すべての型のsubtypeであるから.
Read-only properties and index signatures¶
SHOULD EASY
readonly
を付けると、プロパティやインデックスを読み取り専用にできる.
すると代入ができなくなる(コンストラクタは例外).
readonlyのプロパティが持つプロパティは自動でreadonlyにならないので注意
class Human {
constructor(public readonly readArg: number, public name: string, public readonly favorite?: Human){}
}
const tatsuwo = new Human(100, "tatsuwo", new Human(330, "mimizou"))
tatsuwo.readArg = 5 // Error
tatsuwo.name = "hoge" // OK
tatsuwo.favorite = new Human(333, "mitsuwo") // Error
tatsuwo.favorite.name = "MITSUWO" // OK
Specifying the type of this for functions¶
NOT NECESSARY CAN NOT UNDERSTAND
関数内で使うthisの型を指定できる. (Classの場合だけ?)
普通に書くとcはany型になる.
this型になった?
cがany型ではなくthis型と判断された..後のリリースで仕様が変わった?
class Caluculator {
add(x: number, y: number): number {
const c = this; // c: any
return x + y;
}
}
const calc = new Caluculator();
console.log(calc.add(1, 2));
Pythonのselfみたいに第1引数にthisを指定すると、thisの型を強制できる.
class Caluculator {
add(this: Caluculator, x: number, y: number): number {
const c = this; // c: Caluculator
return x + y;
}
}
const calc = new Caluculator();
console.log(calc.add(1, 2)); // 第1引数のthisはスルーされる
this parameters in callbacks¶
NOT NECESSARY EASY
コールバック関数の第1引数にthis: void
を指定することで、this
の禁止を強要できる.
※ コールバックのthis
はバグの温床だからね!
onclick: (e: Event) => ...
ではなくonclick: (this: void, e: Event) => ...
にすると..
interface UIElement {
addClickListener(onclick: (this: void, e: Event) => void): void;
}
どのような形でthis
を使おうとしても、type checkの時点でエラーになる.
class Handler {
info: string;
// `this: Handler` としたとき
onClickBad(this: Handler, e: Event) {
this.info = e.message;
}
}
// Error: `onClickBad(this: Handler, e: Event)` に `(this: void, e: Event) => void` は代入できない
({} as UIElement).addClickListener((new Handler()).onClickBad);
class Handler {
info: string;
// `this: void` としたとき
onClickBad(this: void, e: Event) {
// Error: infoはvoid型に存在しない!
this.info = e.message;
}
}
({} as UIElement).addClickListener((new Handler()).onClickBad);
--noImplicitThis¶
NOT NECESSARY CAN NOT UNDERSTAND
--noImplicitThis
を有効にすると、this
の型が明示されていない場合エラーになる.
this型になった?
エラーにならずthis
型と判断された.. 後のリリースでthis
型に対応したから?)
Base URL¶
NOT NECESSARY NORMAL
compilerOptions.baseUrl
にパスを指定すると、相対名のないimportはtsconfig.json
を基点とした指定値からの相対パスとなる.
tsconfig.json
の場所を~/typescript-sample
としたとき、"baseUrl": "./foo/bar"
なら以下は同じ.
import "foo/bar"
import "~/typescript-sample/foo/bar"
AMD moduleをロードする場合に使う.
Path mapping¶
SHOULD EASY
特定のmoduleだけbaseUrl
配下の特定ファイルを読み込みたい場合、paths
にkey-value形式で指定できる.
import "moduleName"
のような簡潔表記が可能になる.
Nuxt.jsのtsconfig.json
では以下のようにRoot直下のパスをどこからでも~/...
や@/...
で指定可能になっている.
"baseUrl": ".",
"paths": {
"~/*": ["./*"],
"@/*": ["./*"]
},
Virtual Directories with rootDirs¶
NOT NECESSARY EASY
複数のディレクトリがあたかも同じディレクトリであるかのように見せてimportができる.
ファイル構成
* src
* one
* one1.ts
* other
* other1.ts
tsconfig.json
の設定にrootDirs
を入れる.
"rootDirs": [
"src/one",
"src/other"
]
すると、one1.ts
とother1.ts
はお互いがカレントディレクトリにいるものとしてimportできる.
one1.ts
import { add } from './other1'; // src/one と src/other は同ディレクトリと判断されるためOK
export function add3(a: number, b: number, c: number): number {
return add(add(a, b), c);
}
other1.ts
export function add(a: number, b: number): number {
return a + b;
}
Tracing module resolution¶
HAD BETTER EASY
--traceResolution
オプションを付けると、コンパイラがモジュールの依存関係をどう解決したか表示できる.
Shorthand ambient module declarations¶
以下のようなindex.d.ts
のような型定義ファイルを作成すると、any型としてmoduleをインポートできる.
HAD BETTER EASY
declare module "your-import-module"
型定義ファイルはないが、とりあえず動かしたいときに有効.
Wildcard character in module names¶
NOT NECESSARY EASY
型定義のmodule宣言対象にワイルドカードを指定できる.
たとえばdeclare module '*.json'
のようにすると、全てのjsonファイルがインポート対象になる.
*!text
やjson!*
でprefix/suffix指定表現にも対応できる.
ただ実行時までの間にパスを解決する必要があるためWebpackなどのツールチェーンが必要なはず.
Optional class properties¶
SHOULD EASY
strict null checking modeが有効のとき、クラスプロパティに?
をつけると| undefined
になる.
class Hoge {
a?: number
b?(): string
}
a
はnumber | undefined
になるb
は(() => string) | undefined
になる
Private and Protected Constructors¶
SHOULD EASY
privateやprotectedのコンストラクタを作ることができる.
可視性 | 外部からの呼び出し(new) | 継承 |
---|---|---|
private | O | X |
protected | O | O |
public | O | O |
Abstract properties and accessors¶
HAD BETTER EASY
abstractクラスには以下を定義できる.
- abstract プロパティ
- abstract getter
- abstract setter
Implicit index signatures¶
NOT NECESSARY EASY
index signature ({[key: string]: number}
など) にObject literalを割り当てることができる.
ただし、値の型が等しい場合のみ.
※ 今回のケースではnumber
function func(arg: { [key: string]: number }) {
console.log(arg);
}
const obj = {
key1: 1,
key2: 1,
3: 1,
};
func(obj);
keyは型が一致していなくても動作する.
Including built-in type declarations with --lib¶
SHOULD EASY
--lib
やcompilerOptions.lib
でランタイムに含まれるAPIを指定できる.
たとえば、dom
を追加するとdocument
などの型が認識される.
tsconfig.json
{
"compilerOptions": {
"target": "es2018",
"lib": ["es2018", "dom"]
}
}
Flag unused declarations with --noUnusedParameters and --noUnusedLocals¶
HAD BETTER EASY
使われていないパラメータやローカル変数があるとエラーになる.
パラメータ | エラーの対象 |
---|---|
--noUnusedParameters |
使われていない関数やメソッドの引数 |
--noUnusedLocals |
使われていない/exportされていない宣言(変数/関数/クラス..etc) |
パラメータ/変数の名前を_
から始めると検査対象から除外される.
Module identifiers allow for .js extension¶
UNNECESSARY EASY
import d from "./moduleA.js"
のように.js
拡張子を付けてインポートできるようになった.
- 2.0以前
moduleA.js.ts
ormoduleA.js.d.ts
を探しにいく
- 2.0以降
moduleA.ts
ormoduleA.d.ts
を探しにいく
Support ‘target : es5’ with ‘module: es6’¶
UNNECESSARY EASY
target: es5
とmodule: es6
の組み合わせが不正ではなくなった.
メリットは分からず...
Trailing commas in function parameter and argument lists¶
HAD BETTER EASY
関数定義の引数リストや、関数の引数リストに末尾カンマが許容されるようになった.
function hoge(
a: int,
b: int,
) {
return a + b
}
hoge(
1,
2,
)
改行で区切って無くてもOK. ただ末尾カンマをつけたいのは通常改行時 (diffを見やすくするため)
New --skipLibCheck¶
SHOULD EASY
--skiplibcheck
をコンパイル時にオプションとして付けると、d.ts
ファイルの型検査をスキップする.
...↑と書いてあるのだけどスキップしてくれない。。なぜ。。。
Allow duplicate identifiers across declarations¶
NOT NECESSARY EASY
複数の同一名称型において、重複する識別子を許容する.
以下は問題なしとなる。
interface Hoge {
hoge1: string;
}
interface Hoge {
hoge1: string;
hoge2: string;
}
const hoge = {
hoge1: '11',
hoge2: '22',
}
一方、同一ブロック内の重複はNG.
interface Hoge {
hoge1: string;
hoge2: string;
hoge1: string; // これはエラー
}
New --declarationDir¶
HAD BETTER EASY
.d.ts
ファイルの出力ディレクトリを指定できる.
tsc --declaration --declarationDir ${dirname}
もちろんtsconfig.json
のdeclaration
とdeclarationDir
を指定してもOK.