エラーの再現例
以下のコードは、UserEntryインターフェースを使用して、ユーザーエントリーの必須フィールドがすべて存在するかどうかをチェックしようとしています。しかし、TypeScriptはエラーを投げます。
interface UserEntry {
id: number;
name: string;
sex: "male" | "female";
}
const entry: UserEntry = {
id: 1,
name: "太郎",
sex: "male"
}
const errors: string[] = [];
const requiredFields = [
"id",
"name",
"sex"
];
requiredFields.forEach(field) => {
if (!entry[field]) { // エラー: 型 'UserEntry' には文字列インデックス シグネチャがありません
console.log(`Missing value for ${field}.`)
}
});
なぜエラーが発生するのか?
entry[field]のように動的にオブジェクトのプロパティにアクセスしようとする場合、TypeScriptはそのオブジェクトの型定義に文字列インデックスシグネチャがないとエラーを発生させます。UserEntryインターフェースは静的に定義されているため、動的な文字列キーでアクセスできないのです。
解決方法①:keyof UserEntry
このエラーを解決するために、requiredFields配列の型を正確に指定する必要があります。keyof型演算子を使用して、UserEntryのキーを型として指定することで、TypeScriptに対して安全なアクセスを保証します。
interface UserEntry {
id: number;
name: string;
sex: "male" | "female";
}
const entry: UserEntry = {
id: 1,
name: "太郎",
sex: "male"
}
const errors: string[] = [];
- const requiredFields = [
+ const requiredFields: (keyof UserEntry)[] = [
"id",
"name",
"sex"
];
requiredFields.forEach(field) => {
if (!entry[field]) {
console.log(`Missing value for ${field}.`)
}
});
keyof UserEntryは、"id" | "name" | "sex"のユニオン型を生成します。これにより、requiredFields配列の各要素がUserEntryのキーであることが保証され、entry[field]のような動的アクセスが安全になります。
解決方法②:as const
credit to: @sugoroku_y さん
as const は、TypeScript において、コンパイラに requiredFields 配列を変更不可の定数値として扱うように指示する型アサーションです。これにより、配列内の各要素はリテラル型として扱われ、より一般的な string 型ではなくなります。
interface UserEntry {
id: number;
name: string;
sex: "male" | "female";
}
const entry: UserEntry = {
id: 1,
name: "太郎",
sex: "male"
}
const errors: string[] = [];
const requiredFields = [
"id",
"name",
"sex"
- ];
+ ] as const;
requiredFields.forEach(field) => {
if (!entry[field]) {
console.log(`Missing value for ${field}.`)
}
});
1. as const なし:
const requiredFields = ["id", "name", "sex"]; // 推論される型は string[]
もし requiredFields が単なる string[] の場合、TypeScript はエラーを出します。なぜなら、field が entry に実際に存在するキーであることを保証できないからです。
2. as const あり:
const requiredFields = ["id", "name", "sex"] as const; // 推論される型は readonly ["id", "name", "sex"]
しかし、as const を使うと、TypeScript は field が必ず entry に存在するキーの1つ("id"、"name"、"sex" のいずれか)であることを認識します。このため、as const を使用すると型エラーを回避し、安全にインデックスアクセスができるようになります。
まとめ
TypeScriptでは、動的にオブジェクトのプロパティにアクセスする場合、型の安全性を確保するためにkeyofやインデックスシグネチャを適切に使用することが重要です。このアプローチにより、エラーを防ぎつつ、堅牢でメンテナンス性の高いコードを書くことができます。
as const を使うことで配列がリテラル型を持つタプルとして扱われ、entry に安全にインデックスアクセスできるため、型エラーを防ぐことができるということです。