介绍

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

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

第一个例子

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

interface Person {
name: string;
age: number;
}

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

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

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

printPerson({ name: "Alice", age: 30 });

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

printPerson({ name: "Alice", age: 30, location: "Wonderland" });

代码一样可以正常运行!

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

第二个例子

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

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);

但是这段代码却报错了:

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类型添加索引签名

interface Person {
name: string;
age: number;
[key: string]: any; // 索引签名
}

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

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

方式二:使用keyof关键字

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

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方法来遍历对象的属性,这样就不需要担心索引签名的问题了。

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打印出来。

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

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

typescript结构化类型应用两例的更多相关文章

  1. TypeScript 学习笔记 — 类型兼容 (十)

    目录 一.基本数据类型的兼容性 二.接口兼容性 三.函数的兼容性 四.类的兼容性 类的私有成员和受保护成员 五.泛型的兼容性 六.枚举的兼容性 标称类型简短介绍 TS 是结构类型系统(structur ...

  2. 结构化日志类库 ---- Serilog库

    在过去的几年中,结构化日志已经大受欢迎.而Serilog是 .NET 中最著名的结构化日志类库 ,我们提供了这份的精简指南来帮助你快速了解并运用它. 0. 内容 设定目标 认识Serilog 事件和级 ...

  3. NumPy之:结构化数组详解

    目录 简介 结构化数组中的字段field 结构化数据类型 创建结构化数据类型 从元组创建 从逗号分割的dtype创建 从字典创建 操作结构化数据类型 Offsets 和Alignment Field ...

  4. GO语言基础(结构+语法+类型+变量)

    GO语言基础(结构+语法+类型+变量) Go语言结构 Go语言语法 Go语言类型 Go语言变量       Go 语言结构 Go 语言的基础组成有以下几个部分: 包声明 引入包 函数 变量 语句 &a ...

  5. NoSQL生态系统——类似Bigtable列存储,或者Dynamo的key存储(kv存储如BDB,结构化存储如redis,文档存储如mongoDB)

    摘自:http://www.ituring.com.cn/article/4002# NoSQL系统的数据操作接口应该是非SQL类型的.但在NoSQL社区,NoSQL被赋予了更具有包容性的含义,其意为 ...

  6. 详解Google-ProtoBuf中结构化数据的编码

    本文的主要内容是google protobuf中序列化数据时用到的编码规则,但是,介绍具体的编码规则之前,我觉得有必要先简单介绍一下google protobuf.因此,本文首先会介绍一些google ...

  7. 深入研究 Win32 结构化异常处理(好多相关文章)

    摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的 ...

  8. 深入研究 Win32 结构化异常处理(作者博客有许多SEH的研究文章)

    摘要 就像人们常说的那样,Win32 结构化异常处理(SEH)是一个操作系统提供的服务.你能找到的所有关于 SEH 的文档讲的都是针对某个特定编译器的.建立在操作系统层之上的封装库.我将从 SEH 的 ...

  9. DeepLearning.ai学习笔记(三)结构化机器学习项目--week2机器学习策略(2)

    一.进行误差分析 很多时候我们发现训练出来的模型有误差后,就会一股脑的想着法子去减少误差.想法固然好,但是有点headlong~ 这节视频中吴大大介绍了一个比较科学的方法,具体的看下面的例子 还是以猫 ...

  10. 结构化您的Python工程

    我们对于"结构化"的定义是您关注于怎样使您的项目最好地满足它的对象性,我们 需要去考虑如何更好地利用Python的特性来创造简洁.高效的代码.在实践层面, "结构化&qu ...

随机推荐

  1. 【Node.js】npm配置国内镜像源

    [服务器]npm配置国内镜像源 零.问题 配置Node.js的HTTPS的时候,下载不了 一.解决 这里使用的是淘宝的镜像: npm config set registry https://regis ...

  2. Linux下时区/系统时间/硬件时间的设置

    涨姿势,顺带笔记,留爪. 先简述下时区/系统时间/硬件时间的3个主要命令吧 tzselect tzselect命令主要针对时区设置和查看 tz=timezone的缩写,直译=时区 date date命 ...

  3. C# 多项目打包时如何将项目引用转为包依赖

    项目背景 最近开发一组类库,大约会有五六个项目.一个Core,加上若干面向不同产品的实现库,A/B/C/D...它们都依赖Core. 首先,我想统一版本号,这个容易,通过Directory.Build ...

  4. CSP - J理论(1)

    CSP-J理论(1) CSP-J理论合集跳转 目录 本目录中所有标题单击均可以快速跳转哦 一.排列组合与概率 $\ \ \ \ \ $1.排列 $\ \ \ \ \ $2.组合 $\ \ \ \ \ ...

  5. virtualbox怎么设置鼠标在物理机和虚拟机间切换

    一.先打开管理,在"全局设定"中选择"热键",会看到如下图所示的内容,默认使用右ctrl键让鼠标离开虚拟机

  6. 基于Zookeeper实现调度任务选主及心跳检测

    在微服务架构中使用ZooKeeper实现分布式任务调度选主,并确保Follower节点能实时监控Master状态并及时触发重新选举,可以通过以下方案实现: 一.核心设计原理 1. ZooKeeper特 ...

  7. Java--Calendar类,Date类的简单使用,日期的格式化

    package demo; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; / ...

  8. CUDA原子操作

    这节主要涉及到一个多线程情况下存在的数据竞争问题 -- 多个线程同时访问共享数据时,由于没有正确的同步机制,导致数据出现不一致的情况. C/C++ 多线程中,可以通过互斥锁(mutex).原子操作(a ...

  9. 操作系统:设备I/O -- 设备如何处理内核I/O包

    上一讲实现了建立设备的接口,相当于制定了部门的相关法规,只要遵循这些法规就能建立一个部门.一个部门的职责不难确定,它应该能对上级下发的任务作出响应,并完成相关工作,而这对应到设备,就是如何处理内核的I ...

  10. 开发一组交易信号--K线与10均线的关系

    K线上穿/下穿10日均线,如图所示: 类似于,之前写的基于聚宽平台写的一个典型的双均线策略思想类似,当K线上穿10日均线时,发出买入信号,当K先下穿10日均线时,发出卖出信号. 比较当前的收盘价和MA ...