TypeScript面向对象、装饰器、keyof、infer、extends 不同场景下的使用(笔记)

1. 什么是面向对象

  • 万物皆是对象,对象有属性和方法,拿人来举例:人的姓名、年龄、性别等这些都是属性,属性可以通过点属性的形式去获取,人可以吃东西、走路、唱歌等这些都是方法,方法可以通过点方法括号的形式去调用
  • 举例来说
    • 操作浏览器要使用window对象
    • 操作网页要使用document对象
    • 操作控制台要使用console对象

2. class 类

2.1 构造函数与修饰符
class 类名 {
  // static开头 静态属性(类属性),默认属性是需要对象的实例去访问,静态属性是直接通过类去访问
  // 静态函数只能访问静态属性
  static 属性名: 类型
  // readonly开头 只读
  readonly 属性名: 类型
  // static在前readonly在后
  static readonly 属性名: 类型
  
  // 在构造函数中使用this.属性名的时候类中要有这个属性才行(js可以没有,但是ts要有)
  constructor(实参: 类型){
    // 在实例中this指向当前实例
    // 在构造函数中指向当前类
    this.属性名 = 参数
  }
    
  方法名(){
    ...
  }
}

let 变量 = new 类名(形参)
2.2 继承 extends
// extends 继承,为什么要使用继承?
// 打个比方动物都有很多相同的特征但是种类又不一样,这个时候可以用一个动物的总类把相同的特征都写在里面
// Animal是一个动物类,使用 Dog extends Animal 表示小狗类继承了动物类
// 继承后的类拥有父类所有的属性和方法
// 如果希望在子类添加一些父类没有的属性和方法直接就可以了
// 如果希望覆盖掉父类的一些方法,直接子类的方法和父类相同即可(方法重写)
// 如果子类写了constructor,需要用个super关键字(super表示当前类的父类)
// 需要把父类和当前类的参数都写入constructor()中
// 父类的参数还要在写入super()中
class Animal {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  say() {
    console.log("动物在叫~~");

  }
}

class Dogs extends Animal {
  run() {
    console.log(this.name + "在跑");
  }
  say() {
    console.log("小狗在叫");
  }
}

class Cal extends Animal {
  sex: string
  constructor(name: string, age: number, sex: string) {
    super(name, age)
    this.sex = sex
  }
  say() {
    console.log("小猫在叫");
  }
}

let dog = new Dogs("泰迪", 5)
let cal = new Cal("咪咪", 4, "nan")

console.log(dog);
dog.run()
dog.say()
console.log(cal);
cal.say()
2.3 抽象类 abstract
// abstract
// 在类前面写为抽象类,表示这个抽象类不能被实例
// 在方法前写为抽象方法,他的子类必须要重写这个方法,抽象方法没有方法体(void类型),抽象方法只能在抽象类中
abstract class Animal {
    name: string
    age: number
    constructor(name: string, age: number) {
      this.name = name
      this.age = age
    }
    abstract say(): void
  }
2.4 接口 interface
// 1.接口可以当成类型声明(type)去使用,可以创建多个相同类名的接口,他们的属性和方法会叠加
// 2.接口主要是用来定义一个类的结构,用来定义一个类中应该包涵哪些属性和方法
//   接口中的属性都不能有实际的值,接口只是定义对象的结构
//   接口中所有的方法都是抽象方法
// 抽象类和接口的区别:
// 抽象类是用来继承的,接口是来限制类必须符合自己的标准(属性和方法),可以追加标准
interface myInter {
  name: string;
  age?: number; // 可选属性
  [propName: string]: any; // 可以添加任意属性
}

// 规定属性值只能为 number 或 string 类型
interface myInter {
  readonly name: string; // 接口里面也可以设置只读属性
  age: number;
  [propName: string]: number | string;
}

interface myInter {
  // say(): void
}

// 对象使用
let aaa: myInter = {
  name: '小明',
  age: 18
}

// 类使用
class myClass implements myInter {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  say() { }
}

// 接口继承,B 要符合自己和 A 的标准
interface B extends A { }

// 接口合并,需要遵守 B C,也可以自己添加
interface A extends B, C { }
2.5 属性的封装
// 在类中定义的属性可以随意被更改会非常不安全,比如说年龄不能为负数
// 可以使用private或者protected把属性变为私有,再用ts中的get、set方法读取(可以给属性添加约束)
class Animal {
  // public 共有属性(默认值),可以在任意位置访问修改
  // private 私有属性,只能在当前类内部进行访问(子类都不行)
  // protected 私有属性,他的子类也可以访问
  private _name: string
  private _age: number
  constructor(_name: string, _age: number) {
    this._name = _name
    this._age = _age
  }
  get age() {
    console.log("获取了数据");
    return this._age
  }
  set age(value) {
    if (value > 0) {
      console.log("修改了数据");
      this._age = value
    }
  }
}
let animal = new Animal("小狗", 3)
animal.age = 1
console.log(animal);
// 属性语法糖写法,下面这两种写法一样
// 1.
class Animal {
  constructor(name: string, age: number) {

  }
}
let animal = new Animal("小狗", 3)

