Programming PR

【TypeScript】as constの特徴3つと2つの注意点

【TypeScript】as constでリテラル値の型安全性を高める
記事内に商品プロモーションを含む場合があります

こんにちは!
今日はTypeScriptの便利な機能の一つ、as constについて学んでいきましょう。

TypeScriptを使うと、JavaScriptに型安全性という強力なツールを加えることができますよね。
それによって、コードのバグを早期に発見し、より信頼性の高いプログラムを書くことが可能になります。

特に、as constはTypeScriptの型システムをさらに強化する小さなキーワードですが、その効果はとても大きいんです。

この機能をマスターすることで、皆さんのTypeScriptコードはより厳密に、そして柔軟になります。
今回は、そのas constがどのようなものなのか、どんなメリットがあるのかを一緒に見ていきましょう!

as constとは

as constは、TypeScriptで変数やオブジェクト、配列に使うことができる特別な型アサーションです。
このアサーションを使うと、TypeScriptはより具体的な型を推論するようになります。

例えば、通常JavaScriptでオブジェクトや配列を定義すると、そのプロパティや要素は変更可能(mutable)と見なされます。
しかし、as constを使うことで、それらが変更不可能(immutable)なものとして扱われ、さらに型がより厳密に推論されるようになるのです。

簡単に言うと、as constは「この値は絶対に変わらないよ」とTypeScriptに教えてあげるキーワードです。
これにより、変更不可の値として扱われ、型推論もより正確になります。

as constの効果

as constを使うことで、TypeScriptの型推論がどのように変わり、どんなメリットがあるのかを見ていきましょう。
この効果は主に次の三つのポイントに分けられます。

  1. Wideningされなくなる
  2. as constを利用することで、リテラル型が広がることなく、その値自体の型として扱われるようになります。
    これにより、より厳密な型チェックが可能となります。

  3. オブジェクトのプロパティをreadonlyにする
  4. オブジェクトにas constを適用すると、そのオブジェクトの全てのプロパティが読み取り専用(readonly)として扱われるようになります。
    これは、不変性を保証し、予期せぬ変更を防ぐのに役立ちます。

  5. 配列がreadonlyのタプル型になる
  6. 配列にas constを使うと、その配列は変更不可能なタプル型として扱われます。
    各要素の型が保持され、配列の長さも固定されます。

これらの効果により、as constはコードの安全性と予測可能性を大きく向上させることができます。
それでは、これらの効果を一つずつ詳しく見ていくことにしましょう。

Wideningされなくなる

TypeScriptでは、変数やリテラルによって型が「広がる」(Widening)ことがあります。
これは、例えば文字列リテラルを変数に割り当てた時に、その変数が具体的なリテラル型ではなく、広い「string」型として扱われる現象です。
しかし、as constを使うと、この「広がり」を防ぐことができます。

例を見てみましょう。


// string型に推論される
let withoutAsConst = "Hello";
// "Hello"という文字列リテラル型になる
let withAsConst = "Hello" as const;

ここで、withoutAsConstは通常の文字列として扱われ、「string」型になります。
これに対して、withAsConst「Hello」という具体的な文字列リテラル型として扱われます。

この差異は小さいように見えますが、TypeScriptを使って型安全性を確保しようとする時には、この「細かさ」が非常に重要になります。
なぜなら、より具体的な型を使うほど、型の不一致によるエラーをコンパイル時に発見しやすくなるからです。

as constを使うことで、意図しない型の「広がり」を防ぎ、コードの安全性を高めることができるんですね。
これにより、型に関するバグを減らし、より堅牢なアプリケーションを構築する手助けとなります。

Wideningについては、こちらの記事で詳しく解説していますので、詳しく知りたい方はこちらも参考にしてください。

【TypeScript】Wideningってなに?型の拡大の落とし穴
【TypeScript】Wideningってなに?型の拡大の落とし穴こんにちは! 今日はTypeScriptで重要な概念の一つ、「Widening(型の拡大)」についてお話しします。 TypeScri...

オブジェクトのプロパティをreadonlyにする

オブジェクトはconstで宣言すると、オブジェクト自体を変更することができなくなりますが、プロパティを変更することはできます。


const person = {
  name: "Taro",
  age: 30,
};

// オブジェクト自体に代入はできない
person = {
  name: "Jiro",
  age: 26,
};

// プロパティは変更できてしまう
person.name = "Jiro";
person.age = 26;

一つ一つにreadonlyをつけてもいいですが、コードが冗長になってしまうのでas constを使いましょう。

オブジェクトにas constを使うと、そのオブジェクトのプロパティ全てが読み取り専用になります。

例を見てみましょう。


