首页 > 其他分享 >第五篇 TypeScript 【 typeScript 泛型 + typeScript 装饰器 】

第五篇 TypeScript 【 typeScript 泛型 + typeScript 装饰器 】

时间:2023-03-28 15:56:14浏览次数:40  
标签:TypeScript target name any typeScript 泛型 装饰 string

typeScript 泛型

1、软件工程中,我们不仅要创建一致的定义良好的 API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能

2、在像 C# 和 Java 这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据,这样用户就可以以自己的数据类型来使用组件

3、设计泛型的关键目的是在成员之间提供有意义的约束,这些成员可以是

   1、类的实例成员
   
   2、类的方法
   
   3、函数参数
   
   4、函数返回值

4、泛型(Generics)是允许同一个函数接受不同类型参数的一种模板

5、相比于使用 any 类型,使用泛型来创建可复用的组件要更好,因为泛型会保留参数类型
泛型接口 interface GenericIdentityFn
interface GenericIdentityFn<T> {
  (arg: T): T;
}
泛型类
class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
  return x + y;
};
泛型变量
1、对刚接触 TypeScript 泛型,看到 T 和 E,还有 K 和 V 这些泛型变量时,估计会一脸懵逼

2、其实这些大写字母并没有什么本质的区别,只不过是一个约定好的规范而已

3、也就是说使用大写字母 A-Z 定义的类型变量都属于 泛型,把 T 换成 A,也是一样的

4、下面介绍一下一些常见泛型变量代表的意思

   1、T(Type):表示一个 TypeScript 类型
   
   2、K(Key):表示对象中的键类型
   
   3、V(Value):表示对象中的值类型
   
   4、E(Element):表示元素类型
泛型工具类型
1、为了方便开发者 TypeScript 内置了一些常用的工具类型,比如 Partial、Required、Readonly、Record 和 ReturnType 等

2、要使用 TypeScript 泛型工具类型 需要掌握一些相关的基础知识

  1、typeof
  
  2、keyof
  
  3、in
  
  4、infer
  
  5、extends
  
3、最后介绍 Partial 工具类型
typeof
在 TypeScript 中,typeof 操作符可以用来获取一个 变量声明 或 对象 的类型

interface Person {
  name: string;
  age: number;
}

const sem: Person = { name: 'semlinker', age: 30 };
type Sem= typeof sem; // -> Person

function toArray(x: number): Array<number> {
  return [x];
}

type Func = typeof toArray; // -> (x: number) => number[]
keyof
keyof 操作符可以用来遍历一个对象中的所有 key 值

interface Person {
    name: string;
    age: number;
}

type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join" 
type K3 = keyof { [x: string]: Person };  // string | number
in
in 用来遍历枚举类型

type Keys = "a" | "b" | "c"

type Obj =  {
  [p in Keys]: any
} // -> { a: any, b: any, c: any }
infer
在条件类型语句中,可以用 infer 声明一个类型变量并且对它进行使用

type ReturnType<T> = T extends (
  ...args: any[]
) => infer R ? R : any;

以上代码中 infer R 就是声明一个变量来承载传入函数签名的返回值类型,简单说就是用它取到函数返回值的类型方便之后使用
extends
有时候我们定义的泛型不想过于灵活或者说想继承某些类等,可以通过 extends 关键字添加泛型约束

interface ILengthwise {
  length: number;
}

function loggingIdentity<T extends ILengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

现在这个泛型函数被定义了约束,因此它不再是适用于任意类型

loggingIdentity(3);  // Error, number doesn't have a .length property

这时我们需要传入符合约束类型的值,必须包含必须的属性

loggingIdentity({length: 10, value: 3})
Partial
Partial<T> 的作用就是将某个类型里的属性全部变为可选项 ?

/**
 * node_modules/typescript/lib/lib.es5.d.ts
 * Make all properties in T optional
 */
type Partial<T> = {
  [P in keyof T]?: T[P];
};

