TS 自学笔记

229 阅读20分钟

TS 自学笔记

主要解决问题

js 存在问题

  1. 使用不存在的变量、成员或函数

    const num = '123';
    num.charat(0) // charAt  a写错,无法知道只能运行看结果
    
  2. 类型错误

    const num = 123;
    num.charAt(0) // Number 用了String方法
    
  3. 使用nullundefined的成员

    const obj = undefined;
    obj.prop  // 不存在需要编译过后报错才知道
    

js 设计缺陷

  1. 弱类型语言( 变量可以随意变换类型 )

    let obj = { func: () => {} }; // 创建num属性func方法 
    obj = 'string'; // 重新赋值
    obj.func() // 调用方法 ,但是中间重新赋值了,导致方法不存在,编译出错
    
  2. 解释性: 错误发生是在运行时抛出错误

  3. 本身比较松散,难以适应大型项目

    • 前端开发过程中是在排错
    • 在运行等待运行结果是否报错

ts官网 个人翻译中文网

  1. tsjs 超集 (包含js语法且有自己新语法)

  2. 类型检测

    • 对代码所有标识符进行类型检查(变量、函数、参数、返回值)
    • 可选的类型检查(可以选择加不加类型检查)
  3. 静态(只在编写的时候进行类型检查,不在运行时)

    • 不直接支持浏览器和node环境,编译过程需要先转换为js文件(通过tsc)

ts学习

ts安装

  1. 需要node环境,下载node
  2. npm i -g typescript // 全局安装 环境安装完成
    

ts代码

// test.ts
let str: string = "hello" // 使str为字符串类型的变量
str = 123 // 此时 str会飘红
tsc test.ts // 编译通过,但是会抛出错误

三方库

  1. @types/node 由于之前都是js写的,如果需要类型检查需要安装@types/node

    • 比如jquery 需要类型检测安装 @types/jquery
  2. ts-node 可以直接编译运行ts文件 运行直接 ts-node src/index.ts 后面参数入口文件

  3. nodemon 实现热更新检测 nodemon --exec ts-node src/index.ts 在文件更新输入时会自动更新

    • nodemon -e ts --exec ts-node src/index.ts // -e 后面文件类型 只监控 ts文件的改变
    • nodemon --watch src -e ts --exec ts-node src/index.ts // --watch 只监听src目录下的ts文件变化
tsc --init // 生成tsconfig.json配置文件
//  tsconfig.json  编译时直接 tsc完成编译,不用加文件名,否则不会用配置文件
{
    "compilerOptions": { // 编译选项
        "target": "es2016"; // 配置编译目标代码的标准
        "module": "commonjs" // 配置目标模块化标准es6、commonjs
        "lib": ["es2016"], // 编写环境 默认"dom" 环境
        "outDir": "./dist" // 编译结果
    },
    "include": ["./src"], // 编译目录
    "files": ["./src/index"] // 入口文件 只会编译这个文件以及其依赖
}

ts 语法

  1. 在变量和函数命名地方 加个:string
