笔记-14-TypeScript
解决JS没有类型检测的痛点(根据V8优化代码的原理,其实也可以提升一些性能)
TypeScript的基础类型:数字、布尔、字符串、数组、元组、联合、枚举、any、unknow、void、undefined、Never
高级类型:union组合类型、Nullable可空类型、Literal预定义类型
//一般的写法 添加类型注解
function add(n1:number, n2:number) {}
//数组的写法
let list:number[] = [1,2,3]
let list2:Array<number> = [1,2,3]
//泛型就是指在类定义时不会设置类中的属性或方法参数的具体类型,而是在类使用时(创建对象)再进行类型的定义。
//会在编译期检查类型是否错误。
//tuple元组,是固定长度、固定类型、顺序固定的数组。目前有bug,可以通过push的方式加进去,声明时一定指定类型
// 存放不同类型的数据
let list6:[number,string] = [1,'alwx']
//联合类型Union和文献(字面量)类型Literal,联合类型就是指一个变量支持多种类型。使用时也需要缩小类型,从而推断出更具体的类型
let union:string|number
//字面量类型
let union2:0|1|2 //如果给他赋值为3就会报错,只能是0、1、2
//枚举类型Enum
enum Color {
red = 5,
green = 10
} //typescript提供基于数字和基于字符串的枚举。
//ts中的枚举类型和普通的js对象本质上没有区别,只是对于开发者来说,相较于直接使用值类型,枚举类型更易读
//any和unknow(未知类型)
//当不知道用什么类型时可以用any,它支持任何类型。
//unknown和any很类似,不同的是unknown要求要经过类型确认才能使用,否则报错。
//使用any的优势是开发快,但是会有安全隐患,unknown会更安全一些,但是要写更多代码。
在声明标识符时,如果有直接赋值,那么ts会根据赋值的类型推导出标识符的类型注解,这个过程被称为“类型推导”。
let 进行类型推导,推导出来通用类型(string、number等等)
const 进行推导,推导出来字面量类型
let name = "zhangsan" // 此时name就是string类型
注意:真实开发中,数组内一般存放相同的类型,不要存放不同类型
在定义函数时,通常参数时需要明确指定类型的,函数结果会自动推导。
function sum(num1: number, num2: number): number {
return num1 + num2
}
定义对象的类型可以使用type定义
匿名函数最好不要加类型注解,比如给forEach传入的匿名函数。它会根据上下文自动推导。
某些情况下,无法确定一个变量的类型,并且可能它会发生一些变化,这时可以用any类型(比如服务器返回的复杂数据)
函数在没有返回值的时候就是void类型:void意思是这个东西不存在
undefined意思就是这个东西存在但没赋值
never是永远执行不完的函数
类型别名
例如用了很多联合类型后,会导致一行代码过长。此时可以给类型起别名。
type 别名 = 类型...
interface 接口也可以用来声明对象的类型,一般建议对象使用interface,基本类型数据使用type
这两个有些区别:
interface只能用来声明对象,不能用于基本类型
声明对象时,interface可以声明多次,多次声明会合并(type 不允许两个同名别名同时存在)
interface支持继承(extends)
interface可被类实现 (implements)
交叉类型:通常使用在对象中,让一个对象实现两个类型的要求
interface IPerson {
name:string
age:number
}
interface IStudent {
name:string
score:number // ?: 可选
}
const info: IPerson & IStudent = {
name:"zhangsan",
age:18,
score:90
}
类型断言:as
当ts不能自动推导出准确类型的时候可以手动指定
非空类型断言:!.
例如接口的某个属性是可选时,那在使用这个属性时就可能是空,要用可选链?.
来使用。在给它赋值时,如果确定它是非空的,可以用非空类型断言。
函数类型表达式
type FunType = (num:number) => number
const foo:FunType = (num):number => { return num+1 }
注:对于传入的参数个数不进行检测
函数的调用签名:从对象的角度看待这个函数,也可有其他属性
interface IBar {
name:string
age:number
(num:number):number // 函数的调用签名(参数列表): 返回值类型
}
构造签名
用于描述构造函数
class Person{}
interface IPerson {
new (): Person
}
function factory(fn:IPerson){
new fn()
}
函数的重载
先编写重载签名
再编写通用的函数实现
function add(arg1:number, arg2:number):number
function add(arg1:string, arg2:string):string
function add(arg1:any, arg2:any):any{
return arg1+arg2
}
TS面向对象
class Person {
name:string // 必须声明成员属性,否则找不到
age:number
constructor(name:string, age:number) {
this.name = name
this.age = age
}
}
const p = new Person("zhangsan", 18)
console.log(p.name) // 如果没有声明成员属性会找不到
类的属性和方法支持三种修饰符:public、private、protected
readonly修饰符可以让属性只读
getters/setters
一些私有属性不能直接访问,或者想要监听某属性的获取和设置的过程,这时使用存取器。
可以规避一些不合理的赋值
class Person {
private _name:string // 私有属性一般前面_开头,也是为了防止和存取器同名
constructor(name:string) {
this._name = name
}
set name(newV:string) {
this._name = newV
}
get name(){
return this._name
}
}
const p = new Person("zhangsan")
p.name = "abc"
参数属性
可以把一个构造函数的参数转换成一个同名同值的类属性
直接在构造函数参数前面加上可见性修饰符(public/private/protected或者readonly)就可以。(语法糖)
抽象类abstract、抽象方法
继承是多态的使用前提
所以在定义很多通用的调用接口时,通常会让调用者传入父类,通过多态来实现更灵活的调用方式
但是,父类本身可能并不需要对某些方法进行具体实现,所以父类中定义的方法可以定义为抽象方法
abstract class Shape {
abstract getArea() // 抽象方法必须在抽象类中,抽象类不能被实例化
}
class Rect extends Shape {
constructor(public width:number, public height:number){
super()
}
// 子类必须实现父类的抽象方法
getArea() {
return this.width * this.height
}
}
class Circle extends Shape {
constructor(public radius:number){
super()
}
// 子类必须实现父类的抽象方法
getArea() {
return this.radius ** 2 * Math.PI
}
}
// 多个子类有相似的方法,可以在父类中定义抽象方法,在通用函数中就可以通过父类调用不同的子类的方法
function calcArea(shape:Shape){
return shape.getArea()
}
calcArea(new Rect(10,20))
calcArea(new Circle(5))
对象类型的修饰符
type PersonType = {
name?: string // 可选属性
readonly age: number // 只读属性
}
索引签名
有时候不知道一个类型里所有属性的名字,但知道它们的特征,这时通过索引签名来描述可能的值的类型
interface IPerson {
[index:number]:string // 需要使用数字索引获取数据,只能是number或string
}
接口可以被多层实现,而抽象类只能单一继承
抽象类中可以有实现体,接口中只能有函数的声明
类可以实现多个接口
泛型
函数在定义时不固定类型,被使用时再传入类型
function foo<T>(arg: T){}
// 在使用时再确定参数类型
foo<number>(123) //也可省略,因为它会自动类型推导
泛型接口
interface IPerson<T = string>{ // 可设置默认值
name:T
}
const p: IPerson<string> = {
name:"zhangsan"
}
泛型类
class Point<T = string>{
x:T
y:T
}
泛型约束
<T extends xxx>
既有泛型的灵活性也不丢失类型的约束
keyof 拿到对象的key作为联合类型
typeof 操作符,type XXX = typeof abc 提取abc的类型给XXX
映射类型
一个类型需要基于另外一个类型,但又不想拷贝
type xxx = {
}
TS中使用的模块化方案就是ES Module
如果ts文件中无export或者import,它就是一个非模块。如果希望它被作为模块处理可在文件最后添加export {}
.d.ts
文件用来做类型的声明(declare),称之为类型声明(Type Declaration)或者类型定义(Type Definition)文件。仅用来做类型检测,告知ts我们有哪些类型
内置类型声明时ts自带的,内置了js运行时的一些标准化API声明文件。比如DOM API、window、Document等lib.[xxx].d.ts
一些第三方的包如果自己不提供类型声明文件,可以通过社区公有库 https://github.com/DefinitelyTyped/DefinitelyTyped 查找,这是存放类型声明文件的仓库,真正查找的话要去npm
tsc --init
创建tsconfig.json文件
import type {xxx} from 'aaa'
这种方式导入类型可以在打包时安全删除,推荐。