TypeScript 2.9.1がアナウンスされました。
What's new in TypeScriptも更新されているようです。
破壊的変更もあるよ!
この辺に僕が試した時のコードを投げてあります。
ちなみに、次のバージョンは2.10じゃなくて3.0らしいです。
変更点まとめ
- ファイル名のリネームのサポート Add 'renameFile' command to services
- ファイル名をリファクタリングできるようになった
- 選択範囲を別ファイルに切り出す操作のサポート Add 'move to new file' refactor
- 定義を別ファイルに切り出すリファクタリングができるようになった
- 使ってない定義があったら教えてくれるようになった Show unused declarations as suggestions
-
--noUnusedLocals
や--noUnusedParameters
でエラーになる箇所について、これらを指定しない場合警告として表示してくれるようになった
-
- プロパティをgetter/setterに変換できるようになった Convert property to getter/setter
- そのまんま
- 型として
import(...)
をどこでも使えるようになった Allowimport(...)
-ing types at any location-
let p: import("./foo").Person
とか書ける - ついでにimportした関数とかから作った要素をexportする時の縛りがゆるくなった
-
-
--pretty
がデフォルトで有効になった--pretty
error output by default-
tsc --pretty
とかするとエラー表示が見やすくなるオプションがあった これがデフォルト有効に -
--pretty false
で今までと同じ出力
-
-
.json
をimportした時にいい感じの型が付くようになった New--resolveJsonModule
-
--resolveJsonModule
が生えた -
moduleResolution
がnode
のとき、jsonをimportするとjsonの中身に応じた型が自動的に付く
-
- タグ付きテンプレートリテラルにgenericsの型を指定できるようになった Support for passing generics to tagged template calls
-
styledComponent<MyProps>`font-size: 1.5em;`
こんな感じ
-
- keyofとかMapped typesでstring以外のnumberとかsymbolも出てくるようになった Support number and symbol named properties with keyof and mapped types
-
keyof T
でstring | number | symbol
が得られるようになった -
--keyofStringsOnly
も用意された
-
- JSXのタグの中でComponentにgenericsの型を指定できるようになった Support for passing generics to JSX elements
-
<MyComponent<number> data={12} />
こゆ感じ
-
-
import.meta
がサポートされた Support forimport.meta
- https://github.com/tc39/proposal-import-meta この仕様
-
import.meta.__dirname
とか書けるようになりたい!的な - globalに
ImportMeta
を生やす -
--target esnext
と--module esnext
が必要
-
.d.ts.map
が出力できるようになった Declaration source maps and code navigation via them-
--declarationMap
が導入された -
--declaration
と併用する -
.d.ts.map
が生成され、.d.ts
に飛んだ時にオリジナルの.ts
コードを見ることができる -
--inlineSource
とかは反映されないのでつらい
-
- "提案"レベルの情報の追加 Show suggestion diagnostics for open files
- error, warning の他に suggestion(info) が追加された
- require 使ってたら import に変換してくれる Convert require to import in .ts files
- 1個前の仕組みを使っている
- .ts 限定らしい
- 時代を感じますねぇ(互換性があるという判断が行われるようになった)
- Quick Fixとかで使われるクォートを " と ' のどっちかに指定できるオプション Support setting quote style in quick fixes and refactorings
- とか言ってるけど実は(Language) Serviceに対して設定を追加できるAPIが生えた
- 裏で UserPreferences というのが生えた
- 今できるのはクォートの統一とモジュールをimportする時に相対パスにするか絶対パスにするかの設定ができる
typescript.preferences.quoteStyle
typescript.preferences.importModuleSpecifier
- とか言ってるけど実は(Language) Serviceに対して設定を追加できるAPIが生えた
- Node.jsのビルトインモジュールを使おうとしたら
@types/node
入れてくれる Install@types/node
for built-in node modules- まぁあったほうが便利ですよね
-
strictNullChecks
でobject
を底にしてない型パラメータの値をobject
に割り振り不可にした Unconstrained type variable not assignable to 'object'- 一貫性を破壊できたのが修正された
-
never
はfor-in
とかfor-of
とかで繰り返し処理しようとした時にエラーになるようになった Don't allow to iterate over 'never'- そのまんま
破壊的変更!
上記リストのうち、破壊的変更を伴うのは次のものになります。
- keyofとかMapped typesでstring以外のnumberとかsymbolも出てくるようになった
-
--pretty
がデフォルトで有効になった - 関数の引数に可変長引数がある場合、末尾のカンマを許さないように変更
-
strictNullChecks
でobject
にobject
を底にしてない型パラメータの値を割り振り不可にした -
never
はfor-in
とかfor-of
とかで繰り返し処理しようとした時にエラーになるようになった
ファイル名のリネームのサポート
そのまんまです。
VSCodeのファイルエクスプローラでファイル名を変更しようとすると、変更しようとしたファイルを参照しているコードのimportを書き換える?と聞かれます。
No, never update imports
, Yes, always update imports
, No
, Yes
の選択肢が現れます。
Yesを選ぶと自動的にimport句が更新されます。
仕様上、ファイルをrenameした後にダイアログを出しているようで、renameをキャンセルすることは(現時点では)できないようです。
Yes, always update imports
を選ぶとUser Settingsに次の設定が追加されました。
{
"typescript.updateImportsOnFileMove.enabled": "always"
}
大変便利なので常時Yesでいいんじゃないでしょうか。
選択範囲を別ファイルに切り出す操作のサポート
そのまんまです。
次のようなファイルがあるとします。
interface Foo {
name: string;
}
let f: Foo = { name: "test" };
export { }
1〜3行目を選択して、Quick Fixで Move to a new file
を選ぶと該当行を別ファイルに切り出してくれます。
import { Foo } from "./Foo";
let f: Foo = { name: "test" };
export interface Foo {
name: string;
}
こんな感じですね。
ちゃんとexportを付与してくれたりimport句も作ってくれたりして偉いです。
元のファイルで export {}
的なESモジュールであると示しておかないと意図通りに動いてくれないので少しだけ注意が必要です。
使ってない定義があったら教えてくれるようになった
--noUnusedLocals
や --noUnusedParameters
というのがすでにあるんですが、これを使っていない時でも、エディタ上で使ってない変数とかをグレーアウトしてほんのりと使ってないことを教えてくれるようになった、という奴です。
JetBrains系のIDEではこういうほんのりと教えてくれる奴が充実していて役に立っているので嬉しいですね。
まぁ僕は上記オプションをONにしてコンパイルエラーにしますが…。
プロパティをgetter/setterに変換できるようになった
そのまんまですね。
クラスのプロパティ名を選択してQuick Fixでgetter/setterに変換できるようになりました。
次のコードのaとbを変換してみます。
class Hoge {
a?: string;
b: string | undefined;
}
すると、こうなります。
class Hoge {
private _a?: string;
public get a(): string { // ← string | undefined じゃないのでコンパイルエラーになる
return this._a;
}
public set a(value: string) {
this._a = value;
}
private _b: string | undefined;
public get b(): string | undefined {
return this._b;
}
public set b(value: string | undefined) {
this._b = value;
}
}
ちょっとおバカですね…。
でも実用上の役にはしっかり立ちそうです。
型として import(...)
をどこでも使えるようになった
ECMAScriptにdynamic importという仕様があります。
それと同じsyntaxで型注釈が書けるようになった…というものです。
export function hello(name: string) {
return `Hello, ${name}!`;
}
export interface Data {
id: string;
content: any;
}
type fooType = typeof import("./foo");
function f(fn: typeof import("./foo").hello) {
fn("import()");
}
const fn1: typeof import("./foo").hello = name => `Hi, ${name}`;
fn1("import()");
const fn2: fooType["hello"] = name => `Hi, ${name}`;
fn2("import()");
const data1: import("./foo").Data = {
id: "foo",
content: "bar",
};
type Data = import("./foo").Data;
interface Data1 extends Data { }
// この書き方はinterfaceのsyntax的にダメ
// interface Data2 extends import("./foo").Data { }
いろいろな書き方ができますね。
TypeScriptでは import ... from "...";
で型をimportしても、値として利用しなければコンパイル後には消えてしまいます。
よって、この書き方ができなくても実用上問題はなかった気もしますが、わかりにくい仕様だったことに変わりはありません。
ここで、 import(...)
と同様の書き方で型としても参照できるようになり、わかりやすさは改善されたと言えるでしょう。
使い所がいまいちわからない…。
また、この記法の導入により型定義を生成するときの制限が緩和されました。
TypeScript 2.8までは次のようなコードをコンパイルするとエラーになっていました。
import { createHash } from "crypto";
export const hash = createHash("sha256");
src/importAsTypes/relax.ts:3:14 - error TS4023: Exported variable 'hash' has or is using name 'Hash' from external module "crypto" but cannot be named.
3 export const hash = createHash("sha256");
~~~~
これのコンパイルを通すには import { createHash, Hash } from "crypto";
と書く必要がありました。
これは、 .d.ts
ファイルが次のように出力されていたためです。
/// <reference types="node" />
import { Hash } from "crypto";
export declare const hash: Hash;
TypeScript 2.9からは Hash
をimportしていなくてもコンパイルが通ります。
そして、次のような .d.ts
ファイルが生成されます。
/// <reference path="../../node_modules/@types/node/index.d.ts" />
/// <reference types="node" />
export declare const hash: import("crypto").Hash;
…1行目はいるんですかね…?
--pretty
がデフォルトで有効になった
前からあった --pretty
というエラーをpretty printしてくれるオプションがありました。
今回からこれがデフォルトになり、2.8までと同様の出力にしたい場合、 --pretty false
とします。
src/importAsTypes/index.ts:21:25 - error TS2499: An interface can only extend an identifier/qualified-name with optional type arguments.
21 interface Data2 extends import("./foo").Data { }
~~~~~~~~~~~~~~~~~~~~
.json
をimportした時にいい感じの型が付くようになった
まんまです。
import data from "../../tsconfig.json";
// OK
console.log(data.compilerOptions.target);
// こっちは存在しないのでエラーになる
console.log(data.notExists);
なお、moduleResolutionをclassicにするとこの機能は使えません。
タグ付きテンプレートリテラルにgenericsの型を指定できるようになった
タグ付きテンプレート用の関数にgenericsを使えるようになりました。
主にReactというかstyled-componentにいい感じに型をつけるために導入されたっぽいですね。
function tag<T>(strs: TemplateStringsArray, ...args: T[]) {
console.log(strs, ...args);
}
// パラメタが全部 number なのでOK
tag<number>`hoge${1}${2}`;
// 2つ目のパラメタが number なのでエラー
// tag<string>`hoge${"A"}${2}`;
// 1つ目と2つ目のパラメタが一致してないのでエラー
// tag`${true}${1}`;
styled-componentだとこういう感じらしいです。
declare function styledComponent<Props>(strs: TemplateStringsArray): Component<Props>;
styledComponent<MyProps>`
font-size: 1.5em;
`
interface Component<T> {
}
interface MyProps {
name: string;
age: number;
}
よかったですね。
keyofとかMapped typesでstring以外のnumberとかsymbolも出てくるようになった
今までは string なプロパティしかkeyとして切り出せなかったのが、numberやsymbolもkeyとして取れるようになりました。
keyof T
で string | number | symbol
的なものが取れる可能性がある、ということですね。
const sym = Symbol();
interface Foo {
str: string;
11: number;
[sym]: symbol;
}
// typeof sym | "str" | 11 となる
type keyofFoo = keyof Foo;
// typeof sym
type A = Extract<keyofFoo, symbol>;
// str
type B = Extract<keyofFoo, string>;
// 11
type C = Extract<keyofFoo, number>;
// なお --keyofStringsOnly だと keyofFoo は "str" になる
// ちなみに…
type array = [1, 2, 3];
// "1", "2", "3", ...Arrayのメソッドいろいろ
type keyofArray = keyof array;
旧来の挙動にするために --keyofStringsOnly
が用意されました。
部分的にやったいきたい場合、 keyof Foo
を Extract<keyof Foo, string>
に置き換えていくのがよい、とのことです。
JSXのタグの中でComponentにgenericsの型を指定できるようになった
Reactのやり込みが足りないので活用ポイントがよくわかんないですね…。
こんな感じだそうです。
import { Component } from "react";
interface Props<T> {
data: T;
}
class MyComponent<T> extends Component<Props<T>> {
render() {
return <div>{this.props.data}</div>;
}
}
<MyComponent<number> data={12} />
パーサさんも辛い気持ちになったりしているんだろうなぁ。
import.meta
がサポートされた
import.metaがサポートされました。
利用には --target esnext
と --module esnext
が必要です。
declare global {
interface ImportMeta {
foo: string;
}
}
import.meta.foo;
// これは定義されてないのでエラー
// import.meta.notExist;
そういえばTypeScriptでmjsが出力できないのはちょっと不便ですね。
.d.ts.map
が出力できるようになった
.d.ts.map は .d.ts と .ts ファイルの対応関係を記すものです。
.js.map が .js と .ts の関係を記すのと同様ですね。
コードを書いているとき、定義にジャンプすると今まで .d.ts の定義に飛んでいたのが、ちゃんと対応関係が定義されている場合、 .ts ファイルの該当部分にジャンプできます。
有効にするには --declarationMap
を使います。
--declaration
との併用が必須です。
なお、 --inlineSource
を使っても .d.ts.map に .ts の内容は現時点では保存されません。かなしい。
.tsと.d.tsファイルが同じ場所にある場合、.tsの読み込みが優先されます。
ですので、npm publishするときは .ts ファイルをignoreするか、 --outDir
で別ディレクトリに出力しているかのどちらかだと思います。
もしあなたが後者なら、このオプションを使いやすく、ユーザの役に立つでしょう。
ちなみに僕は前者スタイルです(かなしい)
"提案"レベルの情報の追加
Error, Warning の他に Suggestion が追加されました。
現時点では、次の項目である require 使ってたら import に変換してくれる
が実装されています。
Language Service周りの開発に興味がある人はPRをよく読むとよさそうです。
require 使ってたら import に変換してくれる
現時点では、 CommonJSモジュールをESモジュールに変換する?という提案が実装されています。
const fs = require("fs");
↑を↓に変換してくれます
import fs from "fs";
こういう変換を行ってもよい時代になった、とTypeScript teamが判断したこと自体が、趣深いですね。
Quick Fixとかで使われるクォートを " と ' のどっちかに指定できるオプション
裏で(Language) Serviceに対して設定を追加できるAPIが生えました。
UserPreferences というのが生えてます。
とりあえず次の設定をVSCodeに対して行うとLanguage Serviceに設定が伝搬されます。
新規にQuick Fixやリファクタリングで生成された文字列リテラルがダブルクォートかシングルクォートかを指定できるというものです。
{
"typescript.preferences.quoteStyle": "double"
}
typescript.preferences.importModuleSpecifier
というのも追加され、 non-relative
と relative
が選べますが baseUrl
の設定などに左右されてめんどくさいので割愛します。
Node.jsのビルトインモジュールを使おうとしたら @types/node
入れてくれる
そのまんまです。
今までも .d.ts
が存在しないパッケージを @types/
無しに使っていると、Quick Fixで @types/
を入れてくれていました。
この機能はNode.jsのモジュールを @types/node
無しに使うと、 これを入れるか聞いてくれるというものです。
よくあるパターンなのであると嬉しいですね。
strictNullChecks
で object
を底にしてない型パラメータの値を object
に割り振り不可にした
object
型に不正にstringとかを代入できるバグがあったので塞がれたというやつです。
詳細は次のコードの通りです。
function f1<T>(x: T) {
// 今回からエラー! T を object に代入できちゃうのはヤバい!エラーになるのは正しい!
const y: object = x;
console.log(y);
}
f1("string");
f1({});
// T の底を object にする
function f2<T extends object>(x: T) {
const y: object = x;
console.log(y);
}
// string は object ではないのでエラーになる
f2("string");
f2({});
function f3<T>(x: T) {
// object をやめて {} にする
const y: {} = x;
console.log(y);
}
// 両方OK
f3("string");
f3({});
never
は for-in
とか for-of
とかで繰り返し処理しようとした時にエラーになるようになった
次のようなコードが両方エラーになるようになりました
const neverVar: never = (() => { throw new Error() })();
// error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'never'.
for (let v in neverVar) {
console.log(v);
}
// error TS2488: Type 'never' must have a '[Symbol.iterator]()' method that returns an iterator.
for (let v of neverVar) {
console.log(v);
}