likes
comments
collection
share

TypeScript 类型体操之 Readonly & Tuple to Object

作者站长头像
站长
· 阅读数 10

今天继续学 TypeScript 类型体操。按难度来的,学习下第7题 Readonly 和第11题 Tuple to Object

7、Readonly

Readonly 的使用

Readonly 是 Typescript 提供的内置工具类型,Readonly<T>,用于将类型 T 中的所有属性设置为只读。

举个例子,如何使用 Readonly<T> 工具类型:

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 修饰符来指定一个只读属性。

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 修饰符,我们可以指定某个属性为只读,防止其被修改。

在映射类型中,in 关键字用于遍历一个类型的属性名,并对每个属性进行相应的操作。

keyof 是 TypeScript 中的一个关键字,用于获取一个类型的所有属性名组成的联合类型。

现在可以很简单的实现 Readonly 了。

type MyReadonly<T> = {
  readonly [key in keyof T]: T[key]
}

11、Tuple to Object

这个个人认为是相对有点难度的题目。之前面试的时候被问到过,当时真的是一脸懵逼。T^T 然后就挂了。

传入一个元组类型,将这个元组类型转换为对象类型,这个对象类型的键/值都是从元组中遍历出来。

例如:

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 中的一种特殊数据类型,它允许我们定义一个固定长度和固定类型顺序的数组。

在元组类型中,每个元素的类型可以是不同的。我们可以通过使用方括号 [] 来定义一个元组类型,并在方括号中指定每个元素的类型。

以下是一个示例代码,展示了如何定义和使用元组类型:

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 的元组类型,其中包含三个元素,分别是 stringnumberboolean 类型。然后,我们可以将符合元组类型定义的值赋给 tuple 变量。

注意,元组类型要求每个元素的类型和顺序都要与定义一致。如果赋值时不符合元组类型的定义,TypeScript 编译器会报错。

2、获取元组类型中所有元素的联合类型

我们可以通过 T[number] 表示元组类型 T 中的所有元素的联合类型。因为下标是 number 类型,而通过下标我们可以获取所有的元组元素,所以 T[number] 就可以获取所有的元素了。代码举例:

type Tuple = [string, number, boolean];

type TupleElements = Tuple[number]; // 类型为 string | number | boolean

3、通过 in 来遍历联合类型

前面提到了

在映射类型中,in 关键字用于遍历一个类型的属性名,并对每个属性进行相应的操作。

我们通过 in 来遍历映射类型的属性时,需要遍历所有属性名组成的联合类型。

在之前获取映射类型的属性名组成的联合类型时,我们可以通过 keyof 获取,而对于元组类型,我们直接通过 T[number] 获取。

所以现在可以写出答案:

type TupleToObject<T extends readonly any[]> = {
  [key in T[number]]: key
}

但这个时候还是会报错,原因是对象的键只可能使 stringnumbersymbol 这三种类型,而 any 却超出了这个范围,所以简单修改一下即可。

type TupleToObject<T extends readonly (string | number | symbol)[]> = {
  [key in T[number]]: key
}

为什么要加 readonly

题目给定的代码中加了 readonly ,在这里加不加有什么区别呢?

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

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";
// }

我们可以看到,得到的映射类型是下标对应相对的值。

对比题目要求,可以知道我们需要的是下面这个解法。