let a: number = 123; // 定义变量a类型为Number为数字123 还可以定义为sting,boolean,
function add(a: number, b: nuber): number {
    return a + b
} // 定义一个函数两个参数类型number和 返回number类型
  1. 类型推导

    let a = 123; // 推导a为Number类型数据
    a = "123" // a会飘红
    
  2. 未定义的类型会默认为anyts 写的不好戏称anyscript

    let a; // 默认为any类型
    
  3. 不同类型

    1. 基本类型
    let str: string = 'str'; // 类型检测为 string
    let num: number = 123; // 类型检测为 number
    let boo: boolean = true; // 类型检测为 boolean
    let num: Array<number> = [1, 2, 3]; // 类型检测为 Array且Array的项为Number
    let num: number[] = [1, 2, 3]; // 或者Array这么写 防止和React语法冲突,推荐这种
    let str: object = {}; // 类型检测为 Object (基本不用) 引用值都是对象,约束力不强
    
    • null 和undeined是所有类型的子类,可以赋值给任何类型
    let str: string = undefined; // 语法没问题 ,但是ts不应该这么赋值
    
    • 在配置里面tsconfig.json属性 compilerOptions添加strictNullChecks: true 可以获得更严格的控制检查,只能 将其赋值给本身
    let str: string = undefined; // 飘红
    let empty: undefined = undefined; // 正确
    
    1. 其他类型

      • 联合类型: 多种类型任选其一
      • 类型保护: 在某个变量的类型进行判断之后,语句块中确定变量类型,typeof触发类型保护
      let name: string | undefined; // 设置类型为string或者undefined 
      if (typeof name === 'string'){
          name.split(0,-1); // 判断之后确定时sting类型会出现代码提示,否则不会出现代码提示
      }
      
      • void类型
      function func() {} // 一般约束返回值,函数没有返回值
      function func():void {} // 等同上面
      
      • never类型(一般约束函数返回值,表示永远不会结束)
      function func(msg: string): never {
          throw new Error(msg)
      } // 或者 死循环
      
      • 字面量约束
      let sex: 'male'; // 表示只能取直为male
      let sex: 'male'|'female'; // 利用联合类型和字面量类型只能赋值为 male和female
      let user: {
          name: string
          age: number
      } // 定义user
      let obj: user; // obj 必须是对象且属性必须为name和age 注意 不能后面添加属性 eg: user.sex = 'male'; 这样做是不被提倡的,对象应该在创建的时候确定其属性
      
      • 元组
      let arr: [strig, number]; // 设置变量为数组且只有两个项第一项为string第二项为number
      
      • any
      let a: any; // 慎用,可以给任何东西赋值,从而污染别的变量的类型约束 是在不知道类型可以用unknown,不会传递类型检查
      
      • 类型别名
      type user = {
          name: sting
          sex: 'male'|'female',
          age: number
          rich?: boolean // 可选属性
      } // 类型别名
      function func(): user[]{ return {name: '',sex: 'male', age: 23} }
      
      • 函数相关约束
      function func(a: number,b: number): number; 
      function func(a: string,b: string): string;
      function func(a: string|number,b: string|string): string:number{ return string|number } // 在定义函数之前 函数存在约束,表示参数什么类型,返回什么类型// 可选参数? (只能在最后) 默认参数 =
      function func(a: number,b: number, c = 123,d?: number)number; 
      
      1. 扩展类型 - 枚举

        扩展类型: 类型别名、枚举、接口、类

        枚举类型 约束变量的取值范围(或者使用字面量和联合类型实现)

        字面量类型问题

        • 在类型约束位置,会产生重复代码。可以适用类型别名解决

        • 逻辑含义和真实值产生混淆,导致修改真实值时候大量修改(字面量需要赋值比如说 )

          type Gender = 'male'|'female';
          let sex: Gender = 'male' // 需要实际赋值
          
        • 字面量类型不会进入编译结果

        // enum 枚举名 {枚举字段 = 值}  编译结果是一个对象
        enum Gender {  male = "男", female = "女" };  // 如果逻辑名称需要改,选中f2修改
        let sex: Gender = Gender.male // 避免修改字面量名字导致大量代码修改
        
        • 枚举值可以是字符串和数字

        • 数字枚举值会自增(默认0开始)

          enum num {
              level = 1,   // 默认为0 如果设置了则后面自增为2,3
              level2,
              level3
          }
          
        • 枚举的值中尽量保持值类型的统一(都为字符或者为数字)

        • 使用枚举,尽量使用枚举key值,而不用value,eg使用num.level

        • 数字枚举高级使用(位运算符)

          enum Permission{
              Read = 1,
              Write = 2,
              Update = 4,
              Delete = 8,
              // 四种各种组合 权限,可以枚举出来,高级方法 位运算
          } 
          let p: Permission = Permission.Read | Permission.Write // 组合权限 二进制|或运算
          // 判断权限是否有某个 且运算&
          function hasP(target: Permission,per: Permission) {
              return (targe & per) === per;
          }
          // 删除某个权限 ^异或运算 相同取0 ,不同取1
          p = p ^ Permission.Write
          

ts模块化

