ts临时抱佛脚。

139 阅读9分钟

ts类型的分类: 基础类型、高级类型、内置类型、自定义类型、类型体操

  1. ts是为了考虑在赋值的时候是否会发生错误,作类型检测,起到一个提示作用,编译后ts类型就消失了,生产环境下可以通过xxx.d.ts文件来增加类型生命
  2. 并非所有变量都要赋类型,因为ts可以类型推导
基本类型

string number boolean 数组 元组 枚举 null undefiend void never any object symbol bigInt

  1. void代表的是空类型,一般值表示函数的返回值,表示不关心返回值
  2. unefiend可以赋予给void,都代表空 (undefiend 是 void的子类型)
  3. never只能被never类型来赋予值,用来做代码完整性保护
  4. any 任何类型,会导致类型丧失检测
const a1 = 1
const u: undefined = undefined
const n: null = null
function a(): void {
    return undefined
}
const symbol:symbol = Symbol()
const bigint:bigint = BigInt(Number.MAX_SAFE_INTEGER + 1)
// const 是常量意味着定义的值不会修改所以他的类型是一个字面量类型 . const声明变量必须复制 
// let 声明变量 可以修改所以类型范围推到的结果会变大
const a1 = 1
let a2  = 1
包装类型
  1. 小写的类型一般用于描述基本类型 大写的用来描述的是实例类型
let s3: String = '1'; // 在赋予值的时候 子集可以赋予给父级
let s4: String = new String('1'); // 类的类型,类类型,用来描述实例的
数组/元组 tuple
  1. 赋予的值要求得符合这个结构和顺序
  2. 元组在新增内容的时候 不能增加额外的类型的值,只能是已有的,而且增加后无法访问
// 三种写法
let arr1: number[] = [1, 2, 3, 4, 5];
let arr2: Array<number> = [1, 2, 3, 4, 5];
let arr3: (number | string)[] = [1, 2, 3, 'a', 'b', 'c'];
let tuple: [string, number, string, number] = ['1', 1, '1', 1]
枚举 enum
  1. .类型可以进行反举 (值是数字的时候 可以反过来枚举), 枚举没有值会根据上面的索引来自动累加
  2. 异构枚举 就是枚举中不光有数字 还有字符串. 异构枚举上一个是字符串下一个无法推导
const enum STATUS { // 常量枚举 不会额外编译成对象, 所以更节约
    'OK' = 'ok',
    'NO_OK' = 100,
    'NOT_FOUND'
}
联合类型
  1. !是非空断言,表示某个变量一定存在
  2. as是指定成某种类型
  3. ele?.style.background 表示的是取值操作,不能赋值
let strOrNum :string | number;
// !TS语法  ?js语法 链判断运算符(如果值有再去取值)
let ele = document.getElementById('app');
// 断言方式1.  
ele!.style.background = 'red';
// 断言方式2.  
(ele as HTMLElement).style.background = 'red';
// 类型别名将类型提取出来
type Direction = 'up'|'down'|'left'|'right'; // 快速构建一个可以复用的类型
let direction:Direction
let up:'down' = direction! as 'down'
// 值  as xxx 或者 <xxx>值  一般用于联合类型. 将大范围的类型 断言成子类型
(strOrNum! as unknown as boolean); // 双重断言,一般不建议使用,但是还会用到,破坏原有的关系
函数类型

函数的声明方式、函数的参数、函数的返回值

  1. 函数关键字声明的函数:不能标注函数类型
  2. 表达式来声明的函数: 必须赋予的值要满足定义的类型,要求有一个兼容性在里面
  3. 函数类型的定义 (a: any, b?: string) => any(a: any, b: any) : any
  4. ? 可选参数 意味着可以不传 和 string | undefiend ,只能在参数列表中的后面
  5. this问题,尽量不采用this 来作为函数的上下文, this的缺陷就是类型推导问题
  6. 函数重载
type ISum = {(a: any, b: any) : any}
type ISum2 =  (a: any, b: any) => any
// 如果标明函数的类型,在使用函数的时候以标明的为准
const sum:ISum  = function(a:string,b:string){
    return a+b
}
// 如果想限制this类型 那么需要手动指定this类型
function getValue(this: IPerson, key: IKeys) { // this不是行参 是标明this的类型
    return this[key];
}
// keyof 可以获取对象中的类型 作为联合类型
const person = { name: 'jw', age: 30, address: '昌平霍营' }
type IPerson = typeof person; // 提取对象的类型为IPerson, type类型会提升到顶部
type IKeys = keyof IPerson;
// 可以将子类型赋予给父类型 
getValue.call(person, 'age')
// string[] | number[]    (number|string)[]
function toArray(value: string): string[]// 具体的某一种方案
function toArray(value: number): number[];
// 上面的声明仅仅是类型上的重载
function toArray(value: string | number): string[] | number[] { // 所有的实现
    return []
}
typeof、keyof关键字
  1. typeof:根据值来获得类型 typeof, 配合type来声明新的类型
  2. keyof: 可以获取对象中的类型 作为联合类型