// { readonly name: "Taro"; readonly age: 30; } のオブジェクトリテラル型になる
const person = {
  name: "Taro",
  age: 30
} as const;

// エラー:読み取り専用プロパティであるため、'name'に代入することはできません。ts(2540)
person.name = "Jiro";
// エラー:読み取り専用プロパティであるため、'age'に代入することはできません。ts(2540)
person.age = 26;

このコードでは、personnameageは変更不可になります。

エラーが出るということは、TypeScriptがちゃんと私たちのオブジェクトを守ってくれている証拠です。
つまり、as constを使うと、オブジェクトの内容が意図せず変わることを防ぐことができるんですね。
これでコードがより安全になり、予期せぬバグを防ぐことができるようになります。

配列がreadonlyのタプル型になる

次は配列についてお話ししましょう。

TypeScriptで文字列の配列を宣言するとstringの配列型として型推論されます。


// string[]型として推論される
const fruits = ["Apple", "Banana", "Cherry"];

// 値の変更、追加ができる
fruits[0] = "Orange";
fruits.push("Orange");

配列もオブジェクトと同じように、constで宣言するだけでは値の変更、追加を防ぐことはできません。

as constを配列に使うと、その配列は読み取り専用のタプル型として扱われるようになります。
これは、配列の要素を後から変更したり追加したりすることができなくなるということです。
要するに、配列が固定されてしまうんですね。

こちらが具体例です。


// readonly[("Apple", "Banana", "Cherry")]のタプル型になる
const fruits = ["Apple", "Banana", "Cherry"] as const;

// エラー:読み取り専用プロパティであるため、'O'に代入することはできません。ts(2540)
fruits[0] = "Orange";
// エラー:プロパティ 'push'は'readonly ["Apple","Banana","Cherry"]'に存在しません。 ts(2339)
fruits.push("Orange");

このfruits配列は、'Apple', 'Banana', 'Cherry'の3つの要素を持つタプルとして扱われます。
そして、これらの要素は読み取り専用になるので、要素を変更しようとするとエラーが発生します。

as constを使って配列をタプルとして定義することで、プログラムの予測可能性が高まり、安全性が向上します。
また、特定の要素数や型を持つ配列を扱いたい時にも便利ですよ。

as constの注意点

as constは便利な機能ですが、使用する際には注意が必要です。
ここでは、特に覚えておくべき二つの重要なポイントについて説明します。

適用できるのはリテラル式のみ

as constはリテラル値やリテラルからなるオブジェクト、配列に対して適用可能です。
しかし、演算結果や関数の戻り値など、リテラル以外の式には使えません。
この点を誤解すると、コンパイラエラーに遭遇することがあります。

例えば、以下のように数値リテラルにはas constを適用できますが、演算の結果や関数の戻り値には適用できません。


// 25のリテラル型になる
let num = 25 as const;
// エラー:'const'アサーションは、列拳型メンバーへの参照、文字列、数値、ブール值、配列、オプジェクトリテラルにのみ適用できます。ts(1355)
let calculation = (12 + 13) as const;
let age = getNum() as const;

function getNum(): number {
  return 25;
}

完全に不変にすることはできない

as constを使っても、完全に不変にするわけではありません。

以下の例では、personオブジェクトのプロパティ自体は変更できませんが、配列の要素を変更することは可能です。


const skill = ["programming", "writing", "serving customers"];
const person = {
  name: "Taro",
  age: 30,
  skill,
} as const;

// 以下の操作はエラー
person.name = "Jiro";
person.age = 31;
person.skill = ["programming", "bookkeeping"];

// 配列の要素は変更できてしまう
person.skill.push("bookkeeping");

as constの使用例

as constは非常に便利な機能で、様々な場面で役立ちます。
ここでは、as constの具体的な使用例をいくつか見ていきましょう。
特に、以下の二つのシナリオに焦点を当てます。

  1. enumの代わりにas constを使う
  2. 通常、TypeScriptでは列挙型(enum)を使って特定の値のセットを定義します。
    しかし、as constを使うことで、よりシンプルに同様の効果を得ることができます。

  3. as constを使ってUnion型を作る
  4. as constは、Union型を定義する際にも非常に役立ちます。
    リテラルの集合をUnion型として定義することで、より厳密な型制約を設けることが可能になります。

これらの使用例を通じて、as constがいかに柔軟で強力なツールであるかを理解していただけると思います。
次のセクションで、これらの例について詳しく見ていきましょう。

enumの代わりにas constを使う

TypeScriptでenumを使うと、特定の選択肢のセットを簡単に扱うことができます。
例えば、方向を表すenumを使ってみましょう。


enum Direction {
  Up,
  Down,
  Left,
  Right,
}