统一用es6模块化导出,这样编译之后不会有变化 (如果用es6导入导出 以下不需要看)

尽量使用具名导出,这样在别的文件中写方法会自动导入模块

有些 模块是以前的导入情况

// 加入fs模块有个add方法
export.module = { add:() => {}} // 这样导入
// 导入文件
import fs from 'fs' // 会出现问题 不能这样直接导入()可以看编译是把其当成默认导入了// 解决方法
import { add } from  'fs' // 1.选择固定方法
import * as fs from  'fs' // 2. 重命名
// 3. 修改配置 compilerOptions 属性 "esModuleInterop": true

在TS中使用commonjs 修改配置 "esModuleInterop": true

// 导出 app.ts
module.export = {
    a: '123',
    b: (c: string): string => c
}
​
// 导入
const my = require("./app") // 这么写没有类型检测// 导出 app.ts
export = { // 修改导出方式
    a: '123',
    b: (c: string): string => c
}
​
// 导入
import myModule from './app' // 这样写才能有类型检测

模块解析

  • classic: 经典

    • 相对模块导入解析和非相对模块导入模块解析不同的是解析相对模块导入时只会查找指定的一个目录;
    • 解析非相对模块导入时会向上遍历整个目录树。而且解析非相对模块导入会多出一步来。就是为了查找是否安装要导入的声明文件。
  • node: node解析策略(唯一的变化,将 js 替换为 ts )

    • 相对路径 require("./xxx") 先找当前目录,然后再找package.json 中main是否配置,把xxx当作文件夹,找main配置的文件(没配置找xxx文件夹index文件)
    • 绝对路径 require("xxx") node_modules文件,如果没有的话一直往上找到根目录

    推荐 node解析策略,修改配置定死 "moduleResolution":"node"

接口类型的兼容性

拓展类型 -接口

拓展类型: 类型别名、枚举、接口、类

接口: 用于约束类、对象、函数的 标准

标准的形式:API 文档,弱标准; 代码约束,强标准

// interface 接口 不会编译  推荐使用 接口 相对于别名
// 约束对象 约束函数、方法
interface User {
    name: string
    age: number
    func():void
}
type FunType = (n:number) => boolean;
type FunType = {
    (n:number): boolean
}
interface FunType = {
    (n:number): boolean
}
​
function func(done:(s:string) => boolean, callback: FunType): number{}
​
// 继承
interface A {
    T1: string
}
interface B {
    T2: number
}
// 继承 可以多个小的接口组成丰富 的接口约束
interface C extends A,B {   // 如果A,B有属性类型冲突,则不能同时继承
    T3: boolean
}
​
​
// type(别名) 没有继承,可以用  & 实现 
type A = { T1: string } 
type B = { T2: string } 
type C = { T1: number } & A & B; 
// 当ABC属性冲突,会合并属性 即 T1类型为 string & number

修饰符

interface User {
   readonly id: string,  // 只能第一次赋值时候赋值,别的时候不能改变 readonly 只读修饰符 不在编译结果
   readonly arr: readonly string[]
}
​
const arr: readonly number[] = [1,2,3]; // 不能改变arr成员,但是可以修改arr指针; const 不能修改指针,从而固定arr 属性和值
const arr: ReadonlyArray<number> = [1,2,3]
​

类型兼容性

B->A 如果能完成赋值,则B和A类型兼容 鸭子 辨型法(子结构辨型法): 目标类型某一些特征,赋值类型只要能满足特征即可

  • 基本类型: 完全匹配
  • 对象类型: 鸭子辨型法
  • 函数类型: 参数给目标函数,参数可以少,但不可以多;有限定返回值时需要一致,没有限定是可以随意
interface Cat {
    foot: number
    type: 'cat'
    run(): void
}
​
let dog = {
    foot: 4,
    head: 1,
    type: 'dog' as 'cat',  // 前面值 后面是 类型
    run() {
        this.foot + 'foot run'
    }
}
​
let cat1: Cat = dog // 按道理来说需要完全匹配才可以赋值成功,到那时这里 鸭子辨型法可以赋值成功
let cat2: Cat = {  // 如果直接赋值 则ts报错 无法赋值成功
    foot: 4,
    head: 1,
    name: 'dog',
    run() {
        this.foot + 'foot run'
    }
}

