今こそ伝えたい!@ts-ignoreの魅力
2019/10/05
この記事のミッションステートメント
TSの型に疲れて生JSに戻ろうとしている人の足首を掴み、世の中にある生JSを一つでも減らす
対象読者
JSよりはちょっと硬いものを使いたい人
TSのd.ts定義で疲れてしまった人
TSは面倒と思っている人
非対象読者
「なんでこんなもの必要なの?」と思った人
多分それは幸せな環境です。大切にしましょう。
@ts-ignoreとは
あらゆる型の不整合を握りつぶす。
// @ts-ignoreとコメント形式で書くことで、次の行の型チェックがスルーされる
eslint-ignoreなどに近い
構文エラーは潰せない
babelなどを通してどうしてもOptional Chainingを使おうとしたことがあるが、やっぱりこれは無理
危険なのでは?
安全でないのは間違い無い
用法用量を守って正しく使いましょう
基本的にこれを使ってるところは別途UnitTestなどでカバーするのが望ましい
基本的な使い方
code:ts
// @ts-ignore
const foo : string = 10
foo.repeat(3) // コンパイル上はエラーにならない。実行時は当然エラー
「この行だけ!この行だけ通させて!」という場合
後でなんとかするTODO的な場合(ただこの場合type TODO = anyのテクニックのほうが良いかも)
@ts-ignoreが便利なケース
とりあえず古いJSコードを置き換えるとき(この使い方が大本命)
そもそものコードが何らかの事情でtscを通らないケースがある。
window.xxxx を利用しているケース
prototype拡張をしてる場合など(想像するだけで吐きそう・・・)
「まず初手としてTypeScriptをCI通します」というのをやるとき
ESLintの導入手法に非常に近い。
経験則だが、この手の改善するため「間違っているのはわかっていても通す」ということを優先するほうが良い
コードを書き換えてしまうのは、思わぬバグを誘発するので悪手になりがち
「本来通るはずがないとこをシメたらエッジケースでそれ以外の値が来ていた」とか
外部としてd.tsを整えるのは方向としてきれいだが、多くの歴史あるプロダクトはかなりの時間を要する。
その工数出る・・・?
時間を取られて疲弊して頓挫するなら「まず通す」は悪い手ではない。
直すにしてもanyをつけることでコードに手を入れるより、コメントの方がより変更は少なく安全側に倒せる
TODOに利用するAnyの代替
code:ts
// anyの場合
type SomeInterface = Any
const someItem : SomeInterface
someInterface.fooMethod()
例えばこれだとfooMethodがあろうがなかろうが警告は無い
これが@ts-ignoreでその宣言時のみにしていれば、その後処理では警告が走るのでマシになる
code:ts
interface SomeInterface {
fooMethod: () => { ... }
}
// @ts-inogre
const someItem : SomeInterface = generateTodoSomeInterface()
someItem.foooMethod() // 警告される!
Unit Testでの仕様
テストだと「インターフェースや細かい引数については観点ではなく、ロジックに注力したい」のようなケースはある。
特に開発の初期で、インターフェースはある程度決まってるけど実装未着手、先にテストだけ回したいみたいなことがしたいときにはわりと便利
code:ts
// このケースではanyでも良いが・・・
test("Some Test (any pattern)", () => {
const mockInput : any = { foo: "baz" }
expect(someComplexArgsMethod(mockInput)).toEq("bee")
})
// ts-ignoreも宣言部を省略できて便利になる
test("Some Test", () => {
// @ts-ignore
expect(someComplexArgsMethod({ foo: "baz" })).toEq("bee")
})
TypeScript本体の更新 / DefiniteryTypedの更新がライブラリに対してズレのあるケース
DefiniteryTypedの型はどうしてもズレが起きる
d.tsで上書きするなどは出来るが、結構ツライ
ツライ上に、自分が正しい型を書いている保証がどこまであるか?
例えばReactで string やnumber[]を返すものはFunctionalComponentとして利用できる(実際に利用するケースがあるかというとそれは置いといて。
code:typescript
const StringNode = () => {
return "hello"
}
const ArrayNode = () => {
}
const App = () => (
<div>
<StringNode/>
<ArrayNode />
</div>
)
が、これは下記のようなエラーとなる
JSX element type 'Element[]' is not a constructor function for JSX elements.
順当に行けばFragmentを使ってこんな書き換えを求められる。
code:typescript
const StringNode = () => <>{"hello"}</>
const ArrayNode = () => (
<>
<span key={i}>{i}</span>
))}
</>
)
Arrayの方はもはや原型が無くて悲しい気持ちになる
なのでこうする。
code:typescript
// @ts-ignore @tyes/nodeが修正されたらts-ignore不要
const StringNode: FC<{}> = () => {
return "hello"
}
// @ts-ignore
const ArrayNode: FC<{}> = () => {
}
ローダーの兼ね合いでTSから読み込めないんだけどそれ専用の型を書くほどでもないケース
code:ts
// @ts-ignore
import pingSound from "./assets/ping.mp3"
通常はdeclare module "*.mp3" { などをする
ただ一、二箇所しか使ってないときに割と腰が重い。
そもそもここの話をしだすと「import/from形式でスクリプト以外のファイルを読み込んでること自体がいかんでしょ」と思ってしまっててだったら気にしたくないみたいな気持ちが
noImplictAnyはしたいけど型定義の無いライブラリを少量だけ使いたいケース
noImplictAnyは基本使いたいとする。
ただ、いくつかのライブラリで存在しないことがある(これはしょうがない)
d.tsを書くのでもいいけど、例えば一箇所だけ使いたいみたいなときにはコスパが悪い
仮に自分で書くとしても自分の書くd.tsを信用できない(特に初学者は悩むところ)
型がアップデートしたときに追従されてる?そこテストできてる?
ー>だいたいできてない。
最近は割と型定義のあるライブラリが潤沢で、無いライブラリが少数派でそれらを利用するときあまりエネルギーはかけたくない
だったら別途Unit Testでカバーする方が良いじゃんというケースは少なくない
UnitTestでカバーするかd.tsでカバーするかの選択肢
code:ts
// @ts-ignore
import Uifx from "uifx"
const audio = new Uifx("")
JS/TS面の機能に関わるケース
(なんだったか忘れたけどdynamic importらへんで使った記憶がある)
select / radioボタンなどで事前条件が自明である場合
下記のような場合。onChangeのe.target.valueはstringになるが、自明である場合は少なくない
code:typescript
import React, { useState } from "react"
enum FruitEnum {
"banana",
"apple"
}
type FRUITS = keyof typeof FruitEnum
const fruits = Object.keys(FruitEnum).filter((k) => isNaN(Number(k)))
export const FruitSelect = () => {
return (
<select
onChange={
// @ts-ignore
(e) => setValue(e.target.value)
}
value={value}
{fruits.map((key) => (
<option key={key} value={key}>
{key}
</option>
))}
</select>
)
}
ちゃんとしようとすると多分↓こうなる
@ts-ignoreとゆかいな仲間たち
@ts-nocheck
ファイルごとチェックしない。
これまでJSファイルでしか使えなかったが、3.7からTSファイルでも利用可能に
@ts-check
JSファイル専用
JSでcheckJSしてるときに、// @ts-checkしてるファイル見てくれる
こうなればもっといいのに! @ts-ignore!
一部のエラーだけ握りつぶすts-ignore
型だけ潰したくて、同名のエラーは潰したく無いなど。うっかり別なとこまで巻き込まれるケースある
--ignore-ts-ignore オプション
あくまで補助輪なので定期的に掃除したい気持ち
「ここもう不要じゃね?」ってなったときに消してくれる機能
初期導入用に、勝手に // @ts-ignoreつけてくれる機能
ブロックスコープな // @ts-ignore
提案されてる
でも// @ts-nocheckがあれば十分ってなっていきそう