«

笔记-14-TypeScript

codeez 发布于 阅读:1249 笔记


解决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' 这种方式导入类型可以在打包时安全删除,推荐。

前端

请先 登录 再评论