TypeScript——类型系统与类型推导

前言

TypeScript 是由 Microsoft 开发的一种开放源代码语言。 它是 JavaScript 的一个超集,这意味着你可以在 TypeScript 中使用 JS 已存在的所有语法,并且所有 JavaScript 脚本都可以当作 TypeScript 脚本,此外它还增加了一些自己的语法。TypeScript 对 JavaScript 添加的最的核心功能,就是它的类型系统。

1. 类型系统

在 TypeScript 中,可以使用类型提示来识别变量或参数的数据类型。 使用类型提示,可以描述对象的形状,这样不仅可以提供更好的文档,还能使 TypeScript 能够更好的验证代码是否正常工作。

通过静态类型检查,TypeScript 在开发初期就能发现代码问题,而 JavaScript 往往只能够在浏览器中运行时才会发现。 类型还让你可以描述代码的用途, 如果你是在团队中工作,那么在多人协作开发时也能更简单、高效。

类型还可以为开发工具提供各种便捷和生产力优势,例如 IntelliSense(智能感知)、基于符号的导航、转到定义、查找所有引用、语句结束和代码重构。

2. 类型声明

基本类型声明

TypeScript 代码最明显的特征,就是为 JavaScript 变量加上了类型声明。

let foo: string

上面示例中,变量 foo 的后面使用冒号,声明了它的类型为 string

类型声明的写法,一律为在标识符后面添加 “冒号 + 类型” 。函数参数和返回值,也是这样来声明类型,下面是 TypeScript 中常用的声明类型的方式:

let foo: stringlet age: numberlet bar: booleanlet b: nulllet c: undefinedlet d: bigintlet e: symbolfunction getVal(val: string): string {return val
}

同样的它们只能被赋值为该类型的值:

foo = '123'age = 123bar = trueb = nullc = undefinedd = 123456ne = Symbol()

object 类型

根据 Javascript 的设计,object 可以作为所有对象、数组和函数的类型:

let a: objecta = {}
a = []
a = () => {}

上面示例中,对象、数组、函数都属于 object 类型。虽然 TypeScript 这样的行为比较贴合 JavaScript 作为弱类型语言的表现形式,但是在实际开发应用中却很少使用 object 作为以上三者的类型声明。对象、数组、函数的类型都有更为具体、规范的定义方式:

interface Person {name: string
}type Fn = (args: any) => voidlet a: Personlet b: any[]let c: Fn

以上分别列举了最常见的用法,定义了对象、数组、函数的类型。而 interfacetype 的强大之处绝不仅仅于此,在后续的篇幅中我们会详细讲解这两个类型。

undefined 和 null 类型

值得注意的是,undefinednull 是所有类型的子类型,在非严格模式下undefinednull 这两种类型的变量可以赋值给其他类型,如下:
在这里插入图片描述

然而在严格模式下,上述的写法就会报错:

在这里插入图片描述

这里推荐开启 "strict": true 直接启用严格模式开发,可以更严格地检测类型错误,以减少错误的发生。

3. interface「接口」

interface “接口”,我们前端很熟悉的就是跟后端同学一起联调接口,但却不能和 TypeScript 中的 “接口” 混为一谈。

在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。

如何描述对象的形状

TypeScript 中的接口是一个非常灵活的概念,除了可用于 对类的一部分行为进行抽象 以外,也常用于描述对象的形状

interface Person {name: stringage: number
}let p: Personp = {name: '',age: 18
}

上述的代码就是 interface 的一个基本用法。我们都知道,在 JavaScript 中对象是可以随意的增加、删除、修改属性的,这样具备极大的灵活性,却也可能带来一定的开发风险。

而 TypeScript 只关心我们赋值给变量的 “值的结构”,它只关心它是否具有预期的属性。 只关心类型的结构和功能,这就是为什么 TypeScript 常常被称之为是一个 结构化类型 的类型系统。当编写以下代码后,TypeScript 会及时给出报错:

p.age = ''
// TS2322: Type  string  is not assignable to type  number p.gender = 'male'
// TS2339: Property  gender  does not exist on type  Person delete p.name
// TS2790: The operand of a  delete  operator must be optional.

