TypeScript: タイプからタイプを作成する

こんにちは、未来のTypeScriptの達人さんたち!このTypeScriptの类型の世界を楽しみにする旅のご案内を務めさせていただきます。コンピュータサイエンスの教師として数年間の経験を持つ者として、类型を理解することで初心者がコードの魔法使いに変貌するのを見てきました。それでは、一緒にこの冒険の旅に出発しましょう!

TypeScript - Creating Types from Types

ユニオン类型

アイスクリーム屋さんで、チョコレートかバニラを選べることを思い浮かべてください。TypeScriptでは、このような選択を「ユニオン类型」と呼びます。まるで「これかあれか」と言っているようなものです。

コードを見てみましょう:

type Flavor = "chocolate" | "vanilla";

let myIceCream: Flavor;
myIceCream = "chocolate"; // これは大丈夫
myIceCream = "strawberry"; // エラー!Type '"strawberry"' は type 'Flavor' に割り当てられません。

この例では、Flavor は「chocolate」または「vanilla」のユニオン类型です。myIceCream に「strawberry」を割り当てようとすると、TypeScriptは「ちょっと待って!それはメニューに載っていない!」と言います。

ユニオン类型は、変数が持つことのできる値を制限したいときに非常に便利です。別の例を見てみましょう:

type Answer = "yes" | "no" | "maybe";

function respondToQuestion(answer: Answer) {
if (answer === "yes") {
console.log("Great!");
} else if (answer === "no") {
console.log("Oh no!");
} else {
console.log("I see you're undecided.");
}
}

respondToQuestion("yes"); // 问题ない
respondToQuestion("perhaps"); // エラー!引数の型 '"perhaps"' はパラメータの型 'Answer' に割り当てられません。

インターセクション类型

次に、インターセクション类型について話しましょう。ユニオン类型は「これかあれか」についてなら、インターセクション类型は「これとあれ」についてです。ファストフードレストランでセットメニューを注文するようなものです - バーガーとフライと飲み物が揃います。

コードはこんな感じです:

type Name = {
firstName: string;
lastName: string;
};

type Age = {
age: number;
};

type Person = Name & Age;

let john: Person = {
firstName: "John",
lastName: "Doe",
age: 30
};

この例では、PersonNameAge のインターセクション类型です。まるで「人間は名前と姓と年齢を持っている」と言っているようなものです。

別の楽しい例を見てみましょう:

type Swimmer = {
swim: () => void;
};

type Flyer = {
fly: () => void;
};

type FlyingFish = Swimmer & Flyer;

let nemo: FlyingFish = {
swim: () => console.log("Swimming in the ocean"),
fly: () => console.log("Flying over the waves")
};

nemo.swim(); // 出力: Swimming in the ocean
nemo.fly();  // 出力: Flying over the waves

ユーティリティ类型

TypeScriptには、既存の类型を操作し変換するのに便利なユーティリティ类型が付属しています。それらはまるでスイスアーミーナイフの类型のようなものです!最もよく使われるものを見てみましょう:

Partial

Partial<T> は类型Tのすべてのプロパティをオプションにします。まるで「これら全部が必要だけど、今は全部不要だ」と言っているようなものです。

interface Todo {
title: string;
description: string;
completed: boolean;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return { ...todo, ...fieldsToUpdate };
}

const myTodo = {
title: "Learn TypeScript",
description: "Study for 2 hours",
completed: false
};

const updatedTodo = updateTodo(myTodo, {
description: "Study for 3 hours"
});

console.log(updatedTodo);
// 出力: { title: "Learn TypeScript", description: "Study for 3 hours", completed: false }

Pick<T, K>

Pick<T, K> は类型TからプロパティのセットKを選択して新しい类型を作成します。まるで果物バスケットからお気に入りの果物を選ぶようなものです。

interface User {
id: number;
name: string;
email: string;
age: number;
}

type UserSummary = Pick<User, "id" | "name">;

const userSummary: UserSummary = {
id: 1,
name: "John Doe"
};

Omit<T, K>

Omit<T, K> は类型Tからすべてのプロパティを選択し、その後Kを削除して新しい类型を作成します。Pickの逆です。

interface Product {
id: number;
name: string;
price: number;
description: string;
}

type ProductPreview = Omit<Product, "description">;

const productPreview: ProductPreview = {
id: 1,
name: "Cool Gadget",
price: 99.99
};

以下にこれらのユーティリティ类型をまとめた表があります:

ユーティリティ类型 説明
Partial Tのすべてのプロパティをオプションにします
Pick<T, K> TからプロパティのセットKを選択して新しい类型を作成します
Omit<T, K> Tからすべてのプロパティを選択し、その後Kを削除して新しい类型を作成します

typeof 类型演算子

TypeScriptのtypeof类型演算子は、既存の値の形状に基づいて新しい类型を作成します。まるでオブジェクトの形を取って、同じ形状のオブジェクトを更多く作るようなものです。

例を見てみましょう:

const user = {
name: "John",
age: 30,
location: "New York"
};

type User = typeof user;

const anotherUser: User = {
name: "Jane",
age: 25,
location: "London"
};

この例では、typeof userを使って新しい类型Userを作成し、それがuserオブジェクトの形状にマッチします。これは複数のオブジェクトが同じ構造を持つことを確保したいときに非常に便利です。

別の例でもtypeofが役に立つ場面を見てみましょう:

const colors = ["red", "green", "blue"] as const;
type Color = typeof colors[number];

let myColor: Color = "red"; // これは大丈夫
myColor = "yellow"; // エラー: Type '"yellow"' は '"red" | "green" | "blue"' の型に割り当てられません。

この場合、typeofとインデックスアクセス类型を使って、定数配列からユニオン类型を作成しています。

そして、ここまでがTypeScriptの类型の世界での旅です。既存の类型から新しい类型を作成する方法を学びました。実践が完璧を生むことを忘れずに、これらの概念を自分のプロジェクトで実験してみてください。幸せなコーディングを、そして类型がいつもあなたに味方してくれることを祈っています!

Credits: Image by storyset