【保存版】TypeScriptオプション型:疑問を徹底解消! 〜サンプルコードで理解を深める〜
TypeScriptにおける「-?」の意味
例:
interface User {
name: string;
age?: number; // オプション型のプロパティ
}
const user: User = {
name: "田中 太郎"
};
console.log(user.name); // "田中 太郎" と出力
console.log(user.age); // undefined と出力
この例では、User
インターフェースの age
プロパティはオプション型になっています。つまり、user
オブジェクトには age
プロパティが存在する可能性もありますが、存在しない場合や null
の値である場合もあります。
オプション型の利点:
- 型推論機能を活用し、より強力な型チェックを実現することができます。
- ランタイムエラーを防ぐことができます。
- コードの可読性と保守性を向上させることができます。
- オプション型のプロパティの型を厳密に定義する必要があります。
- オプション型のプロパティにアクセスする前に、必ず
null
チェックを行う必要があります。
- オプション型は、複雑な型システムを構築する際に役立ちます。
- TypeScriptには、
?
記号以外にもオプション型を表す方法があります。詳細は、TypeScript の公式ドキュメントを参照してください。
基本的なオプション型
interface User {
name: string;
age?: number;
city?: string;
}
const user: User = {
name: "田中 太郎",
age: 30,
city: "東京"
};
console.log(user.name); // "田中 太郎" と出力
console.log(user.age); // 30 と出力
console.log(user.city); // "東京" と出力
const newUser: User = {
name: "佐藤 花子"
};
console.log(newUser.name); // "佐藤 花子" と出力
console.log(newUser.age); // undefined と出力
console.log(newUser.city); // undefined と出力
この例では、User
インターフェースの age
と city
プロパティはオプション型になっています。そのため、user
オブジェクトには age
と city
プロパティが存在しますが、newUser
オブジェクトには存在しません。
デフォルト値
interface User {
name: string;
age?: number = 20; // デフォルト値を設定
city?: string = "東京";
}
const user: User = {
name: "田中 太郎"
};
console.log(user.name); // "田中 太郎" と出力
console.log(user.age); // 20 と出力
console.log(user.city); // "東京" と出力
この例では、User
インターフェースの age
と city
プロパティにデフォルト値を設定しています。そのため、user
オブジェクトには age
と city
プロパティが明示的に定義されていませんが、デフォルト値が設定されているため、以下の出力が得られます。
関数引数
function greetUser(user: { name: string; age?: number }) {
console.log(`こんにちは、${user.name} さん!${user.age ? ` あなたの年齢は ${user.age} 歳ですね。` : ""}`);
}
greetUser({ name: "田中 太郎" }); // "こんにちは、田中 太郎 さん!" と出力
greetUser({ name: "佐藤 花子", age: 30 }); // "こんにちは、佐藤 花子 さん! あなたの年齢は 30 歳ですね。" と出力
この例では、greetUser
関数の引数 user
にオプション型のプロパティ age
を定義しています。そのため、greetUser
関数に age
プロパティを省略して渡すこともできます。
ジェネリック型
interface Box<T> {
value: T | null;
}
const numberBox: Box<number> = { value: 10 };
const stringBox: Box<string> = { value: "Hello" };
console.log(numberBox.value); // 10 と出力
console.log(stringBox.value); // "Hello" と出力
この例では、ジェネリック型 Box
を定義し、オプション型のプロパティ value
を持たせています。numberBox
と stringBox
はそれぞれ Box
型の具体的な型パラメータとして number
と string
を指定しており、それぞれ value
プロパティに number
型と string
型の値を格納しています。
型推論
function getFirstValue<T>(values: T[]): T | undefined {
if (values.length > 0) {
return values[0];
} else {
return undefined;
}
}
const numbers = [1, 2, 3];
const firstNumber = getFirstValue(numbers);
console.log(firstNumber); // 1 と出力
const strings = ["Hello", "World"];
const firstString = getFirstValue(strings);
console.log(firstString); // "Hello" と出力
TypeScriptにおけるオプション型の代替方法
| undefined 型
最も単純な代替方法は、| undefined
型を使用することです。これは、そのプロパティが型 T
の値であるか、undefined
である可能性があることを意味します。
interface User {
name: string;
age: number | undefined; // `| undefined` 型を使用
city?: string;
}
const user: User = {
name: "田中 太郎",
age: 30
};
console.log(user.name); // "田中 太郎" と出力
console.log(user.age); // 30 と出力
nullish 型
TypeScript 3.7以降では、nullish
型を使用することができます。これは、そのプロパティが null
または undefined
である可能性があることを意味します。
interface User {
name: string;
age: number | nullish; // `nullish` 型を使用
city?: string;
}
const user: User = {
name: "田中 太郎",
age: 30
};
console.log(user.name); // "田中 太郎" と出力
console.log(user.age); // 30 と出力
より高度な代替方法として、関数型を使用することができます。これは、そのプロパティが存在するかどうかを判定し、存在する場合のみ値を取得する関数を定義することを意味します。
interface User {
name: string;
getAge(): number | undefined; // 関数型を使用
city?: string;
}
const user: User = {
name: "田中 太郎",
getAge() {
return 30;
}
};
console.log(user.name); // "田中 太郎" と出力
console.log(user.getAge()); // 30 と出力
型ガード
型ガードを使用することで、オプション型のプロパティの型をより詳細に制御することができます。
interface User {
name: string;
age?: number;
city?: string;
}
function isUserWithAge(user: User): user is { age: number } {
return typeof user.age === "number";
}
const user: User = {
name: "田中 太郎",
age: 30
};
if (isUserWithAge(user)) {
console.log(user.age); // 30 と出力
} else {
console.log("年齢は不明です。");
}
それぞれの方法の選び方
上記で紹介した方法はそれぞれ長所と短所があります。
- 制御性
型ガードは最も制御性がありますが、複雑になります。 - 柔軟性
関数型は最も柔軟性がありますが、コード量が増加します。 - 型安全性
nullish
型は| undefined
型よりも型安全性が高くなりますが、TypeScript 3.7以降でのみ使用できます。 - 簡潔性
| undefined
型は最も簡潔ですが、型安全性は低くなります。
typescript