在以上代码中,首先通过 keyof T 拿到 T 的所有属性名,然后使用 in 进行遍历,将值赋给 P,最后通过 T[P] 取得相应的属性值。中间的 ? 号,用于将所有属性变为可选

示例

interface Todo {
  title: string;
  description: string;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
  return { ...todo, ...fieldsToUpdate };
}

const todo1 = {
  title: "organize desk",
  description: "clear clutter",
};

const todo2 = updateTodo(todo1, {
  description: "throw out trash",
})

在上面的 updateTodo 方法中,我们利用 Partial<T> 工具类型,定义 fieldsToUpdate 的类型为 Partial<Todo>,即

{
   title?: string | undefined;
   description?: string | undefined;
}

typeScript 装饰器

装饰器是什么
1、它是一个表达式

2、该表达式被执行后,返回一个函数

3、函数的入参分别为 target、name 和 descriptor

4、执行该函数后,可能返回 descriptor 对象,用于配置 target 对象
装饰器的分类
1、类装饰器 【 Class decorators 】

2、属性装饰器 【 Property decorators 】

3、方法装饰器 【 Method decorators 】

4、参数装饰器 【 Parameter decorators 】
类装饰器
1、类装饰器 声明

declare type ClassDecorator = <TFunction extends Function>(
  target: TFunction
) => TFunction | void;

2、类装饰器 就是用来装饰类的,它接收一个参数

  target: TFunction - 被装饰的类
  
3、示例

function Greeter(target: Function): void {
  target.prototype.greet = function (): void {
    console.log("Hello Semlinker!");
  };
}

@Greeter
class Greeting {
  constructor() {
    // 内部实现
  }
}

let myGreeting = new Greeting();
myGreeting.greet(); // console output: 'Hello Semlinker!';

上面的例子中,我们定义了 Greeter 类装饰器,同时我们使用了 @Greeter 语法糖,来使用装饰器
属性装饰器
1、属性装饰器 声明

declare type PropertyDecorator = (target:Object, 
  propertyKey: string | symbol ) => void;
  
2、属性装饰器用来装饰类的属性,它接收两个参数  

  1、target: Object - 被装饰的类
  
  2、propertyKey: string | symbol - 被装饰类的属性名
  
3、示例

function logProperty(target: any, key: string) {
  delete target[key];

  const backingField = "_" + key;

  Object.defineProperty(target, backingField, {
    writable: true,
    enumerable: true,
    configurable: true
  });

  // property getter
  const getter = function (this: any) {
    const currVal = this[backingField];
    console.log(`Get: ${key} => ${currVal}`);
    return currVal;
  };

  // property setter
  const setter = function (this: any, newVal: any) {
    console.log(`Set: ${key} => ${newVal}`);
    this[backingField] = newVal;
  };

  // Create new property with getter and setter
  Object.defineProperty(target, key, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true
  });
}

class Person { 
  @logProperty
  public name: string;

  constructor(name : string) { 
    this.name = name;
  }
}

const p1 = new Person("semlinker");
p1.name = "kakuqo";

以上代码定义了一个 logProperty 函数,来跟踪用户对属性的操作,当代码成功运行后,在控制台会输出以下结果

Set: name => semlinker
Set: name => kakuqo
方法装饰器
1、方法装饰器 声明

declare type MethodDecorator = <T>(target:Object, propertyKey: string | symbol, 	 	
  descriptor: TypePropertyDescript<T>) => TypedPropertyDescriptor<T> | void;
  
2、方法装饰器 用来装饰类的方法,它接收三个参数  

  1、target: Object - 被装饰的类
  
  2、propertyKey: string | symbol - 方法名
  
  3、descriptor: TypePropertyDescript - 属性描述符
  
3、示例

function LogOutput(tarage: Function, key: string, descriptor: any) {
  let originalMethod = descriptor.value;
  let newMethod = function(...args: any[]): any {
    let result: any = originalMethod.apply(this, args);
    if(!this.loggedOutput) {
      this.loggedOutput = new Array<any>();
    }
    this.loggedOutput.push({
      method: key,
      parameters: args,
      output: result,
      timestamp: new Date()
    });
    return result;
  };
  descriptor.value = newMethod;
}

class Calculator {
  @LogOutput
  double (num: number): number {
    return num * 2;
  }
}

let calc = new Calculator();
calc.double(11);
// console ouput: [{method: "double", output: 22, ...}]
console.log(calc.loggedOutput); 
参数装饰器
1、参数装饰器 声明

declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, 
  parameterIndex: number ) => void
  
2、参数装饰器 是用来装饰函数参数,它接收三个参数  

  1、target: Object - 被装饰的类
  
  2、propertyKey: string | symbol - 方法名
  
  3、parameterIndex: number - 方法中参数的索引值
  
4、示例

function Log(target: Function, key: string, parameterIndex: number) {
  let functionLogged = key || target.prototype.constructor.name;
  console.log(`The parameter in position ${parameterIndex} at ${functionLogged} has
	been decorated`);
}

class Greeter {
  greeting: string;
  constructor(@Log phrase: string) {
	this.greeting = phrase; 
  }
}

// console output: The parameter in position 0 
// at Greeter has been decorated

标签:TypeScript,target,name,any,typeScript,泛型,装饰,string
From: https://www.cnblogs.com/caix-1987/p/17265527.html

相关文章

  • C#中测试普通方法和对象类型以及泛型所需要的时间
    usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;namespaceMyGeneric{publicclassCo......
  • Java泛型详解
    注:本文转自:https://blog.csdn.net/qq_41701956/article/details/123473592泛型在java中有很重要的地位,无论是开源框架还是JDK源码都能看到它。毫不夸张的说,泛型是通用设......
  • Delphi泛型学习(一)TList<Interger>
    varList:TList;FoundIndex:Integer;begin{CreateanewList.}List:=TList.Create;{Addafewvaluestothelist.}List.AddRange([5,1,8,2,9,14,4,......
  • TypeScript给接口添加任意属性
    一个接口允许存在任意的属性interfaceIPerson{name:string;age:number;userBio?:string;[propName:string]:any;}letwangzz:IPerson={nam......
  • 通过反射获取泛型
    packageedu.wtbu;importjava.lang.reflect.Method;importjava.lang.reflect.ParameterizedType;importjava.lang.reflect.Type;importjava.util.List;importjava.ut......
  • 在Vue3+TypeScript 前端项目中使用事件总线Mitt
    事件总线Mitt使用非常简单,本篇随笔介绍在Vue3+TypeScript前端项目中使用的一些场景和思路。我们在Vue的项目中,经常会通过emits 触发事件来通知组件或者页面进行相应的处......
  • 在基于vue-next-admin的Vue3+TypeScript前端项目中,为了使用方便全局挂载对象接口
    在基于vue-next-admin 的Vue3+TypeScript前端项目中,可以整合自己的.NET后端,前端操作一些功能的时候,为了使用方便全局挂载的对象接口,以便能够快速处理一些特殊的操作,如......
  • Java 泛型
    泛型1.泛型引入不能对加入到集合ArrayList中的数据类型进行约束(不安全)遍历的时候,需要进行类型转换,如果集合中的数据量较大,对效率有影响packagegeneric_;public......
  • 开心的档之TypeScript 变量声明
    变量是一种使用方便的占位符,用于引用计算机内存地址。我们可以把变量看做存储数据的容器。TypeScript变量的命名规则:变量名称可以包含数字和字母。除了下划线_和美元$......
  • 泛型的学习
    泛型的学习一、泛型的引入泛型---泛:宽泛的--不确定的;型:类型---不确定的类型无处不在的调用普通方法的时候,参数类型在声明的时候就确定了,调用按照类型传递参数即可......