TypeScript 4.9からはsatisfies演算子でパターンの網羅性をESLintと共存しつつ簡単にチェックできる
背景
私はAWS CDKを利用する時にデプロイステージをよく以下のように分けています。
export const stages = ["deve", "stag", "prod"] as const;
/**
* 開発用、本番用などを分けるステージ
* {@link https://docs.aws.amazon.com/cdk/api/latest/docs/@aws-cdk_core.Stage.html} は一切関係がない
*/
export type Stage = typeof stages[number];
こうするとstagesを反復することで簡単に全てのステージに対してスタックを作ることが可能ですし、その名前にStageをstringとして埋め込んで区別することが可能です。
引数ではstringではなくStageを受け取ってstage === "deve"のように比較すれば、リテラル型となっているのでtypoを防ぐことが可能です。
問題
この間switch(stage)で処理を分けていた時、
ESLintが怒ってくるため、
TypeScriptでas constで作ったリテラル型の値をswitchしてもESLintがdefault-caseつけろって怒ってくる
— エヌユル (@ncaq) 2023年2月17日
JavaScriptでも動くように文字列で型を作るしかないからこういう齟齬が生まれるんだよね
自分が求めるのはswitchがHaskellのパターンマッチみたいにリテラル型が変更したらビルドエラーになること
TypeScriptのexhaustiveness checkをスマートに書くみたいな黒魔術を見てビビってました。
しかしTypeScript 4.9からは割と簡単に書けることを教えてもらいました。
defaultでsatisfies neverするかneverを受け取る関数に渡すかで網羅してるかのビルドエラーは出せると思うんですが、それじゃダメな場合でしょうか?
— Yano (@yuki_ycino) 2023年2月17日
今回の場合は単純に、
switch (stage) {
case "deve":
return deve;
case "stag":
return stag;
case "prod":
return prod;
default:
throw new Error(stage satisfies never);
}
とすれば良かったです。ちゃんとcase網羅しないとエラーになります。
lspの読み込むバージョンが古くてトラブル
コマンドライン上のlintではTypeScriptとprettierをアップデートすることで問題なくなったのですが、 Emacsのlsp-modeのTypeScript lspがエラーを出し続けて、 language serverを更新してもエラーを出して困惑しました。
しかし*lsp-log*を見て納得しました。
Using Typescript version (user-setting) 4.8.4 from path "/home/ncaq/.config/yarn/global/node_modules/typescript/lib/tsserver.js"
— エヌユル (@ncaq) 2023年2月17日
はい
何故かグローバルにインストールされたTypeScriptコンパイラを優先的に読み込んでしまっていました。グローバルに使うこともないやと最近グローバルにインストールするスクリプトからもTypeScriptコンパイラは削除したので、
yarn global remove typescript
で解決しました。
雑にts-nodeを使いたくなったらどうしようかなと思いましたが、それぐらいならyarn dlxを使えば良いですかね。
これバグ報告した方が良いのだろうか、しかしもうグローバルにインストールするのはとにかくやめろみたいな風習がありますし悩みどころですね。
as constが既についている配列にsatisfiesを付けるモチベーションが分からない
記事を漁っていると私のstagesみたいな変数にas constだけではなくsatisfiesを付けるパターンを見ましたが、
as constしてる時点でリテラル型になっているため、オブジェクト型ならともかく配列型につけるモチベーションは今ひとつ分かりませんでした。
hatena-bookmark