灵活的 interface

那么可能有的同学可能就有疑惑了,我就是要这样写代码,TypeScript 这么不灵活,老子 TM 不学了 😡😡😡。其实不然,通过下述写法在保留可维护性、安全性的同时还能具备一定的灵活性:

interface Person {name?: string // 可选属性age: number | string // 联合类型[p: string]: any; // 任意属性
}let p: Personp = {name: '',age: 18
}p.age = ''p.gender = 'male'delete p.name

我们可以在属性名后面加一个 ? 标记表示这个属性是可选的。这样在 初始化对象的时候 就可以根据需要选择是否添加某个属性,并且在后续操作中也可以 选择删除或增加 这个属性。

通过任意属性,我们可以很灵活的给一个对象扩展新的属性。在某些时候,对象属性值的类型可能会有多种,我们则可以用 联合类型 来定义。

4. type「类型别名」

我们已经学会在类型注解里直接使用对象类型和联合类型,这很方便,但有的时候,一个类型会被使用多次,此时我们更希望通过一个单独的名字来引用它。

这就是类型别名(type alias)。所谓类型别名,顾名思义,一个可以指代任意类型的名字。类型别名的语法:

type Name = string;type NameResolver = () => string;type NameOrResolver = Name | NameResolver;function getName(n: NameOrResolver): Name {if (typeof n === 'string') {return n;} else {return n();}
}

也能用来定义对象和联合类型:

type Age = numbertype Name = stringtype Person = {name: Nameage: Age
}type Foo = Age | Name

跟定义 interface 很类似,将上述的示例代码改改就行:

type Age = number | stringtype Person = {name?: string // 可选属性age: Age // 联合类型[p: string]: any; // 任意属性
}let p: Personp = {name: '',age: 18
}p.age = ''p.gender = 'male'delete p.name

以上的代码可以直接运行,也是没有任何问题的。

5. typeof

JavaScript 中有一个操作符 typeof 可以用来获取一个值的类型,但是我们可能某些时候不会使用它,因为它不能准确分辨出对象和数组。在 TypeScript 中有一个同样的类型操作符 typeof ,它可以用来获取一个值 TS 类型,代码如下:

const info = {name: 'John',
}const list = [1, 2, 3]type Info = typeof infotype List = typeof list

类型 Info 此时等同于 {name: string}List 等同于 number[],我们来试一试加在变量上的表现:

在这里插入图片描述

通过使用 typeof 操作符可以获取任意值的 TS 类型,当某个复杂的对象已经写好了而我们又不太想手写它的类型时,typeof 就变得尤为有用:

const person = {name: 'lison',age: 18,id: 1,school: '清华大学',address: '北京市',birthday: '2001-01-01',height: 170,weight: 120,hobby: ['sleep', 'eat', 'run'],score: {math: 100,chinese: 90,english: 80}
}function printPerson(p: typeof person) {console.log(p.name)
}

还有一个需要注意的点是,在 TypeScript 中,const 声明的变量具有字面量类型(literal types)。字面量类型是一种特殊的类型,用于表示一个固定的、不可变的值。这些类型包括字符串、数字、布尔值、对象、数组和元组等。当你使用 const 声明一个变量并给它赋一个具体的值时,TypeScript 会推断该变量的类型为字面量类型。例如:

const pi = 3.14159type PI = typeof pi // 3.14159

当类型 PI 被作为某个变量的类型时,它就只能被赋值为 3.14159,否则就会报错:
在这里插入图片描述

6. 类型推导

如果没有明确的指定类型,那么 TypeScript 会依照类型推导(Type Inference)的规则推导出一个类型。

什么是类型推导

以下代码虽然没有指定类型,但是会在编译的时候报错:

let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.

事实上,它等价于:

let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7;// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.

TypeScript 会在没有明确的指定类型的时候会通过解析代码推测出一个类型,这就是类型推导。正因为有这样的功能,在日常开发中,对于一些简单的、不会更改的数据我们可以不用显式去定义类型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/81266.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM——类加载与字节码技术—字节码指令

