今天继续学 TypeScript 类型体操。按难度来的,学习下第7题 Readonly 和第11题 Tuple to Object。
7、Readonly
Readonly 的使用
Readonly
是 Typescript 提供的内置工具类型,Readonly<T>
,用于将类型 T
中的所有属性设置为只读。
举个例子,如何使用 Readonly<T>
工具类型:
typescript
interface Person {
name: string;
age: number;
}
const person: Readonly<Person> = {
name: 'Alice',
age: 25,
};
person.name = 'Bob'; // 编译错误,无法修改只读属性
console.log(person); // 输出: { name: 'Alice', age: 25 }
在上述示例中,我们使用 Readonly<Person>
工具类型将 Person
类型中的所有属性设置为只读。然后,我们创建了一个 person
对象,并尝试修改其 name
属性,但由于 person
是只读类型,所以会在编译时报错。
通过使用 Readonly<T>
工具类型,我们可以方便地将任何类型的属性设置为只读,提高代码的可靠性和安全性。
Readonly 的实现
1、首先在 TypeScript 中,可以使用 readonly
修饰符来指定一个只读属性。
typescript
interface Person {
readonly name: string;
age: number;
}
const person: Person = {
name: 'Alice',
age: 25,
};
person.name = 'Bob'; // 编译错误,无法修改只读属性
console.log(person); // 输出: { name: 'Alice', age: 25 }
通过使用 readonly
修饰符,我们可以指定某个属性为只读,防止其被修改。
2、然后在 TypeScript 类型体操之 Pick 中我们已经学到了关键字 in
和 keyof
。
在映射类型中,
in
关键字用于遍历一个类型的属性名,并对每个属性进行相应的操作。
keyof
是 TypeScript 中的一个关键字,用于获取一个类型的所有属性名组成的联合类型。
现在可以很简单的实现 Readonly
了。
ts
type MyReadonly<T> = {
readonly [key in keyof T]: T[key]
}
11、Tuple to Object
这个个人认为是相对有点难度的题目。之前面试的时候被问到过,当时真的是一脸懵逼。T^T 然后就挂了。
传入一个元组类型,将这个元组类型转换为对象类型,这个对象类型的键/值都是从元组中遍历出来。
例如:
ts
const tuple = ['tesla', 'model 3', 'model X', 'model Y'] as const
type result = TupleToObject<typeof tuple>
// { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}
1、什么是元组类型
元组类型是 TypeScript 中的一种特殊数据类型,它允许我们定义一个固定长度和固定类型顺序的数组。
在元组类型中,每个元素的类型可以是不同的。我们可以通过使用方括号 []
来定义一个元组类型,并在方括号中指定每个元素的类型。
以下是一个示例代码,展示了如何定义和使用元组类型:
typescript
let tuple: [string, number, boolean];
tuple = ['apple', 10, true]; // 合法赋值
tuple = [10, 'apple', true]; // 错误,元素类型不匹配
let name: string = tuple[0]; // 类型为 string
let age: number = tuple[1]; // 类型为 number
let isValid: boolean = tuple[2]; // 类型为 boolean
在上述示例中,我们定义了一个名为 tuple
的元组类型,其中包含三个元素,分别是 string
、number
和 boolean
类型。然后,我们可以将符合元组类型定义的值赋给 tuple
变量。
注意,元组类型要求每个元素的类型和顺序都要与定义一致。如果赋值时不符合元组类型的定义,TypeScript 编译器会报错。
2、获取元组类型中所有元素的联合类型
我们可以通过 T[number]
表示元组类型 T
中的所有元素的联合类型。因为下标是 number
类型,而通过下标我们可以获取所有的元组元素,所以 T[number]
就可以获取所有的元素了。代码举例:
typescript
type Tuple = [string, number, boolean];
type TupleElements = Tuple[number]; // 类型为 string | number | boolean
3、通过 in
来遍历联合类型
前面提到了
在映射类型中,
in
关键字用于遍历一个类型的属性名,并对每个属性进行相应的操作。
我们通过 in
来遍历映射类型的属性时,需要遍历所有属性名组成的联合类型。
在之前获取映射类型的属性名组成的联合类型时,我们可以通过 keyof
获取,而对于元组类型,我们直接通过 T[number]
获取。
所以现在可以写出答案:
ts
type TupleToObject<T extends readonly any[]> = {
[key in T[number]]: key
}
但这个时候还是会报错,原因是对象的键只可能使 string
,number
和 symbol
这三种类型,而 any
却超出了这个范围,所以简单修改一下即可。
ts
type TupleToObject<T extends readonly (string | number | symbol)[]> = {
[key in T[number]]: key
}
为什么要加 readonly
题目给定的代码中加了 readonly
,在这里加不加有什么区别呢?
ts
type TupleToObject<T extends (string | number | symbol)[]> = {
[key in T[number]]: key
}
const tuple = [1, '2', 3, '4']
type ObjectByTuple = TupleToObject<typeof tuple>
// type ObjectByTuple = {
// [x: string]: string;
// [x: number]: number;
// }
如果这里我们原始对象为非 const
的时候,我们可以看到得到的映射类型是下标类型对应值类型,但是和元组中具体的值无关。
而当我们尝试指定 const
和相对应的 readonly
:
ts
type TupleToObject<T extends readonly (string | number | symbol)[]> = {
[key in T[number]]: key
}
const tuple = [1, '2', 3, '4'] as const
type ObjectByTuple = TupleToObject<typeof tuple>
// type ObjectByTuple = {
// 1: 1;
// 2: "2";
// 3: 3;
// 4: "4";
// }
我们可以看到,得到的映射类型是下标对应相对的值。
对比题目要求,可以知道我们需要的是下面这个解法。