コンテンツにスキップ

[TypeScript] 2.0

TypeScript2.0のリリース内容まとめ

Null and undefined-aware types

NOT NECESSARY EASY

nullundefinedをそれぞれ型として認識できる

--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 全ての型
TT | undefined 別の型とみなす ほぼ同一の型とみなす
TT | 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がドット表記名をサポートした.
argparamだけでなく、arg.xparam.yarg.x.yなどでもガードが可能.

strict null checking modeではなくてもType guardは作用する.

Expression operators

NOT NECESSARY EASY

オペランド(演算値)の型がnullundefinedを含むとしても、結果はそれらを含まない。
ただし、&&||は例外.

左オペランド型 オペレータ 右オペランド型 結果の型
T | null + T | null T
T | null && U U | null
T | null || U T | U

Type widening

NOT NECESSARY EASY

strict null checking modeの有無によって、nullundefinedを代入した変数の型が変わる.

let x = nullのとき

strict null checking mode xの型
有効 null
無効 any

Non-null assertion operator

SHOULD EASY

!をつけるとnullundefinedの可能性がある変数から、nullundefinedの可能性を排除できる.
たとえばx: number | nullに対しては、x!.

Type guardsを使って型チェッカーが判断できるならその方がいい.

Control flow based type analysis

NOT NECESSARY EASY

文(returnbreakなど)、式により型が限定される場合は絞り込む.

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") {...}が真の場合、...の処理内でshSquare型として扱われる.

※ 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.tsother1.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ファイルがインポート対象になる.

*!textjson!*でprefix/suffix指定表現にも対応できる.
ただ実行時までの間にパスを解決する必要があるためWebpackなどのツールチェーンが必要なはず.

Optional class properties

SHOULD EASY

strict null checking modeが有効のとき、クラスプロパティに?をつけると| undefinedになる.

class Hoge {
  a?: number
  b?(): string
}
  • anumber | 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

--libcompilerOptions.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 or moduleA.js.d.ts を探しにいく
  • 2.0以降
    • moduleA.ts or moduleA.d.ts を探しにいく

Support ‘target : es5’ with ‘module: es6’

UNNECESSARY EASY

target: es5module: 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.jsondeclarationdeclarationDirを指定してもOK.