类型断言 as

// 但你与ts对变量类型有冲突使用
let a = 123 as number; // 前面是值,后面是类型 通常这里的值是一个变量

// js 定义类
class User {
    constructor(name, age){
        this.name = name;
        this.age = age;
    }
}
// ts 定义类
class User {
  name: string // 可写可不写,推荐写,这样写在构造器里面不赋值会报错
  type = 'user'; // 实例属性 定义在实例上 也可以写在构造器里面,要加this
  #count = 0;  // 加#号私有属性,只能自己读取this.#count(es2022) es没有私有方法 需要自己特别实现(可以用ts的private 修饰器)
  private _hobit = 'do something' // 私有属性修饰器 私有属性命名上习惯在前面加个下划线
  public kind = 'people' // 公共属性修饰器 相当于实例属性
  protected _number = '123' // 在类的内部和子类中可以访问,在外面就访问不到了
  static total: number = 3; // 静态属性es6  定义在class类上
  constructor(name: string,public height: string, readonly age: number){  // public height: string 属性简写,如果传入值不做任何改变直接复制可以直接简写,但是需要在属性前面加  任意修饰符
      this.name = name;
      this.age = age;
  }
  // 访问器 es6 就有 也可以用方法实现  如果不设置set方法 ,只能读取属性,相当于只读属性,一般和私有属性配合
  get value(){
    return this._hobit + '123'
  }
  set value(val: number){
    this._hobit = val
  }
}

泛型

附属于函数、类。接口、类别名之上的类型(在函数名之后写<T> T 可以为任意字符)

类型不确定的时候,但是需要一致使用

function func<T,U = sting>(a: T,b: T,c: U): T|U { // 泛型可以设置默认值
    return a || b || c
}
// 调用
func<number,string>(1,2,'3'); // 如果是基本类型,可以省略前面的<number,string>,TS会自动推导type callback<T> = (n: T, i: number) => boolean;
interface callback<T> {
    (n: T, i: number) => boolean;
}
function filter<T>(arr: T[],callback: callback<T>){ // 将T传给callback,统一类型
  const newArr: T[] = [];
  arr.forEach((n,i) => {
      if(callback(n,i)) {
          newArr.push(n)
      }
  })
  return newArr;
}
filter([1,2,3,4],n => n%2 !==0) // 调用 取奇数项

class Arr<T> {
  arr: T[]
  constructor(arr: T[]){
    this.arr = arr
  }
  getNumber(index: number): T{
    return this.arr[index]
  }
}
new Arr<numebr>([1,2,3]) // 因为<number>为基本类型,TS会自动推导,可以省略

泛型的限制,在写泛型的时候需要对泛型进行一定的约束(extends)

interface objProperty {
    a: number
    b: number
}
function add<T extends objProperty>(obj: T): T{
    obj.a += 10  // 如果不约束,无法确定传入的是对象且有a属性
    obj.b += 10;
    return obj
}
  • 类型保护函数 ,返回值是 boolean
  • 系统缺少对能力的定义 -接口
  • 面向对象接口语义: 表达类是否有某个能力
  • 接口 和 类型 别名: 接口可以被类实现,而类型不行

  • 接口可以继承类,表示该类的所有成员都在接口

    • interface C extends A,B(AB是类,当定义 let c:C = {} 对象中需要包含所有的AB类成员)
