TypeScript
作用:JS可以进行全栈开发,应用场景丰富,代码量大,但由于出生简陋(当初诞生时的定位就是一个简单的脚本语言,用于网页内的简答逻辑),导致现在有很多问题
一、为何需要TypeScript
主要的困扰( 将index.js改成index.ts直接报错 )
1.数据类型不清楚
2.相同逻辑用了else if,js不会报错
3.访问不存在的属性,js不会标红报错
4.方法名写错,js不会标红报错,但在控制台会报错(大写应该是toUpperCase)

静态类型检查(运行前检查)
TS在代码** 运行前 **进行检查,发现错误就报错,减少运行时异常几率 ** (JS只会在运行时报错) **

二、编译TypeScript
命令行编译(了解)
tsc的意思是typescript compiler

自动化编译
创建ts配置文件

这里target-es2016指的是es7,版本号为年份+1,那么es6为es2015

使用Vue、React这些官方提供的webpack/vite脚手架时,会自动将ts转换为js,不用管
三、类型声明
官方推荐类型为小写的,string、number等

函数的参数和返回值都可以设置类型限制
四、类型推断
如果没有规定类型,则TS会自动推导,但可能出问题,尽量定义类型

五、类型总览


知识点:
string(小写)为原始类型,String(大写)为包装对象---很少用

原始类型string不能使用构造函数,而包装对象String可以赋值字符串也可以使用构造函数

自动装箱解释
str没有length属性,但是可以执行,结果为5
因为此时str是字符串类型,JS会将str自动装箱为String类型,它有length属性,所以可以输出(也就表明包装对象更占内存空间)

六、常用类型与语法
-
any
any(很少用) :任意类型,即存什么都可以,不做类型检查
let a: any与let a等价,不设置类型就是任意类型** any类型的数据可以给任意类型的变量赋值 **

-
unknown
unknown:未知类型,不知道具体存什么类型
与any很类似,可以存任何类型,区别是** 不能赋值给非unknown类型的变量,更安全 **
如何强行赋值?
1.加类型判断
2.使用断言
x = a as string
x = <string>a

需要用断言!!!
-
never
never(很少用):什么值都没有,用于限制函数的返回值(用于抛出异常)
不要将never用在变量上;never用在函数上不能有返回值,** 只能用于抛出异常函数 **,函数会停止运行(即没有任何返回值,包括undefined)
TS也会自动推断出下面第一段程序中else中的a只有为never类型


-
void
void:空,函数不返回任何值,调用者不应依赖其返回值进行任何操作
** 如果没有return值,无显式返回值,但有隐式返回值undefined,return undefined就是void **
以下三种都可以
void与undefined区别
void类型函数可以返回undefined,void类型没有返回值,不能用其返回值进行后续判断,而undefined可以进行后续操作

-
object与Object
object(很少用):限制太大,含义为所有非原始类型,比如对象、函数、数组等

Object(很少用):限制太大,所有可以调用Object方法的类型(即能调用.toString()方法的类型都可以,数值类型、字符类型、boolean类型)
简单理解:除了undefined和null的任何值

开发中声明对象类型
1.可选属性
let person1: { name: string, age?: number }
这里的 ?表示可选属性,声明时可有可无

2.索引签名
不确定对象属性的数量时,可以用
[key: string]: any 表示可存在任意数量任意类型的其他属性
3.对象的简写形式
对象中的a就等于1,b就等于2

开发中声明函数类型(还有其他形式,后续补充)
let count: (a: number, b: number) => number
参数a:number类型,b:number类型
函数返回值:number类型

开发中声明数组类型

-
tuple
tuple:元组(不能写let a : tuple)
元组是一种特殊的数组类型,但不能声明tuple类型

-
enum
enum(很重要):枚举
开发中可能会遇到下面问题,有很多判断
数字枚举
下标默认从0开始,这里Direction相当于一个类型

也可以自定义下标起始值

使用数字枚举完成之前walk函数逻辑
不用关心Up是几,看名字即可

字符串枚举(用的少,api里面会用)

