TypeScript:从类型创建类型
你好,未来的TypeScript大师们!我很高兴能成为你们在这个激动人心的TypeScript类型世界中的向导。作为一名有着多年经验的计算机科学教师,我亲眼见证了理解类型是如何将初学者转变为编码高手的。那么,让我们一起开始这段冒险吧!
联合类型
想象你在一个冰淇淋店,你可以选择巧克力或者香草。在TypeScript中,我们称这种选择为“联合类型”。就像是说,“我要这个或者那个。”
让我们深入一些代码:
type Flavor = "chocolate" | "vanilla";
let myIceCream: Flavor;
myIceCream = "chocolate"; // 这是对的
myIceCream = "strawberry"; // 错误!类型 '"strawberry"' 不能赋值给类型 'Flavor'。
在这个例子中,Flavor
是一个联合类型,可以是 "chocolate" 或 "vanilla"。当我们尝试将 "strawberry" 赋值给 myIceCream
时,TypeScript会说,“等等!那不在菜单上!”
联合类型在你想要限制变量可能有的值时非常有用。这里再举一个例子:
type Answer = "yes" | "no" | "maybe";
function respondToQuestion(answer: Answer) {
if (answer === "yes") {
console.log("太好了!");
} else if (answer === "no") {
console.log("哦不!");
} else {
console.log("我看出你还没决定。");
}
}
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
};
在这个例子中,Person
是一个交叉类型,它结合了 Name
和 Age
。就像说,“一个人必须有名字、姓氏和年龄。”
这里还有一个有趣的例子:
type Swimmer = {
swim: () => void;
};
type Flyer = {
fly: () => void;
};
type FlyingFish = Swimmer & Flyer;
let nemo: FlyingFish = {
swim: () => console.log("在大海中游泳"),
fly: () => console.log("在波浪上飞翔")
};
nemo.swim(); // 输出:在大海中游泳
nemo.fly(); // 输出:在波浪上飞翔
实用类型
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: "学习 TypeScript",
description: "学习2小时",
completed: false
};
const updatedTodo = updateTodo(myTodo, {
description: "学习3小时"
});
console.log(updatedTodo);
// 输出:{ title: "学习 TypeScript", description: "学习3小时", 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: "酷炫小工具",
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"; // 错误:类型 '"yellow"' 不能赋值给类型 '"red" | "green" | "blue"'。
在这个情况下,我们使用 typeof
和索引访问类型来从常量数组创建一个联合类型。
那么,各位,以上就是我们的TypeScript类型之旅,从现有的类型创建新的类型。记住,熟能生巧,所以不要害怕在你的项目中尝试这些概念。快乐编码,愿类型永远站在你这边!
Credits: Image by storyset