こんばんわ、hisayukiです。
先日、Qiitaで非常に良記事を見つけました。
敗北者のTypeScript!!
タイトルのインパクトだけでざっと読んでみたところ
「ぁー、やっちゃってるなぁ・・・・」
ってところがいくつも出てきて、尚且strictオプションを付けずに今まで作っていたことに気づいた。。。
もともと手探りで調べながら作っていたし、せっかくなので知識を深めるためにもリファクタリングすることにしました!
さっそくやってみた
というわけで早速、tsconfig.tsに“strict”: trueを追加
{
"compileOnSave": false,
"compilerOptions": {
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es6",
"strict": true,
"typeRoots": [
"node_modules/@types"
],
"lib": [
"es2017",
"dom"
]
}
}
そして、ng serveで実行したところ・・・・
見事に真っ赤っ赤!!!
敗北者でした/(^o^)\
ファイル数としては約20ファイルほど修正することに・・・💦
正直、見なかったことにしようか迷いました・・・・w
ですが、今後TypeScriptはもっと伸ばしたいスキルなのでとりあえず初めてみました❗
主な修正点
そんな軽いノリでやりましたが、strictオプションはなめてるの?ってくらいの感じで、容赦なくボコられました。
そんな攻撃を大まかに分けてみました。
外部ライブラリを利用時のスニペット貼り付け
コピペプログラミングしている人は即死するやつです/(^o^)\
SaaSを使ってたりすると、フロント側に埋め込むスニペットが用意されてたりします。
もちろんそのまま十分使えるのですが、そもそもTypeScript用になってるわけではないので型チェックで赤くなります。
strictオプションはAny型を駆逐するので、型無しの変数は即エラーです。
なので、JSのスニペットなどで型がなければ動いてるソースコード追っかけて型を付けましょう。
jQuery
そのまま使うと赤くなります。$('.***')
このDOMの指定の書き方を許してないからです。
気分としてはTypeScriptに完全に置き換えたかったのですが、アンカーやフェードイン、アウトなどのAnimation周りはちょっと面倒だったので少し手抜きしました。
$ npm install --save-dev @types/jquery
これで$('.***')
での指定が許可されます。
FormGroup FormControle
フォームからのデータ取得です。
signUpFrom: FormGroup = this.formbuilder.group({
name: [''],
mailAddress: [''],
password: [''],
}
registration() {
if (this.signUpFrom.invalid) { return; }
this.signUpService.signUp = this.toSignUp(this.signUpFrom);
this.router.navigateByUrl('/registration-confirmation');
}
private toSignUp(form: FormGroup) {
form.get('name').value,
form.get('mailAddress').value,
form.get('password').value,
}
例えばこんな感じにget('***')
を使うと、中身Nullだったらどうすんの?って怒られます。
signUpFrom: FormGroup = this.formbuilder.group({
name: [''],
mailAddress: [''],
password: [''],
}
registration() {
if (this.signUpFrom.invalid) { return; }
this.signUpService.signUp = this.toSignUp(this.signUpFrom.controls);
this.router.navigateByUrl('/registration-confirmation');
}
private toSignUp(controls: {[key: string]: AbstractControl}) {
controls.name.value,
controls.mailAddress.value,
controls.password.value,
}
こんな感じでFormGroup
のControlsをJSONで渡し、受け取る側で型指定します。
こちらの書き方だと怒られなかった(゜゜)
String Enum
これが一番困った/(^o^)\
そもそもとして、JavaScriptにEnumの概念はない。
TypeScriptがEnumで書いたものをJavaScriptで同じ動作をするように書き換えてくれてる。
今回やりたかったのはEnumの逆引きです。kye => value
ではなくvalue => kye
、他の言語では割と普通にできたりする。
が、JavaScriptにEnumの概念はない。(2回目)
ちなみにTypeScriptで逆引きが出来ないわけではない。
というかString Enum
でString型で引数を受け取り、Enumに対して連想配列みたいなとり方をすればvalue => kye
が出来ます。
export enum AccountRole {
USER = 'USER', // ユーザー
ADMINISTRATOR = 'ADMINISTRATOR', // 管理者
NONE_LOGIN = 'NONE_LOGIN' // ログインしていない
}
export namespace AccountRole {
export function parseAccountRole(roleString: string): AccountRole {
const role: AccountRole = AccountRole[roleString];
if (role === undefined) {
return AccountRole.NONE_LOGIN;
}
return role;
}
}
サンプルソースはアカウントの権限をEnum化したもの。
‘USER’という文字列からAccountRole.USERがこれで取れてます。
しかし、JavaScriptにEnumの概念はない。(3回目)
出来ているのだ、動作もしているのだ・・・
しかし、strictオプションはこれを許さない。
error TS7015:Element implicitly has an 'any' type because index expression is not of type 'number'.
直訳:「indexに使ってるのNumberじゃないからAnyな」
strictオプションはEnumのIndexはNumber型しか許さない。
そしてそれ以外の型を使った場合は、すべてAny型にされる。
受け取り引数の段階でString型と宣言しているにもかかわらずだ。
そしてstrictオプションはAny型をすべて駆逐する。
すべてSwitch文に変えました・・・・
JavaScriptにEnumの概念はない・・・・(4回目)
まとめ
今回、敗北者のTypeScriptを見たことでリファクタリングするきっかけになりました。
なかなかヘビーなボリュームでしたが、おかげでTypeScriptのスキルが少しレベルアップ!
今回はTypeScript ver2.5でやってるので、今はまた変わってるかもですね。
最新が確か3.5とかなので・・・w
Enumの扱いも多少変わってくれてると嬉しいなぁ、動作はするんでせめてIndexにString使うのくらい許してほしい。
やっぱ、大変な思いをしてるときのが知識が貯まりますねw
strictオプションはぜひ最初から入れておくことをオススメします。
コメント