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.nameのnameのところでエラーが出ます。エラーの内容は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.hpのhpのところでエラーが出ます。
次に、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に移行していきます。
- 第1回:【TypeScriptの基礎を学ぶ】JavaScriptと比較して起こりがちなミスを防ごう!
- 第2回:【TypeScriptの基礎を学ぶ】TypeScriptの型の付け方・あつかい方を解説
- 第3回:【TypeScriptの基礎を学ぶ】コードの表現力を高めよう!型ガードとジェネリクスによるデータ操作術
- 第4回:既存JavaScriptをTypeScriptに移行する方法(次回)
- 第5回:外部のパッケージやその他の情報