// 导出 接口
export interface Inter { a: string }
// 导出 别名
export type T = { a: string }
// 导出 方法
export function F(a:string): string {
    return a
}
​
​
// 导出 类   抽象类 - 只能用来子类继承,不能new
export abstract class Animal {
  abstract type: string
  constructor(public name: string){   // public name: string 是 name: string onstructor(name: string){ this.name = name } 简写
    this.name = name
  }
  sayHello() {
    console.log(`${this.name} is ${this.type} say hello`)
  }
}
​
// 定义接口
interface JunmpLook {
  lookDoor():void
  jump():void
}
interface Eat {
  eat():void
}
// implements 实现接口 表示某个类有某种能力
class Dog extends Animal implements JunmpLook{
  type = 'dog'
  run() {
    console.log(`${this.name} 跑`)
  }
  jump() {
    console.log(`${this.name} 跳`)
  }
  lookDoor() {
    console.log(`${this.name} 看门`)
  }
}
​
class Bird extends Animal implements JunmpLook{
  type = 'bird'
  fly() {
    console.log(`${this.name} 飞`)
  }
  jump() {
    console.log(`${this.name} 跳`)
  }
  lookDoor() {
    console.log(`${this.name} 看门`)
  }
}
​
class Pig extends Animal implements Eat{
  type = 'pig'
  jump() {
    console.log(`${this.name} 跳`)
  }
  eat() {
    console.log(`${this.name} 吃的多`)
  }
}
​
const animals: Animal[] = [ new Dog('旺财'), new Pig('八戒'), new Dog('二狗'), new Bird('飞鸟')]
​
// 类型保护函数 按理来说此处是检查动物的功能animal 类型应该是Animal,
// 但是会跳和看门的不止动物,我们此处只需要判断有这两个方法的对象即可
// 如果animal: Animal 类型;animal as unknown as JunmpLook 其中unknown 是将其转为未知对象再断言为 有这个方法 因为上面定义了 animal: Animal
function hasFunc(animal: Object): animal is JunmpLook {
     // animal as JunmpLook  中 as JunmpLook 可以省略,因为定义为Object对象了,为了更好的语法提示,所以写上断言
  if((animal as JunmpLook).jump && (animal as JunmpLook).jump){
     true
  }
  return false
}
​
// 判断 动物集合有没有 会看门的 和会跳的
animals.forEach(animal => {
  // if(animal.lookDoor && animal.jump){  animal.lookDoor(); animal.jump() }  // 因为类型不确定,所以无法读取
  // if(animal.type === 'bird' || animal.type === 'dog'){ 
  //   (animal as Bird|Dog).lookDoor(); 
  //   (animal as Bird|Dog).jump() 
  // }  
  // 这么判断 未来维护比较麻烦( 在类改变或者添加新类有方法时需要改变这个方法 )
  // 其次,我们需要的是找出有这个方法的对象,而这是找出对象有这个方法,语义不对
  if(hasFunc(animal)){ // 找出会 看门和 会跳的
    animal.lookDoor(); 
    animal.jump() 
  }
})

this指向

js this 指向比较多变,ts不好控制,在方法或者类使用this中,可以强行指定this指向

"noImplicitThis": true, tsconfig 设置,不能指向any this

function func(this: window) { // window 可选  this不是参数,不会出现在编译结果
    console.log(this)
}

装饰器

为某些属性、类、参数、方法提供元数据信息;

元数据: 修饰数据的数据

类装饰器 在加载的时候执行

多个装饰器 后加入先调用(写在下面的先调用)

// 本质是一个函数 
// 首先需要关闭 "strictPropertyInitialization": false,  
// 开启知晓未来语法(由于修饰器不在es标准中,未来可能改变,需要开启确认知晓) "experimentalDecorators": true,
function test(target: new (..args: any[]) => object) {
    console.log(target)
}
​
@test
class User{
    name = 'user'
}

成员修饰器

function d (...arg) { // 属性 装饰器工厂 有参数需要传递时候可以使用
    console.log(arg)
    return function (target: unknown, key: string) {
        console.log(target, key)
    }
}
​
function d2 () { // 方法 多一个参数,表示修饰符 相当于Object.definePrototype
    return function (target: unknown, key: string, descriptor: PropertyDescriptor) {
        descriptor.enumerable = true // 开启 可以进入遍历
        console.log(target, key)
    }
}
​
class User {
  @d() // target 类的原型 User.prototype ; key 为属性名
  prop: string  
  @d() // target 类本身 User
  static prop1: string  
  @d2() // target 类的原型 User.prototype ; key 为方法名 将func设置成可遍历
  func() {
    console.log(123)
  }
}

参数装饰器

