訂正1: コメントで指摘があった Contravariant→反変では? を反映。coとcontra…
こんにちはソウゾウ社に転職しました@vvakameです。
今回は結構気持ちよくなる更新が多いです。
TypeScript 2.6.1がアナウンスされました。
What's new in TypeScriptも更新されているようです。
破壊的変更もあるよ!
変更点まとめ
- 関数の引数の型についてのチェックを強化 Strict function types
-
(d: Dog) => void
的な関数を(a: Animal) => void
的な変数に突っ込めてたのを弾けるようになった
-
- タグ付きテンプレートリテラルがキャッシュされるようになった Cached tagged template objects in modules
- よりECMAScriptの仕様に準拠した形
- 関数の第一引数に渡される
TemplateStringsArray
は複数回実行した時、前回オブジェクトと===
で比較した時trueになるのが正しかった
- エラーメッセージの各国語ローカライズがnpmにもshipされるようになった Include localized diagnostics in npm
-
--local ja
的な - 結構前からあってVisualStudioバンドルのやつとかは日本語化されてた(はず
-
- .ts でも
// @ts-ignore
が使えるようになった Suppress errors in .ts files using // @ts-ignore comments-
// @ts-ignore: Unreachable code error
とか書くと次の行のエラーを抑制できる
-
-
--watch
時のビルドが早くなった Speed improvements to --watch- 変更のあったモジュール&関連モジュールだけリビルドされるようになった
- 未使用変数のチェック強化 Write-only references now flagged as unused
- unusedな変数などについて代入のみの場合使ってない判定になるようになった
- 各モジュールでexportされてる要素が入力補完候補に出るようになりimport句も挿入されるようになった Automatic imports from completion lists
- みんなが待ち望んでたやつでは!?
- リファクタリング機能の追加要素
- 値を
const
として切り出せるやつ Extract constants/locals in refactorings - JSDocの型注釈をTypeScriptの型注釈に変換 Convert JSDoc type annotations to TypeScript
- 値を
- Quick Fixの追加要素
- noImplicitAnyで怒られる箇所にコードからの利用箇所に従って型注釈を補う https://github.com/Microsoft/TypeScript/pull/14786
- デコレータを関数呼び出ししたうえで使わないといけなさそうな場合に変換を入れてくれる Invoke uncalled decorators
-
@types
が利用であればインストールしてくれる Install from @types
- C#的な
#region
をアウトラインの構成要素としてサポート Support outlining for // #regions - 型定義のコンテキストで
export default
に対して任意の値を指定できなくなった (Add error for using generalized expressions with export assignments in ambient contexts)[https://github.com/Microsoft/TypeScript/pull/18444] - 直積型(Intersection types)で結果が空になる場合
never
になるようになった Remove empty intersection types in unit types
関数の引数の型についてのチェックを強化
いわゆる 共変 反変(Contravariant)と言われるやつです。
元々、関数のパラメタについて、TypeScriptは共変&反変(Bivariant)だったのが改められました。
class Animal {
bark() {
return "???";
}
}
class Cat extends Animal {
name: string;
bark() {
return `${this.name} "meow..."`;
}
}
// エラーとして検出できるようになった例
let f1: (x: Animal) => string = x => x.bark();
let f2: (x: Cat) => string = x => x.bark();
f1 = f2; // --strictFunctionTypes でエラーになるようになった
f2 = f1; // これは前も今もOK
// これは問題のおこらない操作
let objA: Animal = new Cat();
// しかし関数の引数となるとそうもいかない
// 今まではこの手のミスが検出できなかった
f2 = (x: Cat) => `${x.name} is so pretty!`;
f1 = f2; // f1 の引数は Animal ... nameプロパティがない!
// undefined is so pretty! と出力されてしまう…
console.log(f1(new Animal()));
// こういうのもコンパイルエラーにできるようになった
let toLower = (s: string) => s.toLowerCase();
let p = Promise.resolve<string | undefined>(undefined);
p.then(toLower);
--strictFunctionTypes
オプションとしてこのチェックが導入されました。
--strict
オプションを指定するとこのオプションも指定したことになるように変更されました。
これはBREAKING CHANGEとなります。
基本的にはこのオプションを有効にしたほうがよいでしょう。
もし、プロジェクトがこの制約に引っかかってしまい、一時的に回避したい場合は "strictFunctionTypes": false
を指定すると --strict
が有効な場合でもチェックを迂回することができます。
タグ付きテンプレートリテラルがキャッシュされるようになった
まぁタイトルのまんまです。
// query関数 か templateObjectFactory関数 のどちらかにexportが指定されている必要がある(ECMAScriptの仕様ではない)
export function query(x: TemplateStringsArray, ...params: string[]) {
return x;
}
function templateObjectFactory() {
return query`/api/foo?name=${"foo=bar"}`;
}
// これがtrueになるようになった
console.log(templateObjectFactory() === templateObjectFactory());
// 同じ文字列を元にしても別のものが出てきちゃう…(辛い)
let escapedUrl1 = query`/api/foo?name=${"foo=bar"}`;
let escapedUrl2 = query`/api/foo?name=${"foo=bar"}`;
console.log(escapedUrl1 === escapedUrl2);
lit-htmlというツールで問題になるそうで、そこで問題なくなる範囲で頑張って直したっぽいですね…。
ECMAScriptの仕様との互換性を改善しただけで、完全に準拠できるようになったわけではないようです。
エラーメッセージの各国語ローカライズがnpmにもshipされるようになった
VisualStudioバンドルのものには結構前からついてた気がするやつです。
リポジトリには入ってたんですがnpm経由で入れるTypeScriptにはバンドルされていませんでした。
$ tsc
strictFunctionTypes.ts(16,1): error TS2322: Type '(x: Cat) => string' is not assignable to type '(x: Animal) => string'.
Types of parameters 'x' and 'x' are incompatible.
Type 'Animal' is not assignable to type 'Cat'.
Property 'name' is missing in type 'Animal'.
$ tsc --locale ja
strictFunctionTypes.ts(16,1): error TS2322: 型 '(x: Cat) => string' を型 '(x: Animal) => string' に割り当てることはできません。
パラメーター 'x' および 'x' は型に互換性がありません。
型 'Animal' を型 'Cat' に割り当てることはできません。
型 'Animal' にプロパティ 'name' がありません。
こんな感じ。
基本的には英語でやっていきたいですが、見慣れないエラーが出た時はあると役に立つかもしれませんね。
tsconfig.json
に "locale": "ja"
を指定することはできないようです。Issue
.ts でも // @ts-ignore
が使えるようになった
--allowJs --checkJs
の時に使えるやつがTSでも使えるようになった。
{
// @ts-ignore → error TS6133: 'v' is declared but never used.
let v = 1;
}
// @ts-ignore → error TS6133: 'p' is declared but never used.
function func(p: string) {
}
// コンパイルエラーすら無視できてしまう…!
// @ts-ignore → error TS2345: Argument of type '1' is not assignable to parameter of type 'string'.
func(1);
// 流石に構文エラーは弾いてくれる
// @ts-ignore
));
無茶苦茶やな…!
--watch
時のビルドが早くなった
公式の説明によると、ファイルに変更があった時に全部コンパイルするのをやめて、変更があったファイルとそれに関連のあるファイルだけをコンパイルしなおすようになったそうです。
今までやってなかったんかーい!
CommonJSだと依存性解析ができない気がするので、ECMAScript moduleを使っていないと恩恵を受けられないのではないでしょうか(未確認)
他のツール(webpackとかgulpとか)と組み合わせて使っている場合、Roadmapの2.7に Support for incremental builder compiler API
があるので、もう少し待つ必要があるようです。
未使用変数のチェック強化
未使用変数の検出で、2.5までは値の更新(Write)でも未使用フラグを外せてたけどそれができなくなりました。
{
// TypeScript 2.5.1 までは値の更新があれば誤魔化せてた
// 今はエラーとなる! error TS6133: 'v' is declared but its value is never read.
let v = 1;
v = 2;
}
サンプルコードでブロッグを作ってるのはこのバグのため。通常exportとかimportが1つはあるので気にしないでよい。
各モジュールでexportされてる要素が入力補完候補に出るようになりimport句も挿入されるようになった
リファクタリング 値を const
として切り出せるやつ
任意の値をconstとして切り出せるやつです。
VSCode - Insiders で値を範囲選択してQuick Fixで Extract to constant in enclosing scope
でできるやつっぽい。
リファクタリング JSDocの型注釈をTypeScriptの型注釈に変換
そのまんまです。
公式のgifアニメ見ればわかると思います。
動画で使ってたコードを手元(VSCode - Insiders)で試す用として貼っときます。
/**
* @param elem {HTMLElement}
*/
function boundingHypotenuse(elem) {
let { width, height } = elem.getBoundingClientRect();
return hypotenuse(width, height);
}
function hypotenuse(x, y) {
return (x ** 2 + y ** 2) ** 0.5;
}
Annotate with type from JSDoc
の他に 次で紹介する Infer parameter types from usage.
もできてて、とりあえずJSからガガッと移行したい時に便利そう…
Quick Fix noImplicitAnyで怒られる箇所にコードからの利用箇所に従って型注釈を補う
前述の奴です(相互再帰)。
関数の引数の場合、関数内の実装内部での利用例ではなく、関数の呼び出し部分から型を推論するようです。
この時、該当の関数をexportしている場合、このQuick Fixは使えなくなるようです(ちょっとがっかり)。
// この場合、x, y ともに number | string に推論される
function plus(x, y) {
return x + y;
}
plus(1, 2);
plus("a", "b");
Quick Fix デコレータを関数呼び出ししたうえで使わないといけなさそうな場合に変換を入れてくれる
これがエラーになるようになった。
function deco(): (...args: any[]) => any {
return () => {};
}
// error TS1329: 'deco' accepts too few arguments to be used as a decorator here. Did you mean to call it first and write '@deco()'?
@deco
class Foo {
}
Quick Fixも提供されています。
Quick Fix @types
が利用であればインストールしてくれる
完全に最高のやつです。
npmでもyarnでもとりあえず動くっぽい。
npmの場合、package.jsonとpackage-lock.jsonが更新された。
yarnの場合、package.jsonだけが更新された(?)。
普通にパッケージが存在しない場合はQuick Fixが利用可能にならないっぽいです。
すげーざっと変更差分みた感じ、ネットワークアクセスが絡むコードはエディタ側に処理が投げられてるのでは…?(エディタ側の対応がめんどくさいやつ)
C#的な #region
をアウトラインの構成要素としてサポート
なんかあるっぽいです(さほど興味がない)。
型定義のコンテキストで export default
に対して任意の値を指定できなくなった
declare module "foo" {
export default 1 + 1; // should not be allowed
}
的な型定義を手で書くとエラーになるようになったらしいです。
今までってそこ式が書けたんだ…。
直積型(Intersection types)で結果が空になる場合 never
になるようになった
// これは never になる
type T1 = ("A" | "B") & ("C");
// これは "A" になる
type T2 = "A" | "B" & "C";
// これは `"a" & 1` のまま
type T3 = "a" & 1;
要するに型の簡約の計算の量を減らすためにneverに落とせる箇所で早めに落とせるようにした、という感じっぽい。
エラーメッセージがわかりやすくなる…というのもありそう。