TS从0到1入门到大神

770 阅读10分钟
--watch src 代表只监听 src 目录下的文件变化,然后执行下面的命令

-e ts 代表监听 ts 文件的变化,然后执行下面的命令
--exec 代表执行下面的命令
nodemom --watch src -e ts --exec 'node ./dist/index.js'

不用担心,这个错误,并不是我们的错误,原因是默认情况下,TS 会做出下面的几种假设:

  1. 假设当前环境是 dom 环境
  2. 如果代码没有模块化操作 import 或者 export 那么默认是全局执行
  3. 编译的目标代码是 es3 可以加上 TS 的配置文件,或者tsc命令行的选项参
  4. 配置文件 使用配置文件后,在使用 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文档,弱标准
  • 代码约束,强标准
  1. 接口约束对象

  2. 接口约束函数

接口也可以继承

  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

属性的初始化位置:

  1. 构造函数中
  2. 属性默认值

属性可以修饰为可选的

属性可以修饰为只读的

使用访问修饰符

访问修饰符可以控制类中的某个成员的访问权限

  • public:默认的访问修饰符,公开的,所有的代码均可访问
  • private:私有的,只有在类中可以访问 (Symbol js实现的私有属性,但是不完全是私有属性)
  • protected:暂时不讲

属性简写

如果某个属性,通过构造函数的参数传递,并且不做任何处理的赋值给该属性。可以进行简写

访问器

作用:用于控制属性的读取和赋值

set name(value: string) { this._name = value; } get name() { return this._name; }

泛型

有时,书写某个函数时,会丢失一些类型信息(多个位置的类型应该有关联的信息)

泛型:是指附属于函数,类,接口,类型别名之上的类型

泛型相当于是一个类型变量,在定义时,无法预先知道具体的类型,可以用该变量来代替,只有到调用时,才能确定它的类型

很多时候,TS会智能的根据传递的参数,推导出泛型的具体类型

如果无法完成推导,并且又没有传递具体的类型,默认为空对象

在函数中使用泛型

在函数名之后写上<泛型名称>

如何在类型别名、接口、类中使用泛型

直接在名称后写上<泛型名称>

泛型约束

泛型约束,用于现实泛型的取值

多泛型