【青训营】-TypeScript入门

545 阅读10分钟

01.TypeScript基础

1.1 开发环境

官网下载

Node.js (nodejs.org)

image-20210820223844346

创建项目

初始化第一个TypeScript项目 新增一个"first-ts-project"文件夹 image.png

进入"first-ts-project"文件夹以后执行,就能在文件夹下自动出现的一个package.json文件

image.png 然后接着执行以下命令安装typescript最新稳定版

image.png

创建tsconfig.json

方法1:利用VScode编辑器

  • 先创建一个index.ts文件,用VScode打开

  • 点击VScode底部TypeScript的版本号

  • 在弹出来的弹出框点击【创建tsconfg】即可

image.png

会出现提示创建tsconfig.js的选项

image.png 点击创建,便自动创建好了

image.png

创建完tsconfig.json后,想要在vscode中添加参数,只要打上""就能出现提示,鼠标放在参数上还会有提示

image.png

TS Playground

方法2:利用命令行创建 image.png 通过这种方法创建tsconfig.json包含所有编译器参数以及参数说明 image.png

1.2预备知识

TypeScript与JavaScript的关系

TypeScript是JavaScript的超集

TypeScript提供所有JavaScript特性。并在其上层增加了TypeScript的类型系统

这个类型系统被设计为可选的。这意味着所有合法的JavaScript代码都是合法的TypeScript代码。

image-20210821070342642

TS的编译过程

我们用C++的编译过程与TS的编译过程进行对比

image-20210821070616185

从图中可以发现无论TS文件有没有错误,都会生成js文件。如果TS文件有错误,js文件在运行时也会报错。

类型系统

结构类型系统

结构类型系统(Structural Type System):通过类型的实际结构确定两个类型是否相等或者兼容(TypeScript,Haskell,Ocaml,Go)

image-20210821071910678

可以看这个例子:创建了一个Foo类型的a,直接赋值给Bar类型的b;

这样是没有问题的,因为他们类中有相同的结构

名义类型系统

