SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

JavaScriptの限界を超える!TypeScript初めの一歩

【TypeScriptの基礎を学ぶ】コードの表現力を高めよう!型ガードとジェネリクスによるデータ操作術

JavaScriptの限界を超える!TypeScript初めの一歩 第3回

constアサーション

 型アサーションに付随して、constアサーションも紹介しておきます。as constと書き、書き換え不可にします。

 まずはプリミティブ型をあつかう変数にas constを付けた例です。

let str1 = "hello" as const;
str1 = "world"; // エラーになる

 2行目のstr1のところでエラーが出ます。エラーの内容はType '"world"' is not assignable to type '"hello"'.(タイプ '"world"' はタイプ '"hello"' に割り当てることができません。)です。

 上のコードは、次のコードと同じです。

let str2: "hello" = "hello";
str2 = "world"; // エラーになる

 str2: "hello"のように、固定値からしか選べないリテラル型を書いたのと同じ状態になります。

 次に配列を見てみましょう。

let arr1 = [10, 20, 30] as const;
arr1[0] = 100;  // エラーになる

 arr1[0]0のところでエラーが出ます。エラーの内容はCannot assign to '0' because it is a read-only property.(読み取り専用プロパティであるため、「0」に割り当てることはできません。)です。

 上のコードは、次のコードと同じです。

let arr2: readonly number[] = [10, 20, 30];
arr2[0] = 100;  // エラーになる

 次にオブジェクトを見てみましょう。

let obj1 = {
    name: "Tom",
    age: 10,
} as const;
obj1.name = "Bob";  // エラーになる

 obj1.namenameのところでエラーが出ます。エラーの内容はCannot assign to 'name' because it is a read-only property.('name' は読み取り専用プロパティであるため割り当てることができません。)です。

 上のコードは、次の2つのコードと同じです。

let obj2: {
    readonly name: string;
    readonly age: number;
} = {
    name: "Tom",
    age: 10,
};
obj2.name = "Bob";  // エラーになる
let obj3: Readonly<{
    name: string;
    age: number;
}> = {
    name: "Tom",
    age: 10,
};
obj3.name = "Bob";  // エラーになる

 さて、as constを付けた書き方には、大きなメリットがあります。readonlyは浅い階層しか読み取り専用にしません。しかし、as constは、深いところまで読み取り専用にしてくれます。

 違いが分かるコードを示します。1つめはas constを付けた例です。

let objNest1 = {
    name: "Tom",
    status: {
        hp: 10,
        mp: 5,
    },
} as const;
objNest1.status.hp = 1; // エラーになる

 最後の行のstatus.hphpのところでエラーが出ます。

 次に、readonlyを付けた例です。浅いところまでしか読み取り専用が適用されません。

let objNest2: {
    readonly name: string;
    readonly status: Record<string, number>;
} = {
    name: "Tom",
    status: {
        hp: 10,
        mp: 5,
    },
} as const;
objNest2.status.hp = 1; // エラーにならない

 こちらのコードではエラーは起きません。

列挙型(enum)

 TypeScriptには、JavaScriptにはない列挙型(enum)が存在します。JavaScriptの値としても使え、TypeScriptの型にも利用できる強力なものです。

 列挙型の例を示します。

enum Animal {
    Cat,    // 0
    Dog,    // 1
    Pig,    // 2
    Cow,    // 3
};

console.log(Object.entries(Animal));

let animal: Animal = Animal.Cat;
animal = 1;
animal = 2;
animal = 3;
animal = 4; // エラーになる

 列挙型は、何も値を指定しなければ`0`から始まり、次の項目になるごとに1ずつ値が増えていきます。

 上のコードは、Object.entries(Animal)Animal.Catのように、JavaScriptの値としても利用しています。また、animal: Animalのように型としても利用しています。

 また、上のコードは最後の行animal = 4;animalでエラーが起きます。エラーの内容はType '4' is not assignable to type 'Animal'.(型「4」は型「Animal」に割り当てることができません。)です。

 : Animalと型注釈を付けた変数animalに、enum Animalで作成した値以外を代入しようとするとエラーになります。

 ちなみにこのコードを、コマンドnpx tsc main.ts --target es2024でJavaScriptに変換すると次のようになります。

var Animal;
(function (Animal) {
    Animal[Animal["Cat"] = 0] = "Cat";
    Animal[Animal["Dog"] = 1] = "Dog";
    Animal[Animal["Pig"] = 2] = "Pig";
    Animal[Animal["Cow"] = 3] = "Cow";
})(Animal || (Animal = {}));
;
console.log(Object.entries(Animal));
let animal = Animal.Cat;
animal = 1;
animal = 2;
animal = 3;
animal = 4;

 このJavaScriptのファイルを、node main.jsで実行すると次のように出力されます。名前と値のどちらからでも引けるオブジェクトが生成されています。

実行結果
[
  [ '0', 'Cat' ],
  [ '1', 'Dog' ],
  [ '2', 'Pig' ],
  [ '3', 'Cow' ],
  [ 'Cat', 0 ],
  [ 'Dog', 1 ],
  [ 'Pig', 2 ],
  [ 'Cow', 3 ]
]

列挙型(enum)その2

 列挙型は、値を代入することで細かく制御することができます。

 途中で数値を代入するとその数値になり、次の項目ではそこから1ずつ大きくなります。例を示します。

enum Animal {
    Cat = 2,    // 2
    Dog,        // 3
    Pig = 8,    // 8
    Cow,        // 9
};

 文字列を代入することもできます。例を示します。

enum AnimalCode {
    A100 = "Cat",
    A101 = "Dog",
    A102 = "Pig",
    A103 = "Cow",
};

 この場合はコンパイルすると、次のような形になります。

var AnimalCode;
(function (AnimalCode) {
    AnimalCode["A100"] = "Cat";
    AnimalCode["A101"] = "Dog";
    AnimalCode["A102"] = "Pig";
    AnimalCode["A103"] = "Cow";
})(AnimalCode || (AnimalCode = {}));

 JavaScriptとして実行してconsole.log()AnimalCodeを出力すると次のようになります。

実行結果
{ A100: 'Cat', A101: 'Dog', A102: 'Pig', A103: 'Cow' }

 あまりやらないとは思いますが、数値と文字列を混ぜた場合には問題が起きます。文字列を代入したあとに何も代入しないとエラーが起きます。例を示します。

enum AnimalCodeInvalid {
    A100 = 100,     // 100
    A101,           // 101
    A102 = "Pig",   // "Pig"
    A103,           // Error: Enum member must have initializer.
    A104 = 104,     // 104
    A105,           // 105
};

 A103のところでエラーが起きます。数値を代入すると、次の項目から1ずつ値を増やしていきます。

第3章 まとめと次回予告

 今回は、TypeScriptで多様なデータに対応する方法を解説しました。次回からは既存のJavaScriptのコードを、TypeScriptに移行していきます。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
JavaScriptの限界を超える!TypeScript初めの一歩連載記事一覧

もっと読む

この記事の著者

柳井 政和(ヤナイ マサカズ)

クロノス・クラウン合同会社 代表社員http://crocro.com/オンラインソフトを多数公開。プログラムを書いたり、ゲームを作ったり、記事を執筆したり、マンガを描いたり、小説を書いたりしています。「めもりーくりーなー」でオンラインソフト大賞に入賞。最近は、小説家デビューして小説も書いています(『裏切りのプログラム』他)。面白いことなら何でもOKのさすらいの企画屋です。 

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/21597 2025/09/05 11:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー