--watch src 代表只监听 src 目录下的文件变化,然后执行下面的命令
-e ts 代表监听 ts 文件的变化,然后执行下面的命令
--exec 代表执行下面的命令
nodemom --watch src -e ts --exec 'node ./dist/index.js'
不用担心,这个错误,并不是我们的错误,原因是默认情况下,TS 会做出下面的几种假设:
- 假设当前环境是 dom 环境
- 如果代码没有模块化操作 import 或者 export 那么默认是全局执行
- 编译的目标代码是 es3
可以加上 TS 的配置文件,或者
tsc命令行的选项参 - 配置文件 使用配置文件后,在使用 tsc 进行编译时候,不能跟上文件名,如果跟上文件名,那么会忽略配置文 使用第三方库简化流程 npm install -g ts-node ts-node: ts 代码在内存中编译执行,同时完成运行 因为 tsc 命令是编译代码,node 只能运行 js 代码,ts-node 是编译并执行代码 nodemon: 监视文件变化,自动编译执行 npm install -g nodemon
基本类型约束
TS 是一个可选的静态的类型系统
如何进行类型约束
仅需要在 变量,函数的参数,函数的返回值位置加上 :类型
TS 在很多场景中可以完成类型推导
any 类型可以认为是任意类型的值,但是使用 any 类型会失去 TS 的类型检查功能
小技巧,如何区分数字字符串和数字,关键看怎么读? 如果按照数字的方式朗读,那么就是数字,否则就是字符串
源代码和编译结果的差异
编译结果中没用类型约束信息
基本类型
- number
- string
- boolean
- object
- array (支持 arr: number[] 这种写法)
- object(表示约束的对象)
- null 和 undefined 是所有类型的子类型,可以赋值给任何类型
(通过添加
--strictNullChecks: true,可以获得更严格的空类型检查,null 和 undefined 只能赋值给自身)
其他常用类型
-
联合类型: 表示一个变量可以是多种类型中的一种,例如:let num: number | string = 10; // 可以是数字或字符串类型。 配合类型保护进行判断,例如: 类型保护:当对某个变量进行类型判断之后,在判断的语句块中便可以确定它的确切类型,typeod 可以触发类型保护
if(typeof strss === "string"){ strss.toLocaleLowerCase() } -
void 类型: 通常用于约束函数的返回值,表示改函数没有返回值。 例如:
function printMessage(message: string): void { console.log(message); } -
never 类型: 通常用于约束函数的返回值,表示改函数永远不会结束,例如抛出异常或无限循环。 例如:
function throwError(message: string): never { throw new Error(message); }function infiniteLoop(): never { while (true) {} } -
字面量类型: 使用一个值进行约束,例如: `let num: 10 = 10; // 只能赋值为 10
-
元祖类型 (Tuple):一个固定长度的数组,且数组中每一项的类型确定。 例如:
let tuple: [string, number] = ["hello", 10]; // 第一项是字符串,第二项是数字 -
any 类型: 类型可以绕过类型检查,任意类型的数据都可以复制给 Any 类型的变量。
类型推断
TypeScript 的一种特性,它可以根据代码的上下文自动推断变量的类型,而不需要显式地指定类型。以下是一些类型推断的示例:
`let num = 10; // TypeScript 推断 num 的类型为 number let str = "hello"; // TypeScript 推断 str 的类型为 string
// 函数返回值类型推断 function add(a: number, b: number) { return a + b; // TypeScript 推断返回值类型为 number }`
类型断言
类型断言是一种告诉 TypeScript 编译器你比它更了解某个值的实际类型的方式。 类型断言有两种方式: 尖括号语法:
let someValue: any = "this is a string"; let strLength: number = (<string>someValue).length;as 语法:let someValue: any = "this is a string"; let strLength: number = (someValue as string).length;
类型别名
类型别名是一种给类型起一个新名字的方式,它可以使代码更具可读性和可维护性。 例如:
type Point = { x: number; y: number; };
函数的相关约束
函数重载:在函数实现之前,对调用函数的多种情况进行声明,例如:
// 得到 a*b 的结果 function combine(a: number, b: number): number; // 得到 a+b 的结果 function combine(a: string, b: string): string;
可选参数:可以在某些参数后面加上问号?,表示参数可以不用一定要传递
` // 注意:可选参数必须要在必选参数后面 function sum1(a: number, b: number, c?:number) { console.log(a, b, c); } sum1(1, 2);
// 默认参数本身就是可选参数 function sum2(a: number, b: number, c = 10) { console.log(a, b, c); } sum2(1, 2); `
拓展类型-枚举
拓展类型:枚举,类型别名,接口,类
枚举通常用于约束某个变量的取值范围 字面量和联合类型配合使用,也可以达到同样的目标
字面量类型的问题
-
在类型约束位置,会产生重复代码,可以使用类型别名解决问题
type Gender = "男" | "女"; let gender: Gender; // 先生 女士 男 女 male female gender = "男"; gender = "女"; function getUserGender(g: Gender) { return g; } -
逻辑含义和真实的值产生了混淆,会导致当修改真实值的时候,产生大量的修改
let gender: Gender; // 先生 女士 男 女 male female gender = "男"; gender = "女"; -
字面量类型不会进入编译结果
枚举
如何定义一个枚举
enum Gender {
枚举字段1 = 枚举值1,
枚举字段2 = 枚举值2,
...
}
枚举会出现在编译结果中,编译结果中表现为对象
枚举的规则:
- 枚举的字段值可以是字符串或数字
- 数字枚举的值会自动递增 (0,1,2...)
- 被数字枚举约束的变量,可以直接赋值为数字
- 数字枚举的编译结果 和 字符串枚举有差异
{ '0': 'leavel1', '1': 'leavel2', '2': 'leavel3', leavel1: 0, leavel2: 1, leavel3: 2 }
最佳实践:
- 尽量不要在一个枚举中既出现字符串字段,又出现数字字段
- 使用枚举时,尽量使用枚举字段的名称,而不使用真实的值
扩展知识: 位枚举 (枚举的位运算)
针对的数字枚举 位运算:两个数字换算成二进制后进行的运算
模块化
本节课相关配置:
| 配置项 | 说明 |
|---|---|
| module | 设置编译结果中使用的模块化标准 |
| moduleResolution | 设置解析模式的模式 |
| noImplicitUseStrict | 编译结果中不包含 "use strict" |
| removeComments | 编译结果移除注释 |
| noEmitOnError | 错误时不生成编译结果 |
| esMoudleInterop | 启用es模块化交互非es模块导出 |
前端领域中的模块化标准: ES6, commonjs, amd, umd, system, esnext
TS中如何书写模块化语句 编译结果??
TS中,导入和导出模块,统一使用ES6的模块化标准
编译结果中的模块化
可配置
TS中的模块化在编译结果中:
- 如果编译结果的模块化标准是ES6,没用区别
- 如果编译结果的模块化标准是commonjs:导出的声明会变成exports的属性, 默认的导出会变成exports的default属性
const cessss = 123; export default cessss; => const cessss = 123; exports.default = cessss;
如何在TS中书写commonjs模块化代码
导出:export = xxx
导入:import xxx = require('xxx')
模块解析
模块解析:应该从什么位置寻找模块
TS中,有两种模块解析策略
- classic (默认)经典
- node: node解析策略 (唯一的变化,是将js替换为ts)
- 相对路径
require("./xxx") - 非相对路径
require("xxx")
- 相对路径
接口和类型兼容性
拓展类型-枚举
拓展类型:枚举,类型别名,接口,类
TS的接口:用于约束类,对象,函数的契约 (标准)
契约(标准)的形式
- API文档,弱标准
- 代码约束,强标准
-
接口约束对象
-
接口约束函数
接口也可以继承
class Person extends React.Component{
}
可以通过接口之间的继承,实现多种接口的组合
使用类型别名可以实现类似的组合效果,需要通过&, 它叫做交叉类型
他们的区别:
- 子接口不能覆盖父接口的成员
- 交叉类型会把相同的成员的类型进行交叉
type Person = { name: string } & { name: number }; 得到的结果是 { name: string & number } 这样会出问题
readonly
- 只读修饰符,修饰的目标是只读
- 只读修饰符不在编译结果中
interface A { readonly id: number; name: string; age: number; readonly arr: readonly number[]; } readonly 在成员上用 不能重新赋值,但是能用push等方法修改数组 放在类型上用 可以重新赋值,但是不能用push等方法修改数组
类型兼容性
B->A,如果能完成赋值,则B和A类型兼容
鸭子辨型法(子结构辨型法):目标类型需要某一些特征,赋值的类型只要能满足该特征即可
- 基本类型:完全匹配
- 对象类型:鸭子辨型法
类型断言
当直接使用对象字面量赋值的时候,会进行更加严格的判断
- 函数类型
一切无比自然
参数:传递给目标函数的参数可以少,但不可以多
返回值:要求返回必须返回;不要求返回,你随意;
TS中的类
面向对象思想
基础部分,学习类的时候,仅讨论新增的语法部分。
属性
使用属性列表来描述类中的属性
属性的初始化检查
strictPropertyInitialization:true
属性的初始化位置:
- 构造函数中
- 属性默认值
属性可以修饰为可选的
属性可以修饰为只读的
使用访问修饰符
访问修饰符可以控制类中的某个成员的访问权限
- public:默认的访问修饰符,公开的,所有的代码均可访问
- private:私有的,只有在类中可以访问 (Symbol js实现的私有属性,但是不完全是私有属性)
- protected:暂时不讲
属性简写
如果某个属性,通过构造函数的参数传递,并且不做任何处理的赋值给该属性。可以进行简写
访问器
作用:用于控制属性的读取和赋值
set name(value: string) { this._name = value; } get name() { return this._name; }
泛型
有时,书写某个函数时,会丢失一些类型信息(多个位置的类型应该有关联的信息)
泛型:是指附属于函数,类,接口,类型别名之上的类型
泛型相当于是一个类型变量,在定义时,无法预先知道具体的类型,可以用该变量来代替,只有到调用时,才能确定它的类型
很多时候,TS会智能的根据传递的参数,推导出泛型的具体类型
如果无法完成推导,并且又没有传递具体的类型,默认为空对象
在函数中使用泛型
在函数名之后写上<泛型名称>
如何在类型别名、接口、类中使用泛型
直接在名称后写上<泛型名称>
泛型约束
泛型约束,用于现实泛型的取值