名义类型系统(Nominal Type System):通过类型的名称确定两个类型是否相等(C,C++,Java,C#,Rust...)

image-20210821072102017

同样的例子在名义类型系统中就不行,因为他们的类型名不一样。

类型注解

image-20210821073351069

类型与集合

每一个类型对应的集合

空集 never = {}

单元素集合 Null = {null} Undefined = {undefined} Literal Type(字面量类型,"a",1,true)

有限集合 Boolean = { true, false }

无限集合 String = { 'a', 'b',... } Symbol = { Symbol(...),...} BigInt = {..., -1n, 0n, 1n, ...} Number = {-lnfinity,-(2^53 - 1),...,0,,,+2^53 -1,Infinity}和NaN

全集

类型交叉与合并

image-20210821084120184

类型别名

在JS中,可以用letconstvar来声明变量

在TS中,可以使用type为类型声明别名

let ID = 9527;
const PI = 3.14;
var myPI = PI;
// 在ts中 类型名 与 变量名 不冲突,用到类型的时候代表的是类型,用到变量的时候代表的是变量
type ID = string;

type User = {
    id:ID;
    name:string;
}
复制代码

但是typelet一样有作用域

image-20210821082508010

在同一域中声明会报错

但是在不同域中声明就不会报错

image-20210821082616837

类型的拓宽于收窄

类型拓宽(Type Widening)

当你把用字面量赋值给let、var变量时,TS不用字面量类型作为该变量的类型。而是从字面量类型拓宽到相应的更宽泛的类型。

类型收窄(Type Narrowing)

在某些情况写,TS可以更加确定变量的类型,此时它会将变量类型收窄。

TS试图在类型确定性与灵活性之间取得平衡

TS提供一系列方法来帮助收窄类型,以提高类型的确定性:

null check, as const, instanceof, typeof, 属性检查, Tagged Union , 用户类型守卫, 代码流分析

当使用let var声明变量时,TS认为变量会在未来发生变化,所以将类型推断为相对宽泛的类型

当使用const声明变量时,TS知道常量是不会发生变化的,会将类型推断最窄的字面量类型

image-20210821084024959

值空间与类型空间

类型空间是由TS的编译器tsc创建的,里面存在各种类型。tsc会根据代码实例化各种类型。

值空间是后JSEngine 创建的 里面存在各种运行时的值。

image-20210821085441969

TS的语法结构会给两个空间引入值。

type和interface会为类型空间引入相关的类型

const let var 会为值空间引入相关的变量或者常量

enum class namespace 会为类型空间引入类型,为值空间引入值

如果namespace只包含类型声明,不会产生js代码,不会引入变量

如何判断符号在哪个空间?

  1. 转译后消失的符号 -> 类型空间

  2. 作为类型注解、别名的符号 -> 类型空间

    type T= typeof Person; const p:Person

  3. 类型断言后的符号 -> 类型空间

    target as/is HTMLElement

  4. const let var 后面的符号 ->值空间

  5. class enum namespace 后面的符号 -> 值空间 + 类型空间

还有一些操作符在两个空间都存在,但是表达的含义完全不同。

typeof

typeof 后面的表达式只能是变量,不能是类型

在值空间,typeof返回后面表达式对应的JavaScript类型的字符串表示:

('string','number','bigint','boolean','symbol','undefined','object','function')

在类型空间,typeof返回标识符对应的TypeScript类型

image-20210821094631326

[]索引访问操作符

在值空间,val[field]或val.field返回val对应属性的值

在类型空间,Type[T]返回对应T的TS类型

image-20210821095146286

this关键字

在值空间,this指向比较复杂

在类型空间,this可以作为类方法的返回值来实现链式调用

& | 运算符

在值空间表示”按位与“和 ”按位或“

在类型空间表示类型的交叉和联合

const

在值空间用来声明常量

在类型空间与as连用,即”as const“ 常量断言,收窄类型

看下面这个例子

不加as const 被推断成number类型

image-20210822132814513

下面是加as const的结果

image-20210822132732205

externds

在值空间中用于定义子类

class A externds B{}
复制代码

在类型空间中用来进行类型约束或接口继承

T externds number
interface A externds B
复制代码

in

在值空间中用于for循环 和 判断属性是否存在

在类型空间中用于映射类型的key的声明

[K in T]
复制代码

1.3 基础知识

TS中的类型层次

image-20210821195002119

下层类型的值可以赋值给上层类型的变量或常量

unknown类型的变/常量可以指向任何类型的值

不存在never类型的变量 (never是空集)

不同语言的Top Type与Bottom Type

image-20210821195552179

类型讲解

any

1.any比较特殊,其实它既是top type又是bottom type

也就是说,any类型的变量/常量 可以与 任何类型的变量或者常量进行相互赋值

但any类型是不安全的,无语言服务的,所以应该尽量避免使用

2.any具有传染性,他会使它所触及到的地方变得不安全。

所以TS在3.0引入了类型安全的unknown作为Top Type

3.any会隐藏bug

4.any会隐藏代码设计细节:丢失了对数据类型的设计

所以,我们要尽量避免使用any

在tsconfig中开启严格模式

image-20210821200528268

unknown

unknown一定要写注解,TS不会把任何值推导为unknown

unknown类型只能进行等于和不等于操作

只有类型收窄后,才能进行相应的运算或函数调用

如果无法预知类型,不要用any,用unknown,收窄类型后再使用

image-20210821201015872

boolean

布尔类型只有两个元素:true/false

let,var变量会被拓宽成boolean类型

const常量就是对应着字面量类型

有一点值得注意的是,true和false的联合类型,会被反推回boolean类型

image-20210821201508907

number

number包括整数,浮点数,+-Infinity(正负无穷),NaN(not a number)

image-20210821201813251

bigint

number范围[-(2^53-1),2^53-1]

想要使用bigint需要将编译选项设置的大于es2020

bigint是新引入的类型,可以表示任意大小的整数

bigint字面量是在数字后面加小写的n

bigint支持加,减,乘,除,求余,幂

但是bigint 不能与number混合运算,需要显式转换

image-20210821202211177

string

image-20210821202638456

symbol

let、var声明的变量推导成symbol类型

unique symbol必须是const类型

const常量推导成unique symbol

也可以显式注解成unique symbol

unique symbol不是 一个类型而是,一组类型。比如unique symbol b1和unique symbol b1x是两个类型

Symbol的第一个参数是描述,不是符号名,也不是ID。

image-20210821203242227

image-20210821210302293

image-20210821210431959

Symbol.for在内部维护了一个字典,如果之前没有创建过,对应的描述,就返回一个新的符号,如果创建过,就返回已创建的符号。

object

使用keyof 可以获取对象类型上的key

使用Object.key可以获取对象上的key

image-20210822065523526

array

一般array创建时,如果不指定类型,TS会自动判断类型

数组有两种注解方式

  1. T[]
  2. Array接口泛型

image-20210822070028262

tuple

元组是数组的子类型。元组就是各个索引位上的元素类型是确定的数组。

因为元组的创建方法和数组是一样的,所以元组必须是显式注解。

image-20210822070435759

enum

枚举本身是一种映射

未显式赋值的枚举会自动从0开始自增赋值

枚举为整数存在双向映射

显式赋值字符串的枚举不存在反向映射

image-20210822071512575

枚举的合并

枚举可以拆分成多个字段,还可以与同名的namespace合并

image-20210822071703549

常量枚举

常量枚举不会在值空间创建变量

所以引用常量枚举的地方都被替换成对应的值

但是可以通过编译器选项preserveConstEnum来控制

image-20210822092632164

null,undefined,void,never

JS中 Undefined = {undefiend},应该表示尚未定义,但是实际上表示已声明,未赋值

Null = {null} 表示已声明,值为null或值为空

TS中void类型:函数没有显式返回值

never类型,函数无法返回

在js中,void是一个一元操作符:

它执行后面的表达式,

然后无条件返回undefined

在ES1.3之前,undefined是无法直接访问的!

只能通过void(0)的这种形式得到,ES1.3将undefined添加到global object之后才可以访问到。

image-20210822093349387

02.TypeScript进阶

类型操作

keyof

keyof(索引类型查询操作符)是在TS2.1中引入的。

它获取类型上所有已知、public的键对应的类型联合

索引可以是number,string,symbol,但是symbol还没有支持,将在TS4.4之后支持

image-20210822093837314

image-20210822094033077

也就是说 keyof 这个函数 只能查询 接口的key 或者类似于 class 的key

对于普通的 string,number这种类型没有必要使用keyof

在接口中存在键和值

键存在类型,可以是string,number,symbol(symbol将在ts4.4之后支持)

值存在类型,可以是所有类型

接口

接口类型别名可以认为是同一概念的两种语法

image-20210822094209088

接口与类型别名的区别

  1. 类型别名更为通用,其右侧可以包含类型表达式(类型联合、类型交叉、条件类型),但接口右侧只能是某种结构({...})
  2. 接口间继承(extends)时TS会检查二者关系。但类型联合时TS会尽最大的努力尝试,不会报错
  3. 同一作用域中多个同名接口声明会被合并,但多个类型别名会报错

image-20210822095154151

类class

class是在es2015中引入的新特性

这里只讲如何使用interface描述一个class

其实使用interface来描述class 就是

  1. 描述类中的属性的类型

  2. 描述类中的函数

    • 形参类型
    • 返回值类型

image-20210822100355757

03.TypeScript高级

类型操作

映射类型

映射类型是一种创建对象类型的方法

image-20210822110112971

image-20210822110141284

image-20210822110210320

image-20210822110235034

模板字面量类型

条件类型

image-20210822115459910

image-20210822115518100

常用工具类型

image-20210822115549218