
Javascriptを十分に理解しているから、TipeScriptなんて、へのカッパ!
・・・と考えているそこのフロントエンジニアの人。
実は、TSには、JSを熟知していればいるほど、陥りやすい罠があります。
初級エンジニアよりは上とあぐらをかいて、TS学習をおろそかにしてきた、自分のようなタイプの人がちゃんと理解した方がいい内容を学習していきましょう。
1. any地獄(消える型の連鎖)
「any」は便利に見えるが、触れたもの全ての型情報を破壊するウイルスのようなものとも言える。
function parse(data: any) {
return data.user.name;
}
解説
上記コードの場合、
型エラー出ないと言う罠です。
→ 実行時 data.user が無いと落ちる
→ TSの恩恵が0になる
回避方法
・最低限 unknown か Record<string, unknown> にする。
・zod, io-ts, ts-pattern などでバリデーションする。
ルール
anyは最後の手段!
2. unknown放置問題(型確認忘れ)
unknown は安全だけど、扱い忘れると永遠に使えない値になる。
const v: unknown = JSON.parse("{}");
v.name; // × Property 'name' does not exist
解説
上記コードの場合、普通にJS脳だと「なんでアクセスできないの?」となるんですが、TSでは、エラーになります。
回避方法
回避術(型ナローイング)
if (typeof v === "object" && v && "name" in v) {
console.log(v.name);
}
ルール
unknownは「安全な入口」だが出口はちゃんと自分で作ることを忘れずに。
3. Optional なプロパティの油断
type User = { name?: string }
const u: User = {}
console.log(u.name.toUpperCase()); // ×
解説
TSは u.name を string | undefined と推論する。
回避方法
Non-null assertion !(乱用注意)
u.name!.toUpperCase();
この書き方の場合、便利だが事故りやすい。
if (u.name) {
u.name.toUpperCase();
}
こう書いた方が、正式に回避できる。
ルール
Optionalには常にundefinedを意識する。
4. Union型の過信(実行時意識ゼロ問題)
type Id = string | number;
function print(id: Id) {
console.log(id.toFixed(2)); // ×
}
解説
TSは「numberかもしれないし、stringかもしれない」と判断する。
回避方法
if (typeof id === "number") {
console.log(id.toFixed(2));
}
分岐 or in, typeofなどを使って、型判定をちゃんと行うことで回避できます。
ルール
TypeScriptが保証するのは
型であって、
動作保証ではない。
5. const推論を理解してないケース(リテラル vs 一般型)
const a = "hello"; // 推論: "hello" (literal)
let b = "hello"; // 推論: string
解説
type Status = "ok" | "ng";
const s = "ok";
const x: Status = s; // × 補完一致してても literal ではない
このコードの意味に気づかずハマることがある。
次のコードも同意。
type Status = "ok" | "ng";
const s = "ok";
const x: Status = s; // × 補完一致してても literal ではない
回避方法
const s = "ok" as const;
as const を理解する必要あり
ルール
定数扱いならas const。
6. object と {} と Record の違いが曖昧
let a: object = []; // OK
let b: {} = null; // × nullは入らない
let c: Record<string, unknown> = {}; // OK
解説
object は「プロパティを持つ何か」
→ {} は「null/undefined以外」
→ Record は「キー型 × 値型」
回避方法
nullはnull型を使うか、unknown,anyなどでちゃんと定義するのが望ましい。
Javascriptの処理で何でもnullを入れまくっていた人には、少しキツい仕様かも。
ルール
オブジェクト表現は意図に合わせて使い分ける。
7. Date, JSON, schema の型境界忘れ
type User = { createdAt: Date }
const res: User = await fetch().then(r => r.json()); // × ここで型崩壊
解説
API経由では Date は絶対に string として返る。
TSは、Dateだと思ってるけど、実際はstringが返ってくる。
回避方法
type UserDto = { createdAt: string }
ルール
変換レイヤー設ける。
そして、SQLの型とは違う点に要注意。
TSの罠と本質について
TSの罠は「型が正しいと思って進むと、実行時に破綻するタイプのもの」。
JS脳のままだと「型がある気がするだけ」になる場合がある。
TypeScriptの本質としては、型は正しい動作の保証ではなく、単なる型宣言だということを理解しておきましょう。
また、TSは
正しい使い方を強制する道具としてのお作法的な機能。
そして、設計思想をコードに縛り付けるための仕組みなのだと言う認識を持ちましょう。
あとがき
今回紹介したような失敗例を見ると、Javascriptがすこぶる得意な人でも、失敗してしまう落とし穴がたくさんあることがよくわかった。
それだけJavascriptは、これまで型にユルい言語だったことがよくわかる。
きっとこうしたTSのことを理解することで、モダンJSを書くときでも、違った意味での精度の高いコードが書けるようになるし、
コンパイル言語への取り組みも、モチベーションが湧いてくるようになるはず。
0 件のコメント:
コメントを投稿