Skip to main content

TypeScript笔记

鱼雪

TypeScript 是 JavaScript 的一个超集,它添加了静态类型和其他一些功能,使得代码更易于维护和阅读。以下是 TypeScript 的一些主要特点:

  • 静态类型
  • 接口
  • 泛型
  • 枚举
  • 类型推断
  • 模块
  • 类型别名
  • 类型守卫
  • 声明文件

TypeScript的优势

  1. 编译时静态类型检查
  2. 自动提示更清晰明确
  3. 引入泛型和一系列的TS特有类型
  4. 强大的t.ts声明文件
  5. 轻松编译成JS文件
  6. 灵活性高

类型注解和类型推导

let data: number = 0
// data = '222' // 提示报错

let money = "abc" // 类型系统会推断为string
// money = 1111 // 提示报错

  • 区别:

    • 类型注解:定义时固化类型
    • 类型推导:根据值的类型反过来推导类型
  • 优点

TS编译和编译优化

  • 正常情况下,TS检测变量,会把所有变量都当成全局变量检测

    • 解决方法
      • 在代码结尾添加export {}作用域
        • 表明当前变量作用域是当前文件
  • TS代码默认不能被正常运行

    • 不能使用node直接运行
    • 浏览器也不能直接加载运行
  • 运行方式

|------------|       tsc        |------------|
| Javascript |----------------> | Typescript |
|------------| |------------|

TS的24种类型

  • 基本类型

    • number
    • string
    • boolean
    • symbol
    • null
    • undefined
  • 根类型

    • Object
  • 对象类型

    • Array
    • object
    • function
  • 特殊类型

    • any
    • unknown
    • never
    • void
    • tuple
    • 可变元组
  • 合成类型

    • 联合类型
    • 交叉类型
  • 字面量数据类型


// 联合数据类型
let dat: string | number = 'a'

// 交叉类型,可以合并在一起
type Obj1 = { username: sring}
type Obj2 = { age: number}
let obj1:Obj1 = { username: 'aaa' }
let obj2:Obj2 = { age: 22 }
let obj3: Obj1 & Obj2 = { username: 'ww', age: 3 }

anyunknown的区别和应用场景

  • 相同点

    • anyunknown可以是任何类的父类
      • 所以任何类型的变量都可以赋值给any类型或unknown类型的变量
  • 不同点

    • any也可以是任何类的子类,但unknown不可以
      • 所以any类型的变量都可以赋值给其它类型的变量
    • 不能拿unknown类型的变量来获取任何属性和方法,但any类型的变量可以获取任意名称的属性和任意名称的方法
  • any比较典型的应用场景

    • 自定义守卫
    • 需要进行as any类型断言的场景
  • unknown的应用场景

    • 一般用作函数参数
      • 用来接受任意类型的变量实参
      • 但在函数内部只用于再次传递或输出结果,不获取属性的场景
export function isRef(r: any): r is Ref {
return Boolearn(r && r.__v_isRef === true) // any类型的r参数在函数内部获取属性
}

function ref(value?: unknown) {
return createRef(value) // 函数内部只用于再次传递,不获取属性
}

接口和应用场景

  • 接口

    • 另一种定义对象类型类型
  • 接口的应用场景

    • 一些第三方包或者架构底层源码中有大量的接口类型
    • 提供方法的对象类型的参数时使用
    • 为多个同类别的类提供统一的方法和属性声明
  • 如何定义接口

    • 接口中的类型的方法都只有声明,没有实现
  • 接口继承

    • 新的接口知识在原来的接口继承上增加了一些属性或方法
  • 接口的可索引签名

    • 用来接收不确定字段名
interface Product {
name: string
price: number
account: number
[x: string]: any // string, 支持各种类型,如果是number,则只支持number
}

let p: Product = {
name: "1",
price: 1,
account: 1,
describe: 'ok', // Ok
[Symbol('stockno')]: 33, // Ok
100: 1, // Ok
true: "o" // Ok
}

索引访问类型

  • keyof
    • keyof 类型
      • 获取类型的所有属性名组成的联合类型
type symid = Symbol('productno')
interface Product {
[symid]: string
name: string
price: number
account: number
buy(): string
}

type A = Product['price']
type B = Product['price' | 'name']
type S = Product[typeof symid]

type PKeys = keyof Product // 获取所有属性名组成的联合类型
// "name" | "price" | "account" | "buy" | typeof symid
  • typeof
    • typeof variable
      • 获取变量对应的类型

void 类型

null 和 undefined

  1. 如果希望没变量没赋值还不报错,使用undefined的联合类型

  2. ?会将字段的类型变成字段类型和undefined的联合类型

  3. 哪些类型可以接受undefined

    • any
    • unknown
    • undefined
  4. 那些类型可以接受null

    • any
    • unknown
    • null
typeof null      // {}
typeof undefined // undefined

let str: string | undefined
console.log("str: ", str)