type IPerson = { name: string, age: number, address: undefined } 
// 等价于下面代码 
const person = { name: 'jw', age: 30k, address: undefined }
type IPerson2 = typeof person;
// 等价于下面代码 
type IKeys = "name" | "age" | "address"
type IKeys2 = keyof IPerson;

type IKeys1 = keyof any; // string | number | symbol  使用场景比较多
type IKeys2 = keyof unknown; //  never
class类型
  1. 构造函数
  2. 属性(实例属性,原型属性、静态属性)、
  3. 方法 (实例的方法,原型方法,静态方法)
  4. 访问器
  5. 类的修饰符
  6. 抽象类
class Animal {
  /* 静态属性 可以被子类继承的 */
  static AnimalName: string = "动物"
  static instace: Animal = new Animal("aaa",11)
  // 静态方法
  static getMessage(): Animal {
    return this.instace
  }
  // static getMessage: (text: string) => string = () => {
  //     return text
  // }

  public readonly y: number
  a = "1" // 实例的属性a
  public fn: () => void //实例有一个属性叫fn
  public foo (num: number): void { } // 类的原型方法foo
  /* 
    类的修饰符  可以简写一行
      public 公共   
      private 自己    如果加在constructor前面,不能被new,只能在类自身里面new实例
      protected 我和儿子
      readonly是只读的,在constructor初始化后就不能被修改了
  */
    protected constructor(public name: string,  y : number) {
    this.name = name;
    this.y = y;
    this.fn = () => {}  

  }
  /* 
    原型属性:通过访问器来实现 Animal.prototpye.aliasName
  */
  get aliasName () {
    return 'msk-' + this.name
  }
  /* 
    原型方法,每个实例共享的方法:父类的方法,可以被子类重写  Animal.prototpye.changName
    void表示不关心返回值。并不是空的意思
  */
  changName(name: string): void {
      this.name = name
  }
}

class Cat extends Animal {
  constructor(name: string, age: number) {
    //  super这里指向的父类 
    super(name, age)
  }
  changName(name: string) {
    console.log(this,);
    
    // super在这里(原型方法中)指向的父类的原型
    super.changName("aaa")
    this.name = name
  }
}
const c = new Cat("haha",18)
c.changName("eee")
console.log(c.aliasName);
console.log(Cat.AnimalName);
console.log(Cat.getMessage());

抽象类

// ts 中有抽象类概念, abstract 不存在的
// 抽象类 可以含义非抽象的方法和属性 , 不会new它 , 抽象类可以被继承,抽象类中抽象方法子类必须要实现
abstract class Animal{
    public abstract a:string
    drink(){ // 非抽象,已经有实现了
        console.log('喝水')
    }
    abstract eat():void // 抽象的方法,父类没有实现,那么子类必须实现
}
class Cat extends Animal{
    public a!: string;
    eat(): void {
        throw new Error("Method not implemented.");
    }
}
// 因为我们编写的代码的时候 , 慢慢的脱离继承了。 组合优于继承。   类的装饰器(redux,nest, mobx)
接口
  1. 描述数据的结构、或者形状的, 定义好结构,在去针对结构来进行实现
  2. 接口: 抽象类(有抽象的也有非抽象) 接口必须都是抽象的(没有具体的实现)
  3. type 和 interface 区别?
    • 一般情况下 描述对象,类 用interface更多一些 ,interface无法声明联合类型
    • type 可以快速声明类型 联合类型,工具类型只能采用type , type不能重名
    • type用的更多,能用type 就type 不能用interface. 复杂类型采用type

接口可以描述对象结构 (子可以赋予给父)

interface IPerson {
    username: string // 类型,不是具体实现
    age:number
}
let obj = {
    username:'abc',
    age:30,
    address:'地址'
}
// 赋值的时候 会产生兼容性  (儿子可以赋予给父亲,如果是声明的必须必须一致)
let person:IPerson = obj; 

// 2) 接口可以描述函数
interface ICounter {
    ():number // ()表示函数参数类型  number是函数的返回值类型
    count:number
}
// const 标识此值不能修改  let 可以修改的 (如果给函数增加类型定义 函数不能被修改时只能用const)
const counter:ICounter = () =>{
    return counter.count++
}
counter.count = 0;

接口特性

  1. 可以通过?表示接口的属性 可有可无
  2. 断言的方式来进行赋值, 用的最多 as IVeg
  3. 接口同名的会进行合并
  4. 可以扩展一个新类型 可以扩展属性
  5. 任意类型 随机的属性 描述数字索引的 (除了必有的属性之外 ,其他任意)
