こんにちは! 株式会社アルシエで教育に関するサポートをしている岸本です。
今回のテーマは「TypeScriptパート5」です。
インデックスシグネチャ
インデックスシグネチャとは、オブジェクトのプロパティを動的に追加する方法です。
type User = {
name: string;
[key: string]: string; // プロパティ名はkeyでなくても良いが、慣例的にkeyがよく使われる
};
const user: User = {
name: "taro",
account: "たろたろ", // 型宣言で[key: string]: stringと宣言されているため、プロパティaccountが明示的に宣言されてなくても使える
job: "Software Engineer", // 同上
};
便利だと思うかもしれませんが、欠点があります。
1つ目の欠点が、他の型が入った時にエラーになります。
インデックスシグネチャがstringとなっているため、他のプロパティにnumber型がある場合エラーが出ます。
export type User = {
name: string;
age: number; // インデックスシグネチャでstringを指定しているためエラー
[key: string]: string;
};
const user: User = {
name: "taro",
age: 20,
account: "たろたろ",
job: "Software Engineer",
};
このときの対処の仕方は、Union Typesにすれば問題ないです。
⚠️Userの型が増えていった時に、毎回Union Typesを記述しないといけない問題があります。
export type User = {
name: string;
age: number;
[key: string]: string | number;
};
const user: User = {
name: "taro",
age: 20,
account: "たろたろ",
job: "Software Engineer",
};
2つ目の欠点がundefinedを考慮してコードを記述するので使いづらいです。
fooは定義していないのでundefinedだと思いますが、fooはstring | number型になってしまいます。
そのためUnion Typesとして、undefinedを追加する事によってstring | number | undefined型になります。
export type User = {
name: string;
age: number;
// fooがundefinedでなければならないので、undefinedを追加
[key: string]: string | number | undefined ;
};
const user: User = {
name: "taro",
age: 20,
account: "たろたろ",
job: "Software Engineer",
};
// foo は string | number | undefined
user.foo
ですが、undefinedを追加する事によって問題があります。
accountが
string | number | undefined型になり、実際に使う時はtypeofを使って型を絞り込まないとメソッドにアクセスできないため使いづらいです。
export type User = {
name: string;
age: number;
[key: string]: string | number | undefined ;
};
const user: User = {
name: "taro",
age: 20,
account: "たろたろ",
job: "Software Engineer",
};
// account は string | number | undefined
user.account
Mapped Types
TypeScriptパート3でお伝えしたMapped Typesについてもう少し詳しく解説していきます。
firstNameとlastNameのプロパティを持ち、値がString型のオブジェクトをMapped Typesを利用して定義した例です。
type userObjType = {
[key in "firstName" | "lastName"]: string;
};
// 以下のように型推論される
// type userObjType = {
// firstName: string;
// lastName: string;
// };
他にも、別の型から参照する事もできます。
type Person = {
firstName: string;
lasttName: string;
};
type userObjType = {
[key in keyof Person]: number;
};
// 以下のように型推論される
// type userObjType = {
// firstName: number;
// lasttName: number;
// }
?をつける事でオプショナルにできます。
type Person = {
firstName: string;
lasttName: string;
};
type userObjType = {
[key in keyof Person]?: number;
};
// 以下のように型推論される
// type userObjType = {
// firstName?: number;
// lasttName?: number;
// }
Type Guard
Type Guardを何度か使いましたが、今回は詳しく解説していきたいと思います。
Type Guardとは型の絞り込みです。
typeof
typeof演算子は、ユニオン型に対して型のチェックを行うために使用することができます。
if文のreturnがあるので、最後のvalueがnumberと絞り込めています。
function numberToStirng(value: string | number) {
if (typeof value === "number") {
return value.toString(); // value は number型
}
return value; // value は string型
}
演算子で型チェック
function numberToStirng(value?: string) {
// undefinedの可能性を消している
if (!value) {
return ;
}
return value.toString();
}
inで型チェック
type CatType = {
name: string;
meow: string;
};
type DuckType = {
name: string;
quack: string;
};
function numberToStirng(value: CatType | DuckType) {
// valueの中にmeowがあるか検査
if ("meow" in value) {
return value; // value は CatType
}
return value; // value は DuckType
}
タグ付きユニオンで型チェック
CatTypeもDuckTypeも同じプロパティがあるときに使います。
type CatType = {
name: string;
meow: "scottishFold";
};
type DuckType = {
name: string;
meow: "Munchkin";
};
function numberToStirng(value: CatType | DuckType) {
// meowの中がscottishFoldだったら
if (value.meow === "scottishFold") {
return value; // value は CatType
}
return value; // value は DuckType
}
以上となります。
次回はTypeScriptパート6です!