function func(data?: string) {
data?.toString()
data!.toString() // ! 会忽略 undefined

if (data) {
data.toString()
}
}

常见取值错误

let obj = { username: 'wu', age: 22 }
let username = "username" // 出问题
const username = "username"
let u = obj[username]

TS函数与JS函数

TS函数类型解构

type TypStuobj = { username: string, age: number, phone: string }
function info(stuInfo: TypStuobj) {
console.log("name: ", stuInfo.username, "age: ", stuInfo.age)
}
// 只需要username和phone,解构
function info({ username, phone }: TypStuobj) {
console.log("name: ", stuInfo.username, "age: ", stuInfo.age)
}

interfacetype的区别

  • type和interface类似

    • 都用来定义类型
  • 区别

    1. 定义类型的范围不同
    • interface
      • 定义对象类型
      • 接口当名字的函数类型
    • type
      • 可以定义任何类型
        • 基础类型
        • 联合类型
        • 交叉类型
        • 元组
    1. 接口可以extends一个或多个接口或实现一个或多个接口
    • 也可以继承type
    • type类型没有继承功能
    • interface可以继承type,但很少见
    1. type交叉类型&
    • 可让类型中的成员合并成一个新的type类型
    • interface不能交叉合并

元组

  • 满足下面3点的数组就是元组
    • 在定义的时候每个元素的类型都确定
    • 元素值的数据类型必须是当前元素定义的类型
    • 元组值的个数必须和定义时个数相同

数组和数组元素怎样同时为只读

const account = [1, 2, 3] as const // 约束每个元素也是常量

可变元组

// 可变元组
let customers: [string, number, string, ...any[]] = ['1', 1, '1', true]
// 可变元组解构,可以解构任意个数
let [custname, age, ...rest]: [string, number, string, ...any[]] = ['1', 1, '1', true]
// 元组标签
let [custname, age, address, ...rest]: [custname:string, age:number, address:string, ...rest:...any[]] = ['1', 1, '1', true]

类与静态属性

    • 拥有相同属性和方法的一些列对象的集合

类型守卫与类型转换

A as Type

绕过类型检查

  • 类型断言: A as B
  • 类型转换: <type>

typeof的局限性与替代方案

  • typeof

    • 用来检测一个变量或类型
  • 局限性

    // 类型判断
    const arr = [30, 30]
    console.log(typeof arr) // object
    const set = new Set()
    console.log(typeof set) // object
    const map = new Map()
    consolo.log(typeof map) // object

    // 替代方法
    console.log(Object.prototype.toString.call(arr)) // [object Array]
    console.log(Object.prototype.toString.call(set)) // [object Set]
    console.log(Object.prjjototype.toString.call(map)) // [object Map]
    // 但不能精准判断类属性的名字

    // 实例判断: <instance> instanceof Type

    // `in`
    // - 属性或方法的判断
    // - <attr>/<method> in variable

    // 字面量相等判断
    // - `===`

自定义类型守卫

function isClass(形参: 参数类型): 形参 is 类型 {
return type or false
}

泛型

  • 泛型

    • 定义时不明确使用时必须确定某种具体数据的数据类型
    • 编译器期间对数据类型检查的数据类型
  • 泛型约束

    • <T extends Type>
    • <T extends Type, K extends keyof T>
    • <T extends Type, K extends keyof T ? K: never>
  • 反向为泛型赋值

函数重载

  • 重载签名: 只有声明
  • 实现签名: 实现多个类型组成的联合类型(参数类型/返回值类型)

泛型工厂函数类型

  • new表示构造函数,不是创建
  • 类名指向类的构造函数的空间
export default class CommercialBank {
address: string = "bj"
name: string = 'zhangs'
count: number

constructor(name: string, address: string) {
this.name = name
this.address = address
}

loan(): void {
console.log(this.name + ' 银行贷款')
}
}

type ConstructorTyp = new (...args: any) => any

interface ConstructorInter {
new (...args: any): any
}

//function createFactoryConstructor(constructorTyp: new (...args: any) => CommercialBank) {
// function createFactoryConstructor(constructorTyp: new (...args: any) => any) {
function createFactoryConstructor(constructorTyp: ConstructorInter) {
console.log(constructorTyp.name + '被创建...')
new constructorTyp()
}

// 调用 ClassName is constructor
createFactoryConstructor(CommercialBank)

交叉类型

type O1 = { a: string; b: number }
type O2 = { a: string; b: number }
type O3 = O1 & O2

let o3: O3 = {a: 'd', b: 1, c: 'c', d: 2}

通用交叉方法

function cross<T extends object, U extends object>(obj1: T, obj2: U): T & U {
const combine = {} as T & U
for (let k in obj1) (combine as any)[k] = obj1[k]
for (let k in obj2) (combine as any)[k] = obj2[k]
}

let o1: O1 = { a: 'a', b: 3}
let o2: O2 = { c: 'c', d: 4}
console.log(cross(o1, o2))