as const satisfiesの話

  • #開発
  • #typescript

こんにちは。CURUCURUエンジニアの長尾です。 最近あまりネタがなくなってきましたので勉強がてらtypescriptの文法のお話をしようと思います。


概要

as const satisfiesのお話。

as const とは

as constを使うとオブジェクトプロパティがreadonlyになるという機能です。

使い方

const test = {
  yamada: 100,
  suzuki: 90,
  saitou: 80,
} as const;

上記のように定義した場合、testオブジェクトは定数なのでそのままtest.yamadaとかで使えますよね。

そして、この変数を使ってunion型を作りたいときがあった場合、以下のようにすると思います。

type TestKey = keyof typeof test;

これは

type TestKey = 'yamada' | 'suzuki' | 'saitou';

と同じ意味です。

では、これのvalue版はどうかというと。以下です。

type TestValue = typeof test[TestKey];

これは

type TestValue = 100 | 90 | 80;

と同じ意味です。

ここで面白いのがas constをつけない場合にvalueのunion型を作ると

type TestValue = number;

と同じ意味になってしまい、推論結果が意図したものになっていないです。(number型を意図していたのなら話は別です) これは「Widening」と呼ばれる型の拡張です。 as constをつけることで型の拡張を止めることができるため正しく型を作ることができます。

satisfies

typescript4.9から追加されたものでsatisfiesというものがあり、これをas constと使うととても型安全になります。

as constはWideningを防ぐ効果がありましたが、satisfiesは定義した型を満たしているかどうかをチェックすることができます。

型のチェックがないのでendouさんにstring型の"80"を与えることが出来てしまいます。
const test = {
    yamada: 100,
    suzuki: 90,
    saitou: 80,
    endou: "80",
};

なのでこれを

type Test = { [key in string]: number };
const test = {
    yamada: 100,
    suzuki: 90,
    saitou: 80,
    endou: "80",
} satisfies Test;

とすることで型安全になります。

satisfiesと同じような意味で違う文法として型注釈があります。

const test: Test = {
  yamada: 100,
  ...
}

と書く方法ですね。

型注釈では、例えば

type Test = { [key in string]: number | number[] };
const test: Test = {
  yamada: 100,
  suzuki: 90,
  all: [100, 90],
};

と書いた場合、allに対してmapを使うことが出来ません。

function GetPerfect() {
  return test.all.filter(t => t === 100);
}

をしたくても プロパティmapはnumber型に存在しませんとエラーが出ます。

これを解消するにはsatisfiesを使います。そうすると型の推論結果(ここではallがnumber[]であること)を保持してくれるため、エラーがでなくなります。

言いたいこと

as const satisfiesを使うと型安全かつwidening防止でより安全にコーディングできそうなので、利用場面をきちんと考えて使っていきたいですね。

まとめ

ということで今回はtypescriptの文法のお話でした。 typescriptは日々新しい機能が増えていくので使いこなせるようにならないといけないですね。

以上、長尾がお届けしました。

メンバー募集

CURUCURUでは開発メンバーを募集中です。 CURUCURUの開発に興味があったり、モダンな開発環境で挑戦してみたいという方がいましたら、ぜひこちらも覗いてみてください!