このコードでは、Directionというenumを定義しています。
Direction.Upのように使うことができるほか、Direction[0]としてインデックスで逆引きすることも可能です。
しかし、この逆引きには落とし穴があります。
存在しないインデックスを指定すると、undefinedが返されてしまうのです。


// "Up"
console.log(Direction[0]);
// undefined
console.log(Direction);

このように、enumを使うときには存在しないインデックスを指定してしまうリスクがあります。

ここで、as constを使った代替案を見てみましょう。
次のようにオブジェクトを定義し、as constを使ってそのオブジェクトを読み取り専用にすることで、数値に基づいた定数セットを作ることができるんです。


const Directions = {
  Up: 0,
  Down: 1,
  Left: 2,
  Right: 3
} as const;

この方法では、Directions.Upのようにして値を参照することはできますが、Directions[0]のようにインデックスでアクセスすることはできません。
これにより、undefinedを返すリスクを完全に回避することができます。
また、Directionsオブジェクトのプロパティは読み取り専用になるため、値の不意な変更も防ぐことができるんです。

as constを使ってUnion型を作る

as constを使うと、Union型をより簡単に、そして安全に作ることができます。
Union型とは、変数が取り得る型が複数ある場合に使う型です。
例えば、ある変数が文字列の「Hello」または「Goodbye」のみを取り得るとき、その変数の型は'Hello' | 'Goodbye'と表現できます。

しかし、これらの値が多くなると、手動でUnion型を定義するのは大変ですよね。
そこでas constの出番です。
オブジェクトのプロパティや配列の要素にas constを適用することで、TypeScriptによる型推論を利用して、自動的に正確なUnion型を得ることができるんです。

以下の例を見てみましょう。


const responseStates = ["loading", "success", "error"] as const;
// type ResponseState = "loading" | "success" | "error"
type ResponseState = (typeof responseStates)[number];

ここでは、responseStatesという配列にas constを適用しています。
そして、ResponseState型は、responseStatesの各要素の型、つまり'loading' | 'success' | 'error'と自動的に推論されます。

この方法を使えば、Union型の管理がずっと楽になり、コードのメンテナンス性も向上します。
値を変更または追加する場合も、responseStates配列のみを更新すればいいので、タイプミスのリスクも減少しますね。

キャリア形成/給与還元
ひとつひとつ真摯に向き合う企業
ONE_WEDGE社員募集

株式会社 ONE WEDGEでは、新たな仲間を募集しています!

私たちと一緒に、革新的で充実したキャリアを築きませんか?
当社は、従業員が仕事と私生活のバランスを大切にできるよう、充実した福利厚生を整えています。

  • 完全週休2日制(土日休み)で、祝日や夏季休暇、年末年始休暇もしっかり保証!
  • 様々な休暇制度(有給、慶弔、産前・産後、育児、バースデー休暇)を完備!
  • 従業員の成長と健康を支援するための表彰制度、資格取得支援、健康促進手当など!
  • 生活を支えるテレワーク手当、記事寄稿手当、結婚祝金・出産祝金など、様々な手当を提供!
  • 自己啓発としての書籍購入制度や、メンバー間のコミュニケーションを深める交流費補助!
  • 成果に応じた決算賞与や、リファラル採用手当、AI手当など、頑張りをしっかり評価!
  • ワークライフバランスを重視し、副業もOK!

株式会社 ONE WEDGEでは、一人ひとりの従業員が自己実現できる環境を大切にしています。
共に成長し、刺激を与え合える仲間をお待ちしております。
あなたの能力と熱意を、ぜひ当社で発揮してください。
ご応募お待ちしております!

ホームページ、採用情報は下記ボタンからご確認ください!

応募、ご質問など、LINEでお気軽にご相談ください♪

まとめ

as constは、TypeScriptで型安全性をさらに強化するための強力なツールです。
この記事を通じて、as constの基本的な使い方から、いくつかの応用例に至るまで、その便利さと注意点を見てきました。

as constを使うことで、変数やオブジェクト、配列を不変にできるため、プログラムの予測可能性が高まり、誤った代入や意図しないデータの変更を防ぐことができます。
特に、リテラルの型推論を強化し、より具体的な型を得ることが可能になるため、TypeScriptの型システムを最大限に活用することができます。

しかし、as constを使う際には、リテラル式にのみ適用可能であること、また完全な不変性は保証されないことなど、いくつかの注意点もあります。
これらを理解し、適切にas constを利用することで、より安全で信頼性の高いコードを書くことができるでしょう。

これからも、as constをはじめとするTypeScriptの機能を活用して、バグの少ない効率的なプログラミングを目指しましょう。

COMMENT

メールアドレスが公開されることはありません。 が付いている欄は必須項目です