typescript结构化类型应用两例

介绍

结构化类型是typescript类型系统的一个重要特性,如果不了解这个特性,则经常会被typescript的行为搞得一头雾水,导致我们期待的行为与实际的行为不一致。今天我们就来看两个例子。

不了解结构化类型的同学,可以先看看这篇:// 插入文章链接。

第一个例子

下面的代码定义了一个Person类型

typescript 复制代码
interface Person {
  name: string;
  age: number;
}

然后又定义了一个函数打印这个类型的对象

typescript 复制代码
function printPerson(person: Person) {
  console.log(`Name: ${person.name}, Age: ${person.age}`);
}

按道理来说,要调用这个函数,必须传递一个Person类型的对象,但是你会发现,直接传一个对象进去也行。

typescript 复制代码
printPerson({ name: "Alice", age: 30 });

这段代码没有报错,为什么呢?因为typescript的结构化类型系统认为,只要传入的对象包含了Person类型所需的所有属性,就可以被认为是Person类型。你甚至可以多加一些属性,比如:

typescript 复制代码
printPerson({ name: "Alice", age: 30, location: "Wonderland" });

代码一样可以正常运行!

为什么?因为在typescript中,类型是基于结构的,而不是基于名称的。只要对象的结构符合要求,就可以被认为是该类型。如果一个类型A包含了类型B的所有属性,那么类型A就可以被认为是类型B。在使用类型B的地方,就可以使用类型A代替。

第二个例子

还是以上面的Person类型为例,假设我们要打印Person对象中的所有属性,有的同学可能不假思索的写下如下代码:

typescript 复制代码
interface Person {
  name: string;
  age: number;
}

const person: Person = { name: "Alice", age: 30 };

function printProperties(person: Person) {
  for (const property in person) {
    console.log(`${property}: ${person[property]}`);
  }
}

printProperties(person);

但是这段代码却报错了:

bash 复制代码
TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Person'. No index signature with a parameter of type 'string' was found on type 'Person'.

当我第一次看到这个错误时,我只想撞墙,我哪里用any了,这不是胡扯吗?但这不是对待错误的正确态度,这种错误如果不彻底解决,那么它就会一直困扰你,只有将它彻底击败,下次再遇到时才能得心应手!

仔细看一下这个报错,它大概描述了两件事情:

  1. string类型的值不能用来索引Person类型。
  2. Person类型没有定义索引签名。

其实这两件事本质上说的是一个问题,那就是在TypeScript中,只有在类型中显式定义了索引签名,才能使用string类型的值来索引该类型。那么我们就给Person类型添加一个索引签名:

方式一:为Person类型添加索引签名

typescript 复制代码
interface Person {
  name: string;
  age: number;
  [key: string]: any; // 索引签名
}

[key: string]: any; 这行代码的意思是,Person类型可以有任意数量的属性,属性名必须是字符串类型 ([key: string]),属性值可以是任意类型(any)。

现在我们再来运行printProperties函数,就不会报错了。

方式二:使用keyof关键字

坦白的说,为了一个遍历函数给Person类型添加一个索引签名有点过于冗余了,其实我们可以使用另一个方法来解决这个问题,那就是使用keyof关键字来获取Person类型的所有属性名。

typescript 复制代码
function printProperties(person: Person) {
  for (const property in person) {
    console.log(`${property}: ${person[property as keyof typeof person]}`);
  }
}

来看这一句代码property as keyof typeof person, 它的执行步骤是这样的:

  1. 先执行typeof person,得到Person类型。
  2. 再执行keyof Person,得到Person类型的所有属性名的联合类型 - name | age
  3. 最后使用as操作符将property转换为这个联合类型。

这样做的好处是,property的类型被限制为Person类型的属性名,在本例中就是nameage这两个属性,绝不会超出这个范围,这样就可以安全地索引person对象了。

眼力好的同学可能已经发现了,上面这个写法可以简化一下,property as keyof typeof person可以简化为property as keyof Person,因为person的类型就是Person,所以我们可以直接使用Person类型来代替。这样可以节省一个typeof操作符的使用。

方式三:使用Object.entries

当然,我们还可以使用Object.entries方法来遍历对象的属性,这样就不需要担心索引签名的问题了。

typescript 复制代码
function printProperty(person: Person) {
  Object.entries(person).forEach(([key, value]) => {
    console.log(`${key}: ${value}`);
  });
}

分析一下这段代码:

  1. Object.entries方法会返回一个二维数组,其中每个元素又是一个数组,这个数组包含了对象的属性名和属性值。以上面的person对象为例,Object.entries(person)会返回[['name', 'Alice'], ['age', 30]]
  2. 接下来的forEach方法会遍历这个数组,这里使用了一个数组解构操作符([key, value]),将每个属性的名字赋值给key,属性的值赋值给value,
  3. 最后使用console.log打印出来。

我比较喜欢方式三,简洁易懂,无需额外的操作。

今天就到这里了,觉得有用就点个关注吧,我们下次再见,我要去打弹弓了。

相关推荐
OEC小胖胖3 小时前
告别 undefined is not a function:TypeScript 前端开发优势与实践指南
前端·javascript·typescript·web
行云&流水3 小时前
Vue3 Lifecycle Hooks
前端·javascript·vue.js
老虎06273 小时前
JavaWeb(苍穹外卖)--学习笔记04(前端:HTML,CSS,JavaScript)
前端·javascript·css·笔记·学习·html
三水气象台3 小时前
用户中心Vue3网页开发(1.0版)
javascript·css·vue.js·typescript·前端框架·html·anti-design-vue
烛阴4 小时前
Babel 完全上手指南:从零开始解锁现代 JavaScript 开发的超能力!
前端·javascript
CN-Dust4 小时前
[FMZ][JS]第一个回测程序--让时间轴跑起来
javascript
全宝5 小时前
🎨前端实现文字渐变的三种方式
前端·javascript·css
yanlele6 小时前
前端面试第 75 期 - 2025.07.06 更新前端面试问题总结(12道题)
前端·javascript·面试
妮妮喔妮6 小时前
【无标题】
开发语言·前端·javascript
fie88896 小时前
浅谈几种js设计模式
开发语言·javascript·设计模式