class MyMath {
    sum(a: number, @test b: number) {
        return a + b 
    }
}
​
function test(target: any, method: string, index: number){
    console.log(target, method, index) // MaMath 类本身 方法 第几个参数
}

// reflect-metadata 装饰器 元数据修饰 
// class-validator 装饰器 验证规则(各种验证规则都有)
import 'reflect-metadata'
import { IsNotEmpty, validate, MinLength, MaxLength, Min, Max } from 'class-validator'
@Reflect.metadata('a','一个用户信息的类')  // 全局注册
class RegUser {
    @IsNotEmpty({message: "账号不能为空"}) // message 可以不设置有默认值,可以自定义记住这里也是上面的修饰器后执行
    @MinLength(5,{message: "账号至少5个字符"})
    @MaxLength(12,{message: "账号最多12个字符"})
    @Reflect.metadata('a','用户id') // 可以和类重名,每个都不一样
    loginId: string
    @Reflect.metadata('prop','用户密码')
    loginPwd: string
}
const post = new RegUser(); // 后续赋值不能改变post指针
post.loginId = "22"validate(post).then(errors => { // 返回的是一个promise 需要then来接受errors
    console.log(errors) // errors 是一个数组,包含每个 错误信息以及属性信息
})
​
Reflect.getMetadata('a', RegUser) // 一个用户信息的类
Reflect.getMetadata('a', post, 'loginId') // 用户id
Reflect.hasMetadata('a', RegUser) // 是否有该元数据 返回 boolean
Reflect.hasMetadata('prop', post, 'loginPwd') // 是否有该元数据 返回 boolean
​
​
​
​
// class-transformer  将平对象转化为类对象  平面对象: { name: 'name', age: 12 }
// myjson.com  获取一个json格式的数据测试
// 可以配合 'class-validator' 类型判定对返回数据
import 'reflect-metadata'
import { plainToClass, Type } from 'class-transformer'
class User {
    id: number
    firstName: string
    lastName: string
    @Type(() => Number) // 元数据信息,强行转为number 注意N是大写
    age: number
    getFullName() {
        return this.firstName+' '+this.lastName
    }
    isAduit() {
        return this.age > 18
    }
}
axios.get('地址').then( resp => resp.data ).then(data =>{  // data 是 User[]
    // 有时候对数据做一些特等的处理 需要写一些方法 但是不集中
    // const users = plainToClass(User, users)  可以直接将数组平面对象转为数组类对象 这样就不需要下面每一个都转一次了
    data.forEach(user: User[] => { // Use[] 的意思是 数组的项都是User类的实例
        // 将平面对象转为 类对象 可以直接调用类的方法 ,如果这样调用就不需要上面 转为数组类对象    
        const user = plainToClass(User, u)
        cosole.log(user.getFullName(), user.isAduit())
    })
})

关键字 -- 类型演算

// typeof
const b: number = 123
const a: typeof b = 123 // 书写位置再约束位置上,表述获取某个数据的类型
class User {}
const U: typeof User = User // typeof 作用于类的时候,是该类的构造函数
// 如果直接写 U: User 这表示U是User类的实例
const N: () => User = User // 或者也可以这么写// keyof 动态形成联合类型 用来获取去他类型中所有的成员名组成联合类型
interface User {
    loginId: string
    age: number
}
​
function printUser(obj: User, prop: keyof User){ // 或者写 prop: loginId|age  
    console.log(obj[prop])
}
​
// in 
type Obj = {
    [p in keyof User]: string 
    // 相当于 type Obj = { [p in loginId|age]: string  }
    // 相当于 type Obj = { loginId: string, age: string }
}
type Obj1 = {
    readonly [p in keyof User]: User[p] // 全部拷贝User
}
// 动态 将对象变为只读
type Obj3<T> = {
    readonly [p in keyof T]: T[p]
}
let user: Obj3<User> = {
    loginId: '123',
    age: 18
}
// TS自带 类型演算// 将类型变为可选  Partial<T>
type Partial<T> = {
    [p in keyof T]?: T[p]
}
// 将可选转为必填 Required<T>
type Required<T> = {
    [p in keyof T]-?: T[p]
}
​
// 将成员变为只读 Readonly<T>
type Readonly<T> = {
    readonly [p in keyof T]: T[p]
}
​
// 从T中剔除可以赋值给U的值 (差集)  Exclude<T,U>
type Exclude<T,U> = T extends U ? never : T  // T 中包含U成员的话去掉,不包含的保留
let u: Exclude<'string'|'number'|null|undefined, null|undefined> 
let u: 'string'|'number' // 等同于// 从T中只保留U的值  (交集) Extract<T,U> 
type Extract<T,U> = T extends U ? T : never 
let u: Extract<'string'|'number'|null|undefined, null|undefined> 
    