// 2.
class Animal {
  name: string
  age: number
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
}
let animal = new Animal("小狗", 3)
2.6 泛型
2.6.1 基本使用

在定义函数或者类时,如果遇到类型不明确就可以使用泛型(any不安全)

// 可以写多个 <T, K>,泛型可以理解为类型变量
function fn<T>(a: T): T {
  return a
}
let result = fn(10) // 不指定泛型,会自动进行判断
let result2 = fn<string>("hello") // 指定泛型

interface MyInters {
  name: string
  age: number
}
// T extends MyInters 表示泛型T必须实现 MyInters 类
function fn2<T extends MyInters>(a: T) {
  return a
}
let result3 = fn2({ name: "小明", age: 18 })// 传的值必须要满足接口中的结构
2.6.2 泛型约束

K 必须满足 T 里的所有属性

function fn<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

let obj = { a: 1, b: 2, c: 3 };

fn(obj, "a");

fn(obj, "d"); // 类型“"d"”的参数不能赋给类型“"a" | "b" | "c"”的参数
2.6.3 泛型类

指定这个类,只能添加一种类型的数据到数组中

class Sum<T> {
  list: T[] = [];
  add(v: T) {
    this.list.push(v);
    return this;
  }
}

let sum = new Sum<number>();
sum.add(123).add(123);

3. 装饰器(实验性)

类似于封装公共的功能,然后添加到对应的类中

const watcher: ClassDecorator = (target: Function) => {
  // target 当前类
  target.prototype.fn = () => {
    console.log("我是新增的方法");
  };
};

const addHandle: ClassDecorator = (target: Function) => {
  target.prototype.add = () => {};
  target.prototype.remove = () => {};
};

// 一个类可以添加多个装饰器
@watcher
@addHandle
class A {}

@watcher
class B {}

// 还有方法装饰器、属性装饰器、参数装饰器...

4. keyof(类型操作)

keyof 提取对象中的属性名称

// 添加一个类型,判断当前对象是否存在当前属性,这种并没有实际意义(方便理解)
type key<K, T> = K extends keyof T ? true : false;

type bool1 = key<"name", { name: "小黄鸡" }>; // type bool1 = true
type bool2 = key<"age", { name: "小黄鸡" }>; // type bool2 = false

// ts 内置了一个类型,把对象的所有值变成只读的
// [P in keyof T] 相当于 forin 遍历
// keyof 相当于把 {name: xx, age: xx} 变成 name | age
// in 是专门遍历 name | age 这种
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

注:数组遍历 [P in T[number]]

5. infer(类型操作)

推导类型,充当占位符使用

// 获取泛型类型
interface Person<T> {
    name: T;
}
type personType = Person<string>;
type getType = personType extends Person<infer T> ? T : never; // type getType = string


// 提取头部类型
type arrType = [number, string, boolean];
type firstType<T extends any[]> = T extends [infer first, ...infer args] ? first : [];
type first = firstType<arrType>; // type first = number

6. extends 不同场景下的使用

6.1 类的继承
class Animal {
  name: string;
  constructor(name: string) { this.name = name; }
}

class Dog extends Animal {
  bark() { console.log('Woof!'); }
}
6.2 接口的扩展
interface Animal {
  name: string;
}

interface Dog extends Animal {
  bark(): void;
}
6.3 类型约束
function getProperty<T extends { [key: string]: any }, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
6.4 条件类型
type TypeName<T> = T extends Function ? 'function' : T extends object ? 'object' : 'unknown';
6.5 联合类型(union)
// 类型数量,前多后少,分配类型进行比较
type A = number;
type B = string;
type C = boolean;

// 这个条件类型将检查 T 是否包含类型 string
type HasC<T> = T extends string ? T : never;

// 当我们使用联合类型 A | B | C 作为输入时,
// 结果将是 (A extends string ? A : never) |
//          (B extends string ? B : never) |
//          (C extends string ? C : never)
type Result = HasC<A | B | C>; // 结果为 never | string | never,最终简化为 string
6.6 类型比较与类型检查,[] 取消分配性
// 1. 泛型 extends 类型,会进行分配(类型比较)
type If<C extends boolean, T, F> = C extends true ? T : F;
type A = If<boolean, 1, 2> // 1 | 2

// 2. 类型 extends 类型,直接进行检查(类型检查)
type If<C extends boolean, T, F> = boolean extends true ? T : F;
type A = If<boolean, 1, 2> // 2

// 3. [泛型] extends [类型],直接进行检查
type If<C extends boolean, T, F> = [C] extends [true] ? T : F;
type A = If<boolean, 1, 2> // 2

注:加括号像是直接把泛型变成值了,所以不会分配了(个人理解)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值