【青训营】-TypeScript入门

369 阅读12分钟

TypeScript基础

代码仓库

github.dev/aztack/Intr…

tinyurl.com/TypeScript2…

1.1开发环境

1.安装vscode

添加vscode的路径到环境变量

这样我们就可以在命令行中使用code命令

image-20210820223456591

2.安装nodejs

方法一:使用nvm

nvm(Node Version Manager)github.com/nvm-sh/nvm

curl -o- raw.githubusercontent.com/nvm-sh/nvm/… | bash

image-20210820223735400

方法二:官网下载

Node.js (nodejs.org)

image-20210820223844346

3.创建项目

初始化一个TypeScript项目

image-20210820224451908

4.创建tsconfig.json

方法一:

先创建一个TS文件,然后双击打开

打开后,在右下角找到版本号

点击版本号

image-20210820224911977

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

image-20210820225130776

我们点击创建

便自动创建好了

image-20210820225215452

方法二:

利用tsc命令创建

image-20210820225443159

通过这种方式创建tsconfig.json会包含所有的编译参数以及参数说明

image-20210820225553549

创建完tsconfig.json后

想要在vscode中添加参数,只要打上 "" 就能出现提示

image-20210820225935389

鼠标放在参数上还会有提示

image-20210820230028843

5.选择TS版本

点击右下角版本号

image-20210820230153772

image-20210820230225699

6.工具

TS Playground

TS playground是官方提供的TS代码测试工具

TypeScript: TS Playground - An online editor for exploring TypeScript and JavaScript (TypeScriptlang.org)

image-20210820230444117

Equal<X,Y>

type-challenges/index.d.TS at master · type-challenges/type-challenges (github.com)

image-20210820230755708

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]

基础知识

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<T>接口泛型

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

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

TypeScript高级

类型操作

映射类型

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

image-20210822110112971

image-20210822110141284

image-20210822110210320

image-20210822110235034

模板字面量类型

条件类型

image-20210822115459910

image-20210822115518100

常用工具类型

image-20210822115549218

资源推荐

书籍推荐

《TypeScript编程》 特点:全面

《深入理解TypeScript》 特点:深入

《高效TypeScript》 特点:指导性,原则性

《TypeScript类型编程》 特点着重ts类型编程

链接

1.TypeScript官网文档:通读;重点Handbook,关注What's New

www.typescriptlang.org/docs/

2.《深入理解TypeScript》在线版: 通读

github.com/jkchao/type…

3.掘金:文章、专栏、小册子

juejin.cn/search?quer…

4.知乎TypeScript专栏:

www.zhihu.com/topic/19766…

5.StackOverflow: 不懂就问(提问时附上Playground链接)

stackoverflow.com/questions/t…

6.TypeScript源码、Issue、PR:

github.com/microsoft/T…

总结1:

  1. 这次学习TypeScript有些仓促,有很多知识,定义,只是听一个大概,都没有好好的实践过。不过由于做的有笔记,能够好好的对照笔记在理解理解。
  2. 这次学习TypeScript主要是学习是TypeScript与JavaScript的一些区别,因为TypeScript中有类型的概念,所以主要在讲各种类型的特点,以及使用方法
  3. 我个人理解TypeScript其实就相当于js生成器,但是js中我们操作任何东西都是没有指定类型的,所以操作起来不会报错,不会提示,写完代码需要调试的地方很多,但是TypeScript中,我们预先会定义好操作的类型,定义好数据接口的类型,这样在我们写代码的时候就直接给错误提示。

总结2:

老师总结

用ts在写什么?

我们通过ts类型系统提供的这个语言,精确的描述我们需要的类型。ts汇报我们的就是编译期就将问题提前暴露出来,代码更健壮,更易拓展

image-20210822120012128

日记:

8月22日上午

经过了昨天一天,加今天以上午,稀里糊涂把知识点整理完了,但是很多不明白的地方

下午我打算把笔记中的代码都自己写一遍看看效果

8月22日下午

下午又讲代码都看了一遍,有些不懂的在playground中手敲了一遍。

可以说是基本上能理解代码的意思,但是,如果自己写的话,可能还欠火候,尤其是刚形成一些类型的深刻印象。

我现在能够理解的是,在类型空间中把类型像变量/值一样理解,能够进行条件判断,映射,赋值等等。

之前写C++/C的代码也不少,对类型也是有写概念的,我觉得ts存在的意义就是减少因为类型而导致的错误,在有接口的情况下,能够编写实际的接口类型,然后再去编写代码。这样可以使代码的可读性更高,扩展性更强。