常量枚举
使用const定义枚举,在TS编译时会将成员引用替换为实际值,例如下图中的x=0,不生成额外的枚举对象,可以减少JS代码量提高运行时性能
使用普通枚举编译后JS代码

使用常量枚举编译后JS代码

-
type类型
-
基本用法:可以为任意类型创建别名
-
联合类型(或|):多种类型的集合,可用字面量

-
交叉类型(并&)
type House = Area & Address
这里的House类型需要包含Area和Address类型的所有属性

-
-
一个特殊情况
正常情况

特殊情况
分两步走,先声明类型限制返回值为void,再给函数限制类型(** 这时TS不会严格要求函数返回值是否为空 **)

防止以下问题
当箭头函数只有一句时,这里的push语句返回值是数组的长度,即number类型,而forEach方法接受的回调函数返回值为void类型,为了能让箭头函数正常执行官方才这么做的



-
复习类相关知识
类的声明和Java一样

继承extends
- 如果子类属性和父类一样,子类连构造器都不用写了
- 属性不一样时,用super(name,age)表示需要父亲的相同属性,这里用super表示调用父类的构造器,要写在构造器的第一行
- override表示重写从父类继承的方法

-
属性修饰符
当没有给类中属性和方法添加修饰符时,默认为public

public修饰符
类的内部、子类、类外部访问

属性简写形式
相当于不写属性,直接在构造器里面的参数填属性并加上public

protected修饰符
类的内部和子类可以访问

private修饰符
只能在类的内部访问

readonly修饰符
只读属性,在属性前面加

-
抽象类
简单理解:抽象类无法被实例化,它可以写抽象方法,也可以写具体方法,抽象方法不能有具体实现,要由它的儿子来实现(不能有函数体,即花括号)
例题:

这里的标准包裹类有自己的属性---单位价格,需要实现抽象方法,会用到这个单位价格
创建实例时要用子类创建,因为抽象类无法被实例化

何时使用抽象类
需要所有子类都实现关键行为

-
interface(接口)
接口定义一种结构,确保代码的一致性和安全;接口只能定义格式,不能有任何实现 ** (抽象类可以有具体实现) **
接口需要实现implements
类实现接口

对象实现接口(这里接口相当于一个类型)

函数实现接口

接口之间的继承(相当于把父类的条件加进来了)

接口的合并性(可以重复定义)

何时使用接口?

interface和type的共同点和区别
相同点:都可以定义对象的结构
不同点:interface支持继承和合并;而type不支持,它可以定义类型别名、联合类型(|)、交叉类型(&)

interface和抽象类的共同点和区别
相同点:都可以定义一个类的格式
不同点:
接口只描述结构,不能有任何实现,一个类可以实现多个接口
抽象类可以包含抽象方法,也可以包含具体方法,一个类只能继承一个抽象类

七、泛型-可以代表任意类型
第一个练习中三个T都是一种类型,当要传入数值类型时T就是number
第二个练习中函数的返回值是 T | U,即T或者U

泛型接口

泛型类

八、类型声明文件
.d.ts文件作用是让ts在使用js库时进行类型检查和提示

九、装饰器
装饰器一共有5种
- 类装饰器
- 属性装饰器
- 方法装饰器
- 访问器装饰器
- 参数装饰器

建议使用experimentalDecorators配置来开启对装饰器的支持

类装饰器(就是一个函数,可以将它以注解的形式写到类上)
基本语法
类似Java中的注解
应用举例
对象类型调用.toString方法返回的时[object Object],现在需要打印p的内容

下图的Object.seal(target.prototype)可以封闭它的原型对象,使其无法修改
调用Person.prototype.x = 100,修改其属性x的值会报错,如果没有上面的封闭操作,则可以修改

关于返回值

类装饰器只能返回类或者不返回
有返回值时:返回的类会替换被装饰的类(需要对象内容一致,方法可以不一样),即加了@Demo的类
无返回值时:则被装饰的类不会被替换

定义构造类型(约定构造函数)
这里定义Constructor类型,用于接收类;首先类可以实例化,且发的返回类型是对象,箭头函数不可以实例化

定义构造类型且指定它的静态参数(这里new后面的大括号前是冒号)
