TypeScriptは、静的型付けを提供することで、JavaScriptに型の安全性を追加する言語です。しかし、実行時にはJavaScriptの動的な性質が残っており、型を正確に判別することが必要になる場面が多々あります。この際に役立つのが「型ガード」という機能です。型ガードを使うことで、変数が特定の型であることを確認し、コードの安全性を高めることができます。TypeScriptでは、typeof
やinstanceof
といった演算子を使って型ガードを実装することができます。本記事では、これらの演算子を使用した基本的な型ガードの実装方法を学び、型の安全性をどのように確保できるのかを詳しく解説します。
TypeScriptにおける型ガードの重要性
TypeScriptは、コードを書く際にコンパイル時の型チェックを行うことでエラーを未然に防ぎます。しかし、実行時に動的に型を扱う場合、型チェックが不足することがあります。ここで型ガードを適切に利用することは非常に重要です。
型安全を確保する役割
型ガードを使用することで、TypeScriptが持つ型安全の強みを活かしながら、実行時に型の確認を行うことができます。これにより、以下のようなメリットがあります。
エラーの早期検出
コード内で誤った型を扱った場合、実行時にエラーを未然に防ぎやすくなります。たとえば、数値の処理で文字列が渡されるといったケースを防ぐことができます。
コードの可読性とメンテナンス性の向上
型ガードを適切に使用することで、コード内の型の判別が明示的になり、他の開発者にもわかりやすいコードを書くことができます。
型ガードを理解することで、より安全で保守性の高いコードを作成できるため、開発プロセス全体において非常に重要です。
typeof演算子とは
typeof
は、JavaScriptおよびTypeScriptで使用される演算子で、変数のデータ型を返すシンプルな方法です。typeof
演算子を使用すると、実行時にその変数がどのデータ型であるかを判別し、その型に応じた処理を実行することが可能です。特にプリミティブ型(数値、文字列、真偽値など)に対して有効で、型ガードとして活用できます。
typeofで判定できる型
typeof
で判定できる主な型は次の通りです。
number
: 数値string
: 文字列boolean
: 真偽値undefined
: 未定義object
: オブジェクトまたはnullfunction
: 関数
適用場面
typeof
は、特に動的に変数の型が決定される場面で役立ちます。例えば、APIレスポンスの処理や、ユーザー入力の値が複数の型で返される場合に、それに応じた処理を行うために使われます。
typeofによる型ガードの実装例
typeof
を使った型ガードの実装は非常に簡単で、特にプリミティブ型を扱う場合に便利です。以下に、typeof
を使った型ガードの基本的な実装例を紹介します。
基本的な実装例
function printValue(value: number | string) {
if (typeof value === 'number') {
console.log(`数値です: ${value}`);
} else if (typeof value === 'string') {
console.log(`文字列です: ${value}`);
}
}
printValue(42); // 出力: 数値です: 42
printValue("Hello"); // 出力: 文字列です: Hello
この例では、printValue
関数が引数として受け取る値が数値か文字列かをtypeof
で判定し、それに応じて異なる処理を行っています。typeof
演算子を使うことで、実行時に型を判別し、正しいロジックを適用することができます。
配列やオブジェクトには不向き
typeof
はプリミティブ型に対しては非常に有効ですが、配列やオブジェクトなどの複雑なデータ構造には不向きです。例えば、typeof []
やtypeof {}
はどちらも"object"
を返すため、配列やオブジェクトの区別はできません。そのため、これらを扱う際には別の方法が必要になります。
ユースケース
- APIからのレスポンスデータが数値か文字列かに応じて処理を変える場合
- ユーザー入力を動的にチェックして適切な処理を行う場合
このように、typeof
を使った型ガードは、簡単な型の判定に非常に便利です。
instanceof演算子とは
instanceof
は、JavaScriptやTypeScriptで使用される演算子で、特定のオブジェクトがどのクラスやコンストラクタから生成されたかを判定するために使われます。主にクラスベースのオブジェクト指向プログラミングにおいて、オブジェクトが特定のクラスのインスタンスであるかどうかを確認する際に利用されます。これにより、型ガードとしても強力なツールとなります。
instanceofの基本的な動作
instanceof
は、オブジェクトとそのコンストラクタ関数やクラスを比較し、そのオブジェクトがそのクラスまたはサブクラスのインスタンスである場合にtrue
を返します。例えば、以下のように使用します。
class Animal {}
class Dog extends Animal {}
const dog = new Dog();
console.log(dog instanceof Dog); // true
console.log(dog instanceof Animal); // true
この例では、dog
オブジェクトはDog
クラスのインスタンスであり、さらにDog
はAnimal
クラスを継承しているため、dog
はAnimal
のインスタンスとしても認識されます。
使用できる場面
instanceof
は、クラスベースのオブジェクトを扱うときに特に有効です。以下のような場面で活躍します。
- 複数のクラスが存在するコードベースで、特定のクラスのオブジェクトだけに対して処理を行いたい場合
- 継承を使用しているオブジェクト指向プログラミングにおいて、親クラスやサブクラスのインスタンスを区別する場合
このように、instanceof
はオブジェクトがどのクラスのインスタンスであるかを判別するための便利な演算子で、クラスベースの型ガードを実装する際に非常に有効です。
instanceofによる型ガードの実装例
instanceof
を使った型ガードは、特定のクラスやコンストラクタから生成されたオブジェクトであるかどうかを判定する際に有効です。これにより、オブジェクトのクラスに応じた処理を安全に行うことができます。以下に、instanceof
を使用した具体的な型ガードの実装例を紹介します。
基本的な実装例
class Animal {
speak() {
console.log("Animal is speaking.");
}
}
class Dog extends Animal {
bark() {
console.log("Woof! Woof!");
}
}
class Cat extends Animal {
meow() {
console.log("Meow!");
}
}
function makeSound(animal: Animal) {
if (animal instanceof Dog) {
animal.bark(); // Dogのインスタンスの場合、barkメソッドを呼び出す
} else if (animal instanceof Cat) {
animal.meow(); // Catのインスタンスの場合、meowメソッドを呼び出す
} else {
animal.speak(); // Animalの場合、speakメソッドを呼び出す
}
}
const dog = new Dog();
const cat = new Cat();
makeSound(dog); // 出力: Woof! Woof!
makeSound(cat); // 出力: Meow!
このコード例では、makeSound
関数がAnimal
型の引数を受け取り、そのオブジェクトがDog
またはCat
のインスタンスであるかどうかをinstanceof
で確認しています。Dog
であればbark
メソッド、Cat
であればmeow
メソッドを、それ以外はAnimal
のspeak
メソッドを実行するという処理を行っています。
継承による型ガード
instanceof
は、クラスの継承関係において特に強力です。たとえば、親クラスのインスタンスかどうかだけでなく、子クラスのインスタンスかどうかも判定することが可能です。これにより、オブジェクト指向プログラミングでの多態性(ポリモーフィズム)をサポートし、クラスごとに適切な処理を行うことができます。
ユースケース
- ユーザーインターフェースで異なるコンポーネントを扱う際、特定のクラスやサブクラスに応じて表示や動作を変える場合
- サーバーサイドで複数のエンティティが存在する場合、エンティティのクラスに基づいて処理を分岐させる場合
このように、instanceof
を利用することで、オブジェクトのクラスを動的に判別し、正確な型ガードを実装することができます。
型ガードの応用例
TypeScriptでは、typeof
やinstanceof
を組み合わせて、より高度で柔軟な型ガードを実装することが可能です。特に、複雑なオブジェクトやユニオン型を扱う場合、これらの型ガードを効果的に利用することで、型の判別を行い、安全で効率的なコードを記述できます。ここでは、typeof
とinstanceof
を組み合わせた型ガードの応用例を紹介します。
typeofとinstanceofの組み合わせによる型ガード
typeof
とinstanceof
を組み合わせることで、プリミティブ型とオブジェクト型を同時に扱う関数での型チェックが可能になります。次の例では、プリミティブ型(数値や文字列)とクラスオブジェクト(Date
)を同時に扱っています。
function processInput(input: number | string | Date) {
if (typeof input === 'number') {
console.log(`数値が入力されました: ${input}`);
} else if (typeof input === 'string') {
console.log(`文字列が入力されました: ${input}`);
} else if (input instanceof Date) {
console.log(`日付が入力されました: ${input.toDateString()}`);
}
}
processInput(42); // 出力: 数値が入力されました: 42
processInput("Hello World"); // 出力: 文字列が入力されました: Hello World
processInput(new Date()); // 出力: 日付が入力されました: Mon Sep 17 2024
この例では、typeof
でプリミティブ型(number
とstring
)を判別し、instanceof
でDate
オブジェクトを判定しています。これにより、異なる型に対して適切な処理を行うことが可能です。
カスタム型ガードの実装
TypeScriptでは、typeof
やinstanceof
を用いたシンプルな型ガードに加えて、カスタム型ガード関数を実装することもできます。これにより、特定のオブジェクトがカスタムタイプかどうかを確認することができます。以下は、カスタム型ガード関数の例です。
interface Fish {
swim: () => void;
}
interface Bird {
fly: () => void;
}
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim(); // petはFish型であると判定されたため、swimメソッドが呼び出せる
} else {
pet.fly(); // petはBird型であるため、flyメソッドが呼び出せる
}
}
const fish: Fish = { swim: () => console.log("泳ぐ") };
const bird: Bird = { fly: () => console.log("飛ぶ") };
move(fish); // 出力: 泳ぐ
move(bird); // 出力: 飛ぶ
この例では、isFish
関数がFish
型であるかどうかを判定し、その結果に基づいてswim
メソッドやfly
メソッドを実行しています。pet is Fish
という構文は、カスタム型ガードとしての型を示し、TypeScriptの型システムにおいて安全に型を特定できるようにしています。
ユニオン型とカスタム型ガードの組み合わせ
カスタム型ガードは、特にユニオン型で多くの型が混在する場合に役立ちます。ユーザーの入力や外部APIからのデータが多様な型を含む場合、それに対して安全に処理を行うために、型ガードをカスタマイズして利用することが推奨されます。
このように、typeof
やinstanceof
、そしてカスタム型ガードを応用することで、より複雑で柔軟な型チェックを行い、TypeScriptで安全なプログラミングを実現できます。
ユニオン型と型ガードの関係
TypeScriptでは、ユニオン型を使うことで、変数や引数が複数の型を持つことが可能になります。しかし、ユニオン型は型が曖昧であるため、使用する際には型ガードを活用して正しい型を特定し、適切な処理を行う必要があります。ここでは、ユニオン型と型ガードの関係について詳しく説明します。
ユニオン型とは
ユニオン型は、複数の異なる型を持つことができるデータ型です。ユニオン型を使用することで、例えばある関数の引数が数値か文字列のどちらかである場合に、その引数を柔軟に処理することができます。
let value: number | string;
value = 42; // 数値を代入
value = "Hello"; // 文字列を代入
この例では、value
はnumber
かstring
のいずれかの型を持つことができるユニオン型として定義されています。
ユニオン型の型ガードが必要な理由
ユニオン型は柔軟性が高い反面、特定の型に応じた処理を行う際に型ガードが必要です。なぜなら、ユニオン型ではすべての型の共通メソッドしか利用できないためです。たとえば、number | string
型では、数値専用の演算や文字列専用のメソッドを使用する際に型を特定する必要があります。
型ガードによるユニオン型の処理
ユニオン型を扱う際、typeof
やinstanceof
を使って、特定の型に対して処理を分岐させることが重要です。以下に、ユニオン型に対して型ガードを使用する例を示します。
function processValue(value: number | string) {
if (typeof value === 'number') {
console.log(`数値の2倍: ${value * 2}`);
} else if (typeof value === 'string') {
console.log(`文字列の長さ: ${value.length}`);
}
}
processValue(10); // 出力: 数値の2倍: 20
processValue("Hello"); // 出力: 文字列の長さ: 5
この例では、value
が数値の場合にはその数値を2倍にし、文字列の場合にはその文字数を出力する処理を行っています。typeof
を使用することで、ユニオン型の中から適切な型を判別し、それに応じた処理を行うことが可能です。
カスタム型ガードを使ったユニオン型の判別
カスタム型ガードを使用すると、もっと複雑なユニオン型でも安全に処理を行うことができます。特に、オブジェクトやインターフェースが混在するユニオン型に対して有効です。
interface Car {
drive: () => void;
}
interface Boat {
sail: () => void;
}
function isCar(vehicle: Car | Boat): vehicle is Car {
return (vehicle as Car).drive !== undefined;
}
function moveVehicle(vehicle: Car | Boat) {
if (isCar(vehicle)) {
vehicle.drive();
} else {
vehicle.sail();
}
}
const car: Car = { drive: () => console.log("車を運転しています") };
const boat: Boat = { sail: () => console.log("ボートを航行しています") };
moveVehicle(car); // 出力: 車を運転しています
moveVehicle(boat); // 出力: ボートを航行しています
この例では、Car
かBoat
かをカスタム型ガード関数isCar
を使って判別し、それに応じたメソッドを呼び出しています。
ユニオン型の活用と型ガードの重要性
ユニオン型を利用することで、柔軟で再利用可能なコードを書くことができますが、型ガードを使用しなければ正しい型に対して適切な処理を行うことができません。型ガードを活用することで、ユニオン型の利便性を活かしつつ、安全かつ堅牢なコードを作成することができます。
ユニオン型と型ガードを組み合わせることで、TypeScriptの型安全性を保ちながら柔軟なプログラムが実現できるのです。
演習問題
ここでは、typeof
やinstanceof
を使った型ガードの実装に挑戦する演習問題を通じて、実践的なスキルを身につけましょう。この演習問題では、ユニオン型やオブジェクト型を活用し、適切な型ガードを実装することが求められます。
演習問題 1: typeofを使った型ガード
次の関数calculate
では、引数として数値か文字列が与えられます。文字列の場合は、その文字列の長さを返し、数値の場合はその数値を2倍にして返すように関数を完成させてください。
function calculate(value: number | string): number {
// ここに型ガードを実装してください
}
期待される出力:
console.log(calculate(10)); // 出力: 20
console.log(calculate("Hello")); // 出力: 5
ヒント:
typeof
を使って、引数が数値か文字列かを判別し、それに応じた処理を行ってください。
演習問題 2: instanceofを使った型ガード
次に、Vehicle
クラスとそのサブクラスCar
とBicycle
を定義し、instanceof
を使って型を判別する関数describeVehicle
を作成してください。それぞれのサブクラスに対応するメッセージを出力します。
class Vehicle {}
class Car extends Vehicle {
drive() {
console.log("車を運転しています");
}
}
class Bicycle extends Vehicle {
ride() {
console.log("自転車に乗っています");
}
}
function describeVehicle(vehicle: Vehicle) {
// ここに型ガードを実装してください
}
期待される出力:
const car = new Car();
const bike = new Bicycle();
describeVehicle(car); // 出力: 車を運転しています
describeVehicle(bike); // 出力: 自転車に乗っています
ヒント:
instanceof
を使って、vehicle
がCar
のインスタンスか、Bicycle
のインスタンスかを判定し、適切なメソッドを呼び出してください。
演習問題 3: カスタム型ガードの実装
次に、Bird
とFish
の2つのインターフェースを持つユニオン型を作成し、カスタム型ガードを使ってBird
かFish
を判別する関数moveAnimal
を実装してください。
interface Bird {
fly: () => void;
}
interface Fish {
swim: () => void;
}
function moveAnimal(animal: Bird | Fish) {
// カスタム型ガードを使って実装してください
}
期待される出力:
const bird: Bird = { fly: () => console.log("鳥が飛んでいます") };
const fish: Fish = { swim: () => console.log("魚が泳いでいます") };
moveAnimal(bird); // 出力: 鳥が飛んでいます
moveAnimal(fish); // 出力: 魚が泳いでいます
ヒント:
カスタム型ガード関数を実装し、pet is Bird
の形式で特定の型を判定します。
これらの演習問題を通じて、typeof
やinstanceof
を用いた型ガードの実装方法を理解し、実際のプロジェクトで役立つスキルを磨くことができます。自分でコードを書いてみて、型ガードの理解を深めましょう。
型ガードのトラブルシューティング
型ガードを実装する際、いくつかの問題やエラーに遭遇することがあります。これらの問題に対処するためには、型システムやTypeScriptの挙動についての深い理解が必要です。ここでは、型ガードの実装時によくあるトラブルとその解決方法について解説します。
1. ユニオン型での型が特定できない問題
TypeScriptのユニオン型は、複数の型が混在するため、型ガードが正しく機能しない場合があります。例えば、ユニオン型の一部の型が型ガードで特定できない場合、エラーが発生します。以下の例を見てみましょう。
function printLength(value: string | number) {
if (typeof value === 'string') {
console.log(value.length);
} else {
// TypeScriptはnumber型にはlengthプロパティがないため、エラーを投げる
console.log(value.length);
}
}
問題点:number
型にはlength
プロパティが存在しないため、エラーが発生します。この場合、型ガードを適切に使ってnumber
型には対応しない処理を行う必要があります。
解決方法:
型ガードを使い、型ごとに異なる処理を行うように変更します。
function printLength(value: string | number) {
if (typeof value === 'string') {
console.log(value.length);
} else {
console.log(`値は数値です: ${value}`);
}
}
これにより、各型に対して適切な処理を行い、エラーを防ぐことができます。
2. instanceofの誤用
instanceof
はクラスやコンストラクタに対して正しく動作しますが、プリミティブ型やインターフェースには使えないため、誤って使うと意図しない挙動が発生します。
function isArray(value: any) {
if (value instanceof Array) {
console.log("これは配列です");
}
}
isArray([1, 2, 3]); // 正常に動作
isArray("Hello"); // instanceofは使えないためエラーになる可能性がある
問題点:instanceof
はクラスに対して機能するため、プリミティブ型やインターフェースには使用できません。誤って使うと、予期しないエラーや動作が発生します。
解決方法:
配列の判定にはArray.isArray()
を使用します。これにより、型が配列かどうかを安全に判定できます。
function isArray(value: any) {
if (Array.isArray(value)) {
console.log("これは配列です");
}
}
これで、配列かどうかを適切に判定でき、他の型にも誤ってinstanceof
を使用するリスクを回避できます。
3. オブジェクトのプロパティによる型ガードの誤用
オブジェクト型を判定する際、存在しないプロパティにアクセスしようとすると、エラーが発生します。また、TypeScriptがそのプロパティが存在しないと推論した場合もエラーが発生します。
interface Car {
drive: () => void;
}
interface Bicycle {
ride: () => void;
}
function move(vehicle: Car | Bicycle) {
if (vehicle.drive) {
vehicle.drive(); // Bicycle型にはdriveがないためエラー
}
}
問題点:
このコードではBicycle
型にもdrive
があると誤認してしまい、コンパイル時にエラーが発生します。直接的なプロパティチェックは型安全性を損なう可能性があります。
解決方法:in
演算子を使うことで、プロパティが存在するかどうかを型安全に確認できます。
function move(vehicle: Car | Bicycle) {
if ('drive' in vehicle) {
vehicle.drive();
} else {
vehicle.ride();
}
}
これにより、Car
かBicycle
かを適切に判別し、エラーを防ぐことができます。
4. カスタム型ガードの誤実装
カスタム型ガードを実装する際、関数の戻り値としてpet is Type
という形式を正しく指定しないと、TypeScriptはその型ガードを認識しません。
interface Dog {
bark: () => void;
}
interface Cat {
meow: () => void;
}
function isDog(animal: Dog | Cat): boolean {
return (animal as Dog).bark !== undefined;
}
function interactWithPet(pet: Dog | Cat) {
if (isDog(pet)) {
pet.bark(); // TypeScriptはisDogを型ガードと認識しないためエラー
}
}
問題点:
カスタム型ガード関数isDog
が、単なるboolean
値を返しており、型ガードとして認識されません。
解決方法:
カスタム型ガードの戻り値をpet is Dog
という形式に変更する必要があります。
function isDog(animal: Dog | Cat): animal is Dog {
return (animal as Dog).bark !== undefined;
}
function interactWithPet(pet: Dog | Cat) {
if (isDog(pet)) {
pet.bark(); // 型ガードが正しく動作し、エラーが解消
}
}
このように正しくカスタム型ガードを定義することで、TypeScriptに型が正しく伝わり、安全な型判定が可能となります。
型ガードのトラブルシューティングを理解し、これらの問題に対処することで、より堅牢で型安全なコードを実現できるようになります。
外部ライブラリを使った型ガードの拡張
TypeScriptの標準機能であるtypeof
やinstanceof
は非常に便利ですが、複雑なオブジェクトやユニオン型の判定、より高度な型検証が必要な場合、外部ライブラリを活用することで型ガードをさらに強化できます。ここでは、外部ライブラリを使った型ガードの拡張方法をいくつか紹介します。
1. io-tsライブラリによる型検証
io-ts
は、TypeScriptの型に基づいた実行時の型検証を行うためのライブラリです。TypeScriptの型はコンパイル時にしか利用できませんが、io-ts
を使うことで、実行時に型の検証が可能となり、より安全な型チェックを実現できます。
npm install io-ts
import * as t from 'io-ts';
const User = t.type({
id: t.number,
name: t.string,
});
type UserType = t.TypeOf<typeof User>;
const validateUser = (input: unknown): input is UserType => {
return User.is(input);
}
const user = { id: 1, name: "Alice" };
if (validateUser(user)) {
console.log(`User ID: ${user.id}, Name: ${user.name}`);
} else {
console.log("不正なユーザーオブジェクトです");
}
ポイント:
io-ts
の型定義を使って、実行時にオブジェクトが正しいかどうかを検証します。- 型ガードとして
validateUser
関数を使い、正しい型かどうかを確認しています。
2. zodライブラリによる型のスキーマバリデーション
zod
は、型定義とバリデーションを簡単に行えるライブラリです。型ガードとして利用することで、オブジェクトのプロパティが正しい型かどうかを確認し、エラーハンドリングを強化できます。
npm install zod
import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string(),
});
type User = z.infer<typeof UserSchema>;
const validateUser = (input: unknown): input is User => {
try {
UserSchema.parse(input);
return true;
} catch {
return false;
}
}
const user = { id: 1, name: "Alice" };
if (validateUser(user)) {
console.log(`User is valid: ${user.name}`);
} else {
console.log("Invalid user data");
}
ポイント:
zod
を使うと、スキーマバリデーションを簡単に導入できます。- 型ガードとして
validateUser
を使用し、オブジェクトの型が正しいかどうかを判定しています。
3. Yupライブラリによるフォームバリデーション
Yup
は、JavaScriptオブジェクトのスキーマバリデーションを行うためのライブラリで、特にフォームデータのバリデーションに強力です。TypeScriptと組み合わせて、フォーム入力の型ガードを行うことができます。
npm install yup
import * as Yup from 'yup';
const userSchema = Yup.object({
id: Yup.number().required(),
name: Yup.string().required(),
});
const validateUser = async (input: unknown): Promise<boolean> => {
try {
await userSchema.validate(input);
return true;
} catch {
return false;
}
}
const user = { id: 1, name: "Alice" };
validateUser(user).then((isValid) => {
if (isValid) {
console.log("User is valid");
} else {
console.log("Invalid user data");
}
});
ポイント:
- 非同期バリデーションが可能で、フォームデータの型ガードやバリデーションに強力です。
- バリデーションエラーをキャッチし、オブジェクトの型が適切かを判定します。
ユースケース
これらの外部ライブラリは、以下のような状況で非常に有効です。
- 複雑なオブジェクト検証: APIレスポンスやデータベースのレコードのような複雑なオブジェクトを扱う場合、ライブラリによる型ガードで安全性を確保します。
- フォームバリデーション: ユーザー入力を確実に検証し、不正なデータがシステムに渡らないようにするため、型ガードが活躍します。
- APIレスポンスの型検証: クライアントサイドでAPIのレスポンスデータを検証し、不正なデータに対処するための型ガードが必要です。
これらの外部ライブラリを活用することで、TypeScriptの標準的な型ガードを超えた高度な型安全性を実現できます。複雑なオブジェクトやフォーム入力の処理、APIレスポンスの検証など、さまざまなユースケースに対応できるように拡張が可能です。
まとめ
本記事では、TypeScriptにおけるtypeof
とinstanceof
を使った基本的な型ガードの実装方法について学びました。これらの演算子を使うことで、プリミティブ型やクラスの型を動的に判別し、安全なコードを記述することが可能になります。また、カスタム型ガードの実装や、外部ライブラリを活用した型ガードの拡張も紹介しました。型ガードを適切に活用することで、TypeScriptの強力な型安全性を最大限に引き出し、エラーを未然に防ぎ、より堅牢なアプリケーション開発を実現することができます。
コメント