TypeScript学习笔记
TypeScript学习笔记
一.TypeScript简介
TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准(ES6 教程)。
TypeScript 由微软开发的自由和开源的编程语言,在 JavaScript 的基础上增加了静态类型检查的超集,添加了诸多现代开发特性。
TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。
二.为什么需要TypeScript
数据类型问题
逻辑漏洞问题
访问不存在的属性
拼写错误
三.编译TypeScript
1.命令行编译
创建
.ts文件全局安装
TypeScript(此处为使用的包管理工具是pnpm,pnpm较npm速度有显著提升且节约空间)
1 | |
- 使用
tsc命令编译
1 | |
2.自动化编译
- 全局安装
1 | |
- 创建编译控制文件
1 | |
- 监视目录中
.ts的文件变化
1 | |
3.Code Runner插件(适用VSCode等平台)
- 在VS Code下安装
Code Runner插件 - 进入插件设置,勾选
File Directory As Cwd - 创建
.ts文件 - 在目录所在终端下依次执行
1 | |
- 点击右上角执行按钮
四.类型声明
使用 :来对变量或函数形参,进行类型声明。
1 | |
:也可以给变量附加上字面量类型,但不常见。
1 | |
五.类型推断
TypeScript 能够自动推断变量的类型。比如当你声明一个变量并赋值时,TypeScript 会根据赋值来推断这个变量的类型,不需要每次都显式声明类型。
六.类型总览
JavaScript数据类型
基本数据类型
| 数据类型 | 描述 | 示例 | 特点 |
|---|---|---|---|
Number |
数字类型,包括整数和浮点数 | 42, 3.14 |
所有数字都是64位浮点数存储,包含特殊值Infinity、-Infinity和NaN |
String |
字符串类型,表示文本数据 | "hello", 'world' |
不可变,可以使用单引号或双引号表示 |
Boolean |
布尔类型,表示逻辑值 | true, false |
只有两个值:true和false |
Undefined |
表示未定义的值 | undefined |
变量声明但未赋值时的默认值 |
Null |
表示空值 | null |
表示有意设置的空值 |
Symbol |
ES6新增,表示唯一的、不可变的值 | Symbol('desc') |
主要用于对象属性的唯一标识符 |
BigInt |
ES2020新增,表示任意精度的整数 | 123n |
用于表示大于2^53-1的整数 |
引用数据类型
| 数据类型 | 描述 | 示例 |
|---|---|---|
Object |
对象类型,用于存储键值集合 | {name: 'John'} |
Array |
数组类型,特殊类型的对象 | [1, 2, 3] |
Function |
函数类型,也是对象的一种 | function() {} |
Date |
日期类型 | new Date() |
RegExp |
正则表达式类型 | /pattern/flags |
| …其他对象 | 包括Map, Set, WeakMap, WeakSet等 |
new Map() |
特殊情况
1. Undefined
特点:
表示变量已声明但未赋值时的默认值
类型为
"undefined"使用
typeof检测未声明变量也会返回"undefined"通常表示”缺少值”,即应该有值但未定义
不建议手动赋值为
undefined,应使用null表示有意空值
1 | |
2. Null
特点:
表示”无”的对象,即该处不应该有值
类型为
"object"(这是历史遗留bug)使用
=== null可以准确检测null值通常用于主动释放对象引用
是原型链的终点(
Object.prototype.__proto__ === null)
1 | |
undefined vs null
| 比较点 | undefined |
null |
|---|---|---|
| 含义 | 表示未定义 | 表示空值 |
| 类型 | "undefined" |
"object" |
| 产生方式 | 变量未赋值时的默认值 | 必须显式赋值 |
| 转换为数值 | NaN |
0 |
| 使用场景 | 表示”缺少值” | 表示”无对象” |
| 严格相等比较 | undefined === undefined → true |
null === null → true |
类型检测方法
typeof操作符返回类型字符串
null返回"object"(历史遗留问题,底层是32为)函数返回
"function"
instanceof操作符- 检测对象是否属于特定构造函数
Object.prototype.toString.call()- 最准确的类型检测方法
TypeScript数据类型
包含全部JavaScript数据类型
新增基本数据类型
| 类型 | 描述 | 示例 | 特点 |
|---|---|---|---|
any |
任意类型,跳过类型检查 | let x: any = 'hello'; |
1. 可以赋值为任何值 2. 可以访问任何属性/方法 3. 尽量避免使用 |
unknown |
类型安全的any |
let y: unknown = 4; |
1. 可以赋值为任何值 2. 不能直接操作,需先类型断言或收窄 |
never |
表示永不存在的值的类型 | function error(): never {} |
1. 用于总是抛出异常的函数 2. 无限循环函数的返回类型 |
void |
表示没有返回值的函数 | function fn(): void {} |
1. 与undefined兼容2. 常用于函数无显式返回值 |
新增复合类型
| 类型 | 描述 | 示例 | 特点 |
|---|---|---|---|
tuple |
固定长度和类型的数组 | let x: [string, number]; |
1. 长度固定 2. 每个位置类型固定 3. 越界元素使用联合类型 |
enum |
枚举类型 | enum Color {Red, Green} |
1. 默认从0开始编号 2. 可手动赋值 3. 支持反向映射(数字枚举) |
新增类型定义方式
| 方式 | 描述 | 示例 | 区别 |
|---|---|---|---|
type |
类型别名,可定义各种类型组合 | type Point = {...} |
1. 更灵活,支持联合类型、交叉类型等高级类型 2. 不能重复声明 |
interface |
接口,主要用于定义对象形状 | interface Point {...} |
1. 更适合定义对象结构 2. 支持声明合并 3. 可被类实现(implements) |
类型对比
any vs unknown
| 比较点 | any |
unknown |
|---|---|---|
| 类型安全 | 不安全,编译时不检查 | 安全,使用时需类型断言或收窄 |
| 赋值 | 可赋值给任意类型 | 只能赋值给unknown或any |
| 属性访问 | 可直接访问任意属性 | 不能直接访问属性 |
| 使用建议 | 尽量避免使用 | 优先使用替代any |
void vs undefined vs never
| 比较点 | void |
undefined |
never |
|---|---|---|---|
| 含义 | 函数无返回值 | 未定义的值 | 永不存在的值 |
| 变量使用 | 不能作为变量类型 | 可以作为变量类型 | 不能作为变量实际类型 |
| 函数返回 | 函数无return或返回undefined |
必须显式返回undefined |
函数无法正常返回 |
| 赋值兼容 | 可接受undefined |
不能赋值给void变量 |
是所有类型的子类型 |
原始类型与包装类型
在JavaScript中的这些内置构造函数: Number、String、Boolean,用于创建对应的包装对象, 在日常开发时很少使用,在TypeScript中也是同理,所以在TypeScript中进行类型声明时,通常都是用小写的number、string、bo olean。
原始类型 VS 包装对象:
原始类型:如number、string、boolean,在 JavaScript 中是简单数据
类型,它们在内存中占用空间少,处理速度快。
包装对象:如Number对象、String对象、Boolean对象,是复杂类型,在
内存中占用更多空间,在日常开发时很少由开发人员自己创建包装对象。自动装箱: JavaScript 在必要时会自动将原始类型包装成对象,以便调用方法或访问属性。
七.常用类型
1. any
any:放弃对该变量的类型检查,可赋值给任意类型的变量
1 | |
2. unknown
unknown:未知类型
unknown可以理解为类型安全的具体类型
1 | |
unknown会强制开发者在使用前进行类型检查,可以通过类型判断或类型断言的方式实现安全操作变量。
1 | |
- 读取
any属性的任何属性都不会报错,而unknown则会。
3. never
never:任何值都不是,不能有值。
- 几乎不使用
never,对变量赋值无意义。
1 | |
never一般是TypeScript主动推断出来的never可用于限制函数的返回值(如抛出错误的情况),表示函数不能结束,或者不能正常结束。
1 | |
4. void
void:用于函数返回值声明,表示函数返回值为空。
- 函数返回值为空,调用者也不应该依赖其返回值进行任何操作。
1 | |
注意:编码者没有编写return指定函数返回值,所以logMessage函数是没有显式返回值的,但会有一个隐式返回值 ,是
undefined。虽然函数返回类型为void,但也是可以接受undefined的,简单记: undefined是void可以接受的一种“空”。
- 对函数返回值声明为
void的函数,TS接受如下写法
1 | |
- 返回值类型为void的函数,调用者不应依赖其返回值进行任何操作!:理解来说,被
void声明的函数返回值,TS希望开发者从语义层面上不要关注其返回值,不期望依赖该函数用于其他的操作。
1 | |
1 | |
理解 void 与 undefined:void是一个广泛的概念,用来表达“空”,而 undefined则是这种“空”的具体实现。
因此可以说 undefined是void能接受的一种“空”的状态。也可以理解为: void包含undefined,但void所表达的语义超越了undefined, void是一种意图上的约定,而不仅仅是特定值的限制。
总结:
如果一个函数返回类型为void,那么:
- 从语法上讲:函数是可以返回
undefined的,至于显式返回,还是隐式返回,这无所谓! - 从语义上讲:函数调用者不应关心函数返回的值,也不应依赖返回值进行任何操作!即使我们知道它返回了
undefined。
5.object
object与Object
object:所有非原始类型,可存储对象,函数,数组等。Object:所有可以调用 Object方法的类型,即除了undefined和null的任何值
1 | |
声明类型
声明对象类型
- 对象类型字面量
1 | |
- 索引签名:允许定义对象可以具有任意数量的属性,这些属性的键和类型是可变的。常用于:描述类型不确定的属性,(具有动态属性的对象)。
1 | |
声明函数类型
函数类型注解
1 | |
TypeScript 中的=>在函数类型声明时表示函数类型,描述其参数类型和返回类型。
JavaScript 中的=>是一种定义函数的语法,是具体的函数实现。
声明数组类型
1 | |
6.tuple
Tuple是一种特殊的数组类型,可以存储固定数量的元素,并且每个元素的类型是已知的且可以不同。元组用于精确描述一组值的类型, ?表示可选元素。
1 | |
7.enum
enum:枚举,定义一组命名常量,它能增强代码的可读性,也让代码更好维护。也可以指定枚举成员的初始值,其后的成员值会自动递增。
1 | |
- 数字枚举:数字枚举一种最常见的枚举类型,其成员的值会自动递增,且数字枚举还具备反向映射的特点,在下面代码的打印中,不难发现:可以通过值来获取对应的枚举成员名称 。好处是语义性更强,便于维护。
1 | |
- 字符串枚举:同理,枚举成员的值是字符串
- 常量枚举: 常量枚举是一种特殊枚举类型,它使用 const关键字定义,在编译时会被内联,避免生成一些额外的代码,推荐使用。
何为编译时内联?
所谓“内联”其实就是 TypeScript 在编译时,会将枚举成员引用替换为它们的实际值,而不是生成额外的枚举对象。这可以减少生成JavaScript 代码量,并提高运行时性能。
1 | |
8.type
type:为任何类型创建别名,让代码更简洁,可读性更强;同时更方便的进行类型复用和扩展。
- 类型别名:使用 type关键字定义, type后跟类型名称,即类型别名。
1 | |
- 联合类型:联合类型是一种高级类型,它表示一个值可以是几种不同类型之一。
1 | |
- 交叉类型:允许将多个类型合并为一个类型。合并后的类型将拥有所有被合并类型的成员。交叉类型通常用于对象类型。
1 | |
9.特殊情况
使用类型声明限制函数返回值为void时, TypeScript并不会严格要求函数返回空。
为什么?
是为了确保如下代码成立,我们知道
Array.prototype.push 的返回值是一个数字,而Array.prototype.forEach方法期望其回调的返回类型是void。
10.属性修饰符
| 修饰符 | 含义 | 具体规则 |
|---|---|---|
public |
公开的 | 可以被类内部、子类、类外部访问 |
protected |
受保护的 | 可以被类内部、子类访问 |
private |
私有的 | 可以被类内部访问 |
readonly |
只读属性 | 属性无法修改 |
属性的简写形式
在构造器写类型注解和属性修饰符即可。
1 | |
public修饰符
1 | |
protected修饰符
1 | |
private修饰符
1 | |
readonly修饰符
1 | |
11.抽象类
定义: 抽象类是一种无法被实例化的类,专门用来定义类的结构和行为,类中可以写抽象方法,也可以写具体实现。抽象类主要用来为其派生类提供一个基础结构,要求其派生类必须实现其中的抽象方法。
简单来说: 抽象类不能实例化,其意义是可以被继承,抽象类里可以有普通方法、也可以有抽象方法。
1 | |
总结:何时使用抽象类
定义通用接口:为一组相关的类定义通用的行为(方法或属性)时。
提供基础实现: 在抽象类中提供某些方法或为其提供基础实现,这样派生类就可以继承这些实现。
确保关键实现: 强制派生类实现一些关键行为。
共享代码和逻辑: 当多个类需要共享部分代码时,抽象类可以避免代码重复。
12.interface
interface:一种定义结构的方式,主要作用是为:类、对象、函数等规定一种契约,这样可以确保代码的一致性和类型安全,但要注意interface只能定义格式,不能包含任何实现 !
- 定义类结构
1 | |
- 定义对象结构
1 | |
- 定义函数结构
1 | |
接口继承: 使用
extends关键字实现接口继承接口自动合并
1 | |
何时使用接口:
定义对象格式: 描述数据类型、API响应格式、配置对象……
类的契约: 规定一个类需要视线的哪些方法和属性
扩展已有接口: 一般用于扩展第三方库的类型
13.相似概念的区别
13.1 interface 与 type 的区别
相同点: interface和type 都可以用于定义对象结构,在定义对象结构时两者可以互换。
不同点:
interface:更专注于定义对象和类的结构,支持继承、合并。type:可以定义类型别名、联合类型、交叉类型,但不支持继承和自动合并。
简言之,对于需要复杂继承和扩展的类型,推荐使用interface&extends实现继承。
13.2 interface与抽象类的区别
相同点: 都用于定义一个类的格式
不同点:
1. 接口:只能描述结构,不能有任何实现代码,一个类可以有多个接口。
2. 抽象类:既可以包含抽象方法,也可以包含具体方法,一个类只能继承一个抽象类。
八.泛型
定义: 泛型运行我们在定义函数,类或接口时,使用类型参数来表示未指定的类型,这些参数在具体使用时,才被指定具体的类型,泛型能让同一段代码适用于多种类型,同时仍然保持类型的安全性。
泛型函数
1 | |
注:泛型可以有多个
泛型接口
1 | |
注:泛型接口本身也可以嵌套
泛型类
1 | |
泛型约束
1 | |
九.类型声明文件
类型声明文件是 TypeScript 中的一种特殊文件,通常以.d.ts 作为扩展名。它的主要作用
是为现有的 JavaScript 代码提供类型信息,使得 TypeScript 能够在使用这些 JavaScript 库
或模块时进行类型检查和提示。
在浏览器引入脚本时,注明类型type:module
1 | |