DRY 原则(Don’t repeat yourself)是软件开发中最重要的原则之一,即不要重复自己。应该避免在代码中的两个或多个地方存在重复的业务逻辑。
在 TypeScript 中,映射类型可以帮助我们避免编写重复的代码,蓝狮注册开户它可以根据现有类型和定义的一些规则来创建新类型。下面就来看一下什么是映射类型以及如何构建自己的映射类型。
1、基本概念
在介绍映射类型之前,先来看一些前置知识。
(1)索引访问类型
在 TypeScript 中,我们可以通过按名称查找属性来访问它的类型:
type AppConfig = {
username: string;
layout: string;
};
type Username = AppConfig[“username”];
在这个例子中,通过 AppConfig 类型的索引 username 获取到其类型 string,类似于在 JavaScript 中通过索引来获取对象的属性值。
(2)索引签名
当类型属性的实际名称是未知的,但它们将引用的数据类型已知时,索引签名就很方便。
type User = {
name: string;
preferences: {
[key: string]: string;
}
};
const currentUser: User = {
name: ‘Foo Bar’,
preferences: {
lang: ‘en’,
},
};
const currentLang = currentUser.preferences.lang;
在上面的例子中,currentLang 的类型是 string 而不是 any。此功能与 keyof 运算符一起搭配使用是使映射类型成为可能的核心之一。
(3)联合类型
联合类型是两种或多种类型的组合。它表明值的类型可以是联合中包含的任何一种类型。
type StringOrNumberUnion = string | number;
let value: StringOrNumberUnion = ‘hello, world!’;
value = 100;
下面是一个更复杂的例子,编译器可以为联合类型提供一些高级保护:
type Animal = {
name: string;
species: string;
};
type Person = {
name: string;
age: number;
};
type AnimalOrPerson = Animal | Person;
const value: AnimalOrPerson = loadFromSomewhereElse();
console.log(value.name); // :white_check_mark:
console.log(value.age); // :x:
if (‘age’ in value) {
console.log(value.age); // :white_check_mark:
}
在这个例子中,因为 Animal 和 Person 都有 name 属性,所以第 15 行的 value.name 可以正常输出,没有错误。而第 16 行的 value.age 会编译错误,因为如果 value 是 Animal 类型,则 value 是没有 age 属性的。在第 19 行的 if 块中,因为只有 value 存在 age 属性才能进入这个代码块。所以,在这个 if 块中,value 一定是 Person,TS 可以知道 value 一定是具有 age 属性的,所以编译正确。
(4)keyof 类型运算符
keyof 类型运算符返回传递给它的类型的 key 的联合。
type AppConfig = {
username: string;
layout: string;
};
type AppConfigKey = keyof AppConfig;
在这个例子中,AppConfigKey 类型会被解析为”username” | “layout”。它可以与索引签名一起使用:
type User = {
name: string;
preferences: {
[key: string]: string;
}
};
type UserPreferenceKey = keyof User[“preferences”];
这里,UserPreferenceKey 类型被解析为 string | number。
(5)元组类型
元组是一种特殊的数组类型,其中数组的元素可能是特定索引处的特定类型。它们允许 TypeScript 编译器围绕值数组提供更高的安全性,尤其是当这些值属于不同类型时。
例如,TypeScript 编译器能够为元组的各种元素提供类型安全:
type Currency = [number, string];
const amount: Currency = [100, ‘USD’];
function add(values: number[]) {
return values.reduce((a, b) => a + b);
}
add(amount);
// Error: Argument of type ‘Currency’ is not assignable to parameter of type ‘number[]’.
// Type ‘string’ is not assignable to type ‘number’.
上面的代码中会报错,Currency 类型的参数不能分配给“number[]”类型的参数,string 类型不能分配给 number 类型。
当访问超出元组定义类型的索引处的元素时,TypeScript 能够进行提示:
type LatLong = [number, number];
const loc: LatLong = [48.858370, 2.294481];
console.log(loc[2]);
// Error: Tuple type ‘LatLong’ of length ‘2’ has no element at index ‘2’.
这里,元组类型 LatLong 只有两个元素,当试图访问第三个元素时,就会报错。
(6)条件类型
条件类型是一个表达式,类似于 JavaScript 中的三元表达式,其语法如下:
T extends U ? X : Y
来看一个实际的例子:
type ConditionalType = string extends boolean ? string : boolean;
在上面的示例中,ConditionalType 的类型将是 boolean,因为条件string extends boolean 是始终为 false。
2、映射类型
(1)初体验
在 TypeScript 中,当需要从另一种类型派生(并保持同步)另一种类型时,使用映射类型会特别有用。
// 用户的配置值
type AppConfig = {
username: string;
layout: string;
};
// 用户是否有权更改配置值
type AppPermissions = {
changeUsername: boolean;
changeLayout: boolean;
};
在上面的代码中,AppConfig 和 AppPermissions 之间是存在隐式关系的,每当向 AppConfig 添加新的配置值时,蓝狮注册登陆AppPermissions 中也必须有相应的布尔值。
这里可以使用映射类型来管理两者之间的关系:
type AppConfig = {
username: string;
layout: string;
};
type AppPermissions = {
};
在上面的代码中,只要 AppConfig 中的类型发生变化,AppPermissions 就会随之变化。实现了两者之间的映射关系。
(2)概念
在 TypeScript 和 JavaScript 中,最常见的映射就是 Array.prototype.map():
[1, 2, 3].map(value => value.toString()); // [“1”, “2”, “3”]
这里,我们将数组中的数字映射到其字符串的表示形式。因此,TypeScript 中的映射类型意味着将一种类型转换为另一种类型,方法就是对其每个属性进行转换。
0 Comments