[TypeScript] 文法

リリースノートに記載された仕様

TypeScript2.0から公式リリースノートで紹介されている内容を紹介する。

一部の仕様は後のリリースで変わっているため、過信はしないこと。
現在確認している最新バージョンは 2.0

Null- and undefined-aware types 2.0 ↑

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

--strictNullChecks 2.0 ↑

--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 2.0 ↑

strict null checking modeが有効のとき、変数の使用前に代入されていることをチェックする。

  • undefinedの場合は代入不要 (未代入がundefined相当)
  • nullの場合は代入が必要 (nullはObject)

Optional parameters and properties 2.0 ↑

value?: Tと表現されたvalueは、型T | undefinedとなる.

Non-null and non-undefined type guards 2.0 ↑

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 2.0 ↑

Type guardがドット表記名をサポートした.
argparamだけでなく、arg.xparam.yarg.x.yなどでもガードが可能.

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

Expression operators 2.0 ↑

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

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

Type widening 2.0 ↑

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

let x = nullのとき

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

Non-null assertion operator 2.0 ↑

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

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

Control flow based type analysis 2.0 ↑

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

Tagged union types 2.0 ↑

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 2.0 ↑

発生しない値の型 never型 (primitive)

  • never型の変数は、すべての型に割り当てることができる (すべてsubtype)
  • never型でない変数は、never型に割り当てることができない

出現するケース

  • 関数の最後に到達しない関数 (必ずthrowするなど)

関数がnever以外を返す可能性があるとき、戻り値の型にneverは出現しない.
なぜなら、すべての型のsubtypeであるから.

Read-only properties and index signatures 2.0 ↑

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 2.0 ↑

関数内で使う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 2.0 ↑

コールバック関数の第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 2.0 ↑

--noImplicitThisを有効にすると、thisの方が明示されていない場合エラーになる.

this型になった?

エラーにならずthis型と判断された.. 後のリリースでthis型に対応したから?)

Base URL 2.0 ↑

compilerOptions.baseUrlにパスを指定すると、相対名のないimportはtsconfig.jsonを基点とした指定値からの相対パスとなる.
tsconfig.jsonの場所を~/typescript-sampleとしたとき、"baseUrl": "./foo/bar"なら以下は同じ.

  • import "foo/bar"
  • import "~/typescript-sample/foo/bar"

AMD moduleをロードする場合に使う.

Path mapping 2.0 ↑

特定のmoduleだけbaseUrl配下の特定ファイルを読み込みたい場合、pathsにkey-value形式で指定できる.

import "moduleName"のような簡潔表記が可能になる.

Nuxt.jsのtsconfig.jsonでは以下のようにRoot直下のパスをどこからでも~/...@/...で指定可能になっている.

    "baseUrl": ".",
    "paths": {
      "~/*": ["./*"],
      "@/*": ["./*"]
    },

Virtual Directories with rootDirs 2.0 ↑

複数のディレクトリがあたかも同じディレクトリであるかのように見せて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 2.0 ↑

--traceResolutionオプションを付けると、コンパイラがモジュールの依存関係をどう解決したか表示できる.

Shorthand ambient module declarations 2.0 ↑

以下のようなindex.d.tsのような型定義ファイルを作成すると、any型としてmoduleをインポートできる.

declare module "your-import-module"

型定義ファイルはないが、とりあえず動かしたいときに有効.

Wildcard character in module names 2.0 ↑

型定義のmodule宣言対象にワイルドカードを指定できる.
たとえばdeclare module '*.json'のようにすると、全てのjsonファイルがインポート対象になる.

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

Optional class properties 2.0 ↑

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

class Hoge {
  a?: number
  b?(): string
}
  • anumber | undefinedになる
  • b(() => string) | undefinedになる

Private and Protected Constructors 2.0 ↑

privateやprotectedのコンストラクタを作ることができる.

可視性 外部からの呼び出し(new) 継承
private O X
protected O O
public O O

Abstract properties and accessors 2.0 ↑

abstractクラスには以下を定義できる.

  • abstract プロパティ
  • abstract getter
  • abstract setter

Implicit index signatures 2.0 ↑

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 2.0 ↑

--libcompilerOptions.libでランタイムに含まれるAPIを指定できる.
たとえば、domを追加するとdocumentなどの型が認識される.

tsconfig.json

{
  "compilerOptions": {
    "target": "es2018",
    "lib": ["es2018", "dom"]
  }
}

Flag unused declarations with --noUnusedParameters and --noUnusedLocals 2.0 ↑

使われていないパラメータやローカル変数があるとエラーになる.

パラメータ エラーの対象
--noUnusedParameters 使われていない関数やメソッドの引数
--noUnusedLocals 使われていない/exportされていない宣言(変数/関数/クラス..etc)

パラメータ/変数の名前を_から始めると検査対象から除外される.

Module identifiers allow for .js extension 2.0 ↑

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’ 2.0 ↑

target: es5module: es6の組み合わせが不正ではなくなった.

メリットは分からず...

Trailing commas in function parameter and argument lists 2.0 ↑

関数定義の引数リストや、関数の引数リストに末尾カンマが許容されるようになった.

function hoge(
  a: int,
  b: int,
) {
  return a + b
}

hoge(
  1,
  2,
)

改行で区切って無くてもOK. ただ末尾カンマをつけたいのは通常改行時 (diffを見やすくするため)

よく使う型

Partial (2.1から)

すべてのプロパティをOptional(|undefined)にする。

Required (2.8から)

すべてのプロパティをRequired(必須)にする。

Readonly (2.1から)

すべてのプロパティを読みこみ専用(readonly)にする。

Pick (2.1から)

ある型から特定のプロパティのみを抜き取った型を作る。

type Pick<T, K extends keyof T>と表現する。
具体例はPick<Human, 'id' | 'name'>のような感じ。