// 从T中剔除null和undefined NonNullable<T>
type NonNullable<T> = T extends null | undefined ? never : T
​
// 获取函数返回值类型   ReturnType<T> 
type func = () => number;
type returnType = ReturnType<func>   // 参数是函数类型 得到函数的返回类型
function add(a: number, b: number){
    return a+b
}
let a: ReturnType<typeof add>; // 因为add 是函数,需要typeof获得类型再通过ReturnType得到函数返回类型约束a// 获取构造函数类型的实例类型返回值类型   InstanceType<T>   
class People {
  id: string
}
type ParamsConstructor = new (arg1: any, arg2: any) => People // 对构造函数约束
// const A: ParamsConstructor = class User extends People { 
//   constructor(a: any, b: any) {
//     super()
//   }
// }type Ins = InstanceType<ParamsConstructor>  // 约束 Ins 只能是People类成员
const us: Ins = {
  id: '123'  
}

TS声明文件

声明文件

.d.ts 后缀的文件

作用:为JS代码提供类型声明

声明文件位置

  • 放置再ts.config.json 包含的位置之中(include: []
  • 放置node_modules/@types 文件 一般不会自己手动放置
  • 手动配置,tsconfig.json "typeRoots": [路径] 配置了的话上面规则失效,只会再配置的位置找
  • 与JS代码所在目录相同,并且文件名也相同的文件 即同文件下有index.d.ts index.js; 用ts代码书写的工程发布之后的格式

编写

手动编写 自动生成 只做类型声明(不能写逻辑)

自动生成

  • 我们打包的以及发包的时候是编译之后的TS文件,即结果是JS文件(从而丧失TS的类型检测)
  • 而我们有时候发的包是工具库给他人使用的,(缺乏类型检测就很难使用)从而可以使用声明文件描述发布结果中的类型
  • 自动生成 配置tsconfig.json中的"declaration": true 还有.js.map 后缀的名字是作为编译后检查错误的位置的"sourceMap": true 控制台 里面的Sources

手动编写

  • 对已有库,他是使用JS书写而成,并且更改该哭的代码为TS成本比较高,就可以手动编写声明文件
  • 对一些第三方库,他们使用JS书写而成并且这些第三方库眉头提供声明文件,可以手动编写声明文件
// global.d.ts  全局声明
interface Console {
    log(message?: any):void
    error(message?: any): void
}
declare var console: Consoledeclare namespace console { // 命名空间,用的少了,表示log函数必须通过console来调用,即 console.log()
    function log(message?: any): void
    function error(message?: any): void
}
// 模块声明  拿lodash举例  也可以直接下载@types/loadsh
declare module "lodash" {
    export function chunk(arg1: <T>[],arg2: number): <T>[][]   // chunk函数接两个参数,一个原数组和一个step 数字,返回一个二元数组 
}
​
// 使用
import _ from "loadsh"
let newArr = _.chunk([1,2,3,4,5], 2) // [ [1,2], [3,4], [5] ]

课外知识 gitHub pr贡献

  1. 进入第三方库 gitHub地址
  2. fork到自己的开源库中
  3. 从自己远程仓库clone到本地开发
  4. 根据三方库的分支命名规则创建分支
  5. 开发完成根据第三方库书写commit信息, push到自己远程仓库
  6. 到第三方库提交 pull request 即pr
  7. 等待第三方库管理员审核,进入审核流程

文章基于渡一教育 袁进老师的ts课程 仅用于个人笔记