2.字节码指令 2.1 入门 jvm的解释器可以识别平台无关的字节码指令,解释为机器码执行。 2a b7 00 01 b1 this . init() return 准备了System.out对象,准备了参数“hello world”,准备了对象的方法println(String)V&#xff…

1.文章复现《热电联产系统在区域综合能源系统中的定容选址研究》(附matlab程序)

0.代码链接 文章复现《热电联产系统在区域综合能源系统中的定容选址研究》(matlab程序)-Matlab文档类资源-CSDN文库 1.简述 本文采用遗传算法的方式进行了下述文章的复现并采用电-热节点的方式进行了潮流计算以降低电网的网络损耗 分析了电网的基本数…

C语言练习1(巩固提升)

C语言练习1 选择题 前言 “人生在勤,勤则不匮。”幸福不会从天降,美好生活靠劳动创造。全面建成小康社会的奋斗目标,为广大劳动群众指明了光明的未来;全面建成小康社会的历史任务,为广大劳动群众赋予了光荣的使命&…

在Ubuntu上安装和设置RabbitMQ服务器,轻松实现外部远程访问

文章目录 前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道 4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址 前言 RabbitMQ是一个在 AMQP(高级消息队列协议)基…

架构评估-架构师之路(十二)

软件系统质量属性 软件系统质量熟悉分为 开发期质量属性 和 运行期质量属性。 质量属性 性能:指 系统的响应能力,如 响应时间,吞吐率。 设计策略:优先级队列、增加计算资源、减少计算开销、引入并发机制、采用资源调度。 可靠…

【jsthreeJS】入门three,并实现3D汽车展示厅,附带全码

首先放个最终效果图: 三维(3D)概念: 三维(3D)是一个描述物体在三个空间坐标轴上的位置和形态的概念。相比于二维(2D)只有长度和宽度的平面,三维增加了高度或深度这一维度…

【ElasticSearch】一键安装ElasticSearch与Kibana以及解决遇到的问题

目录 一、安装ES 二、安装Kibana 三、遇到的问题 一、安装ES 按顺序复制即可 docker network create es-net # 创建网络 docker pull images:7.12.1 # 拉取镜像 mkdir -p /root/es/data # 创建数据卷 mkdir -p /root/es/plugins # 创建数据卷 chmod 777 /root/es/** # 设置权…

QT中资源文件resourcefile的使用,使用API完成页面布局

QT中资源文件resourcefile的使用 之前添加图标的方法使用资源文件的方法创建资源文件资源文件添加前缀资源文件添加资源使用资源文件中的资源 使用API完成布局使用QHBoxLayout完成水平布局使用QVBoxLayout完成垂直布局使用QGridLayout完成网格布局 在Qt中引入资源文件好处在于他…

Firefox(火狐),使用技巧汇总,问题处理

本文目的 说明火狐如何安装在C盘之外的盘,即定制安装路径。如何将同步功能切换到本地服务上。默认是国际服务器。安装在C盘之后如何解决,之前安装的扩展无法自动同步的问题。扩展或插件失效问题解决方案。顺带分享一下,火狐的一些比较好用的…

使用Python搭建服务器公网展示本地电脑文件

文章目录 1.前言2.本地http服务器搭建2.1.Python的安装和设置2.2.Python服务器设置和测试 3.cpolar的安装和注册3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 1.前言 Python作为热度比较高的编程语言,其语法简单且语句清晰,而且python有…

操作系统练习:在Linux上创建进程,及查看进程状态

说明 进程在执行过程中可以创建多个新的进程。创建进程称为“父进程”,新的进程称为“子进程”。每个新的进程可以再创建其他进程,从而形成进程树。 每个进程都有一个唯一的进程标识符(process identifier,pid)。在L…

vue学习之hello world

依赖引入 <script src"https://unpkg.com/vue2.6.10/dist/vue.js"></script>Hello world 实现 <html><head><style></style></head><body><script src"https://unpkg.com/vue2.6.10/dist/vue.js">…