interface Person {
    name: string
    age: number
}
// 2.断言:保证p以后肯定不用age,出了问题就怪写as的人
const p: Person = { name: 'msk' } as Person

interface IVeg { // 3.合并
    address: string
}
interface IVeg { // 3.合并
    readonly name: string // 只读属性
    taste: string;
    size: number,
}
interface IVegetable extends IVeg {
    color?: '红色', // 1.可选
    [key: string]: any // 4.扩展 任意接口 key 随意,值随意
}
const veg: IVegetable = {
    name: '西红柿',
    taste: '甜',
    size: 50,
    color: '红色',
    'a': 1,
    'b': '2',
    [Symbol()]: 'ABC',
    0: 1
}
interface IArray { // 5. 索引接口
    [key: number]: any
}
let arr:IArray = [1,2,3]
let arr2: IArray = { 0: 1, 1: 2, 2: 'abc', 3: true }

interface ResponseData {
  username: string,
  token: string
}
interface ReturnVal {
  code: number,
  data: ResponseData
}
// 通过索引访问符 来获取内部类型
type ICode = ReturnVal['code'];
type IUsername = ReturnVal['data']['username']; // 可以用于取值的类型
type IKeys = ReturnVal[keyof ReturnVal]; // 取值的类型, 可以采用这种方式
// Ikeys = number | ResponseData

implements 定义类的实现

  1. 一个类 可以实现多个接口
interface SpeakChinese {
  speakChinese(): void
}
interface SpeakEnglish {
  speakEnglish(): void
}
class Speak {
  public a!: string;
}
interface Speakable extends SpeakEnglish, SpeakChinese, Speak {
  speak(): void // 实现的是原型方法
  //  speak:()=>void // 实现的是实例方法
}
class Speaker implements Speakable {
  public a!: string;
  speakEnglish(): void {
      throw new Error("Method not implemented.");
  }
  speakChinese(): void {
      throw new Error("Method not implemented.");
  }
  speak() {
      return 100
  }
}
泛型
class Dog {
  constructor(public name: string) { } // new
}
class Cat {
  constructor(public name: string) { }
}
// 描述构造函数 
// interface IClazz<T>{
//     new (name:string): T
// }
type IClazz<T> = new (name: string) => T
// createInstance<T>是形参
function createInstance<T>(clazz: IClazz<T>, name: string) {
  return new clazz(name)
}
const instance = createInstance(Cat, 'tom')
// <dog>是实参,不传也行
const instance2 = createInstance<Dog>(Dog, 'jam')
// 泛型: 泛型坑位 (函数的形式参数) 刚开始类型不确定,通过使用的时候 来确定类型

泛型如何使用

const fn = <T>(name: T): T =>  name
// function 函数名:<泛型 > (参数名: <泛型>): <T> 返回值
function foo<T> (name: T): T[] {
  return [name]
}
// <T>():void 在使用这个函数的函数传入类型
interface ForEach {
    <T>(arr: T[], callback: (val: T) => void): void
}

type ICallback<T> = {(val: T):void} // 
type ICallback2<T> = (val: T) => void 
type IForEach = <T>(arr: T[], callback: ICallback<T>) => void

案例

function swap<T,K>(tuple:[T,K]):[K,T]{
    return [tuple[1],tuple[0]]
}
const r = swap(['jw',true])

泛型默认值

// 配合接口来使用
interface APIResponse<T = any> {
  error: number,
  data: T,
  message?: string
}
interface LoginInfo {
  username: string,
  token: string
}
function login(): APIResponse<LoginInfo> {
// function login(): APIResponse { // 请求方法
  return {
      error: 1,
      data: {username: '张三', token: 'xxxx'},
      message: '成功'
  }
}
let w = login()

泛型约束

type Unique<T = boolean>  = T | string
// 类型约束  子类型 extends 父类型   子类型可以赋给父类型,只要满足父类型的结构
// 约束当前这个类型 K string或number的子类型 (因为在使用泛型的时候参数不能直接做运算,无法确定类型,需要约束)
const getLen = <T extends { length: number | string}, K extends string | number>(val: T, isFlag: Unique, join: K) => {
  return val.length
}
getLen({length: 10}, '11', 111)
getLen('123', false, '2222')

function getObjVal<T extends object, K extends keyof T>(target: T, key: K) {
  return target[key]
}
let person = { name: 'jw', age: 30 };
let animal = { name: 'tom', age: 18, address: 'china' };
getObjVal(animal, "address");

类中的泛型

class MyList<T extends string | number = number> {
  private arr: T[] = []
  add(val: T) {
      this.arr.push(val)
  }
  getMax(): T {
      let max = this.arr[0];
      for (let i = 1; i < this.arr.length; i++) {
          let cur = this.arr[i];
          cur > max ? (max = cur) : void 0
      }
      return max
  }
}
const list = new MyList<string>
list.add('1')
list.add('200');
list.getMax(); // 200