⚖️

TypeScriptのas constを知ろう

に公開

概要

別記事を書くにあたり as const を理解しようの会

as constとは

コンストアサーション

😅

なので、まずはアサーションの説明から。

アサーションとは

asを用いて明示的に示すこと。
参考: https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions

const hoge: string = "hoge"; // 通常の型指定(型アノテーション)
const fuga = "fuga" as string; // asを使った型指定(型アサーション)

これを見ても何が違うんやねん、ってなると思うので別例を紹介。

const inputElement = document.getElementById("inputId");
console.log(inputElement.value);

上記のようにinput要素を取得した場合、console.log()の部分で プロパティ 'value' は型 'HTMLElement' に存在しません。 というエラーが発生する。
じゃあ今まで通り型を記載しようとして

const inputElement: HTMLInputElement = document.getElementById("inputId");

と書くと 型 'HTMLElement | null' を型 'HTMLInputElement' に割り当てることはできません。 というエラーが発生してしまう。

この状態は「いや、TypeScript的にはHTMLElementかもしれないけどinput要素を持っているから絶対HTMLInputElementなんだ!」となっている。
その場合に

const inputElement = document.getElementById("inputId") as HTMLInputElement;
console.log(inputElement.value);

とすることでこちらからTypeScriptにHTMLInputElement型であるよということを教えてあげる。

なので、ざっくりと説明するとTypeScriptでは分からない型情報がある場合にこちらから「これだから!信じて!!」と教えてあげるというイメージ。
アサーションの日本語訳は「主張」なのでこちらから主張しているということでアサーションって名付けているのかも。

今回は as const なのでconstだよ!信じて!!とTypeScriptに教えてあげている。

(再び)as constとは

as constとは厳密には型アサーションではなく コンストアサーション(定数アサーション) と呼ばれているらしい。
「constだよ!信じて!!」は厳密には「const型だよ!信じて!!」ではなく 「constっぽく振る舞わせてあげて!お願い!!」 みたいな感じに近い。

具体的にはリテラル型にするために使用する。

let hoge = "hoge"; // string型
let fuga = "hoge" as const; // リテラル型 "hoge"

hoge = "fuga"; // ✅string型なのでOK
fuga = "fuga"; // 🚫リテラル型"hoge"に当てはまらないのでNG

constとas constの違い

リテラル型にするだけという意味では const hoge = "hoge";と変わらないのでは??と思いそう。

const hoge = "hoge";
const fuga = "hoge" as const;

type Hoge = typeof hoge; // リテラル型 "hoge"
type Fuga = typeof fuga; // リテラル型 "hoge"

const hoge2: Hoge = "fuga"; // 🚫"hoge"じゃないのでNG
const fuga2: Fuga = "fuga"; // 🚫"hoge"じゃないのでNG

確かにそんなに変わらないかも。
でも別のパターンを見てみる。

配列での利用例
const hoge = [1, 2, 3];
const fuga = [1, 2, 3] as const;

type Hoge = typeof hoge; // number[]
type Fuga = typeof fuga; // readonly リテラル型 [1, 2, 3]

const hoge2: Hoge = [1, 2]; // ✅OK
const fuga2: Fuga = [1, 2]; // 🚫NG

※ readonlyの説明はこちら

オブジェクトでの利用例
const hoge = { name: "ほげ太郎", message: "こんにちは、ほげ太郎です。" };
const fuga = {
  name: "ほげ太郎",
  message: "こんにちは、ほげ太郎です。",
} as const;

type Hoge = typeof hoge; // { name: string; message: string }
type Fuga = typeof fuga; // { readonly name: string; readonly message: string }

// ここは型を満たすのでどちらも格納できる
const hoge2: Hoge = { name: "ほげ太郎", message: "こんにちは、ほげ太郎です。" }; 
const fuga2: Fuga = { name: "ほげ太郎", message: "こんにちは、ほげ太郎です。" };

hoge.name = "ほげ次郎";  // ✅constはオブジェクトのプロパティにアクセス可能
fuga.name = "ほげ次郎"; // 🚫readonlyなのでアクセスできない

このように配列やオブジェクトの場合に違いが出てくる。
配列をconst指定する場合にリテラル型でないが、as constを付けるとリテラル型になる。
また、オブジェクトをconst指定する場合にプロパティの更新が有効だが、as constを付けると更新ができない状態になる。
※ constでプロパティの更新ができる旨はここに記載

ということで、constは再代入を非許容にする場合に利用する、as constはより厳密な型定義をする際やプロパティ含めて再代入を非許容にする場合に利用すると考えるといいかも。

まとめ

as constは、値の型推論の厳密性を最大限に高めるためのアサーション。
これによってオブジェクトや配列ができる限りリテラル型になり、オブジェクト全体や配列全体がreadonlyになることでプロパティ含めて再代入が非許容になる。

JavaScriptを学んだ人はconst = 定数 = 変えたくない値に使うというイメージだが、as constを使うとより厳密に変更を防止できるって感じぽい。

Discussion