TypeScript 2.9 変更点

2018-06-01 14:18 +09:00

この記事はQiitaにも投稿されています。


今日TypeScript 2.9がリリースされました。2.8のconditional typesと比べるとちょっと破壊力が足りない気もしますが、個人的に待っていた機能が入ったりしたので、紹介したいと思います。今回のアップデートではCLIやツール系の改善も多かったですが、言語自体の変更点だけ紹介します。詳しくは公式記事を参考してください。

もし内容(や日本語)などが間違ってたら教えてください。

import()式からの型import

import()はES modulesを動的に読み込む式になります。「式」という単語を使っていますが、実際import()は関数ではなく文法要素です。なのでtypeof importしても結果は出ませんし、第一級として扱うことも出来ません。

import()は現在ES仕様のステージ3であり、もうChromeなどでは実装されています。

TypeScriptでももちろんimport()自体は使えましたが、import()から型はimport出来なかったため、不自然にtop-level importを混ぜて使うしかありませんでした。

import { SomeType } from "./foo"

async function func() {
  let val: SomeType

  // ...

  val = (await import("./foo")).someValue
}

TypeScript 2.9 ではimport()が型情報も提供してくれるので、以下のように書けます。

async function main() {
  let val: import("./foo").SomeType

  // ...

  val = (await import("./foo")).someValue
}

JSON import

今まではJSONファイルはrequire()を使ってimportするしかなく、型はいつもanyでした。

let pkg = require("./package.json") // any
pkg.name // any

TypeScript 2.9からはJSONファイルにもimportを使えるようになり、型もつけてくれます。

import pkg from "./package.json"
pkg.name // string

keyof型の変更

今までkeyofは文字列ベースのキーにしか対応しませんでした。

interface X {
  str: any,
  1: any,
  [symbol]: any
}

keyof X // "str" | "1"

keyof any[] // "map" | "length" | ...

TypeScript 2.9からはnumberSymbolなどのキーにも対応し、Arrayの場合ちゃんとnumberもキーに含めてくれます。

interface X {
  str: any,
  1: any,
  [symbol]: any
}

keyof X // "str" | 1 | symbol

keyof any[] // number | "map" | "length" | ...

上の変更で、例えばkeyof Xの型の変数に"1"を入れるとエラーになります。破壊的な変更なので、対応が必要な場合があります。もし今までstringをとあるオブジェクトのキーの型として使ってたとしたら、stringの代わりに以下のように型を宣言して使うとよいです。

type ObjectKey = keyof any // instead of `string`

もしTypeScript 2.8までのように文字列ベースの動作が欲しい場合、compilerOptionskeyofStringsOnlyが新しく追加されましたので、これをtrueにすると以前と同じ動作でコンパイルしてくれます。

Tagged template literalsの型変数サポート

Tagged template literalsとは以下のようにとある関数をtemplate literalのtagとして使うものです。

func`hello${x}world${y}yay`;

上の式は実際以下のように書くのと同じです。

func(["hello", "world", "yay"], x, y);

Tagged template literalsについて詳しくは下のMDN文書を参考してください。

上の例で確認できるよう、tagged templateのtagは普通の関数です。TypeScriptでは関数に型変数を提供してpolymorphicな動作をさせることが出来ます。

function func<X, Y>(strs: TemplateStringsArray, x: X, y: Y)

func<string, number>([...], "hello", 10);

しかしtagged templateの場合それが出来ませんでした。

func<string, number>`hello${x}world${y}yay`; // syntax error
func`hello${x}world${y}yay`; // x: any, y: any

TypeScript 2.9では、tagged templateでも型変数を使えるようになりました。

func<string, number>`hello${x}world${y}yay`; // x: string, y: number

この機能を一番まっていたのはstyled-componentsではないかと思います。styled-componentsはcomponent定義でcustom propsも使えるようにしていますが、TypeScriptではcustom propsの型を提供することが出来ませんでした。

const MyH1 = styled.h1`
  color: ${props => props.customProp}
`; // type error, no `customProp` in props

それをTypeScript 2.9では以下のように提供できるようになります。

type MyH1Props = { customProp: string };

const MyH1 = styled.h1<MyH1Props>`
  color: ${props => props.customProp}
`;

実は上のコードはstyled-componentsに型バグがあるためまだ動きません。その修正をpull requestで出しておきました。

その他

その他、マイナーな変更点が何個かありますので、気になる方は公式記事を見てみてください。

read other posts