TS Everest 003 [Study Notes]

Decorator // Decorator // Can only be used in classes (on the class itself, or class members) // Decorators, class decorators, property decorators, accessor decorators, parameter decorators //
1. Type Decorator: Used to extend a class, or can also return a subclass. //
First, `experimentalDecorators` must be enabled in `tsconfig.json`. `const classDecorator1 =

Decorators

// 装饰器// 只能在类中使用(类本身,类成员使用)// 装饰器,类的装饰器,属性装饰器,访问装饰器 参数装饰器// 1. 类型装饰器 给类进行扩展,也可以返回一个子类// 先要在tsconfig.json中开启experimentalDecoratorsconst classDecorator1 = <T extends new (...args: any[]) => any>(target: T) => {  (target as any).prototype.name = 'hello';  (target as any).prototype.say = function () {    console.log('hello');  };  Object.assign(target.prototype, {    name: 'hello',    drink() {},    eat() {      console.log('hello');    },  });};const classDecorator2 = <T extends new (...args: any[]) => any>(target: T) => {  // 基于父类返回一个子类  return class extends target {    name = 'hello';  };};@classDecorator1class Animal {}@classDecorator2class Person {}// 2. 方法装饰器function Enum(isEnum: boolean) {  return function (    target: any,    propertyKey: string,    descriptor: PropertyDescriptor  ) {    // descriptor.value = isEnum ? 'enum' : 'not enum';    descriptor.enumerable = isEnum;  };}export {};

Decorator Execution Flow

When using it, experimentalDecorators: true must be enabled. Decorators are functions used to operate on classes and members

We have now:
Enabled experimentalDecorators support
Enabled emitDecoratorMetadata support
Set the target to ES5 (decorators work best with this version)
With this configuration, the squiggly line warnings in the IDE should disappear. Decorators can now be correctly used for:
Class decorators (@Echo('class decorator'))
Method decorators (@Echo('prototype method decorator'))
Parameter decorators (@Echo('parameter decorator'))
If you still see squiggly lines, you might need to restart your IDE or reload the TypeScript server.

/*
 * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @Date: 2025-03-22 13:42:13
 * @LastEditors: Walker zw37520@gmail.com
 * @LastEditTime: 2025-03-22 14:22:18
 * @FilePath: /ts-classes/src/index.ts
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
function Echo(val: string) {
  return function (
    target: any,
    propertyKey?: string | symbol,
    descriptorOrIndex?: PropertyDescriptor | number
  ): any {
    if (typeof descriptorOrIndex === 'number') {
      // 参数装饰器
      console.log('参数装饰器:', val, target, propertyKey, descriptorOrIndex);
    } else if (descriptorOrIndex instanceof Object) {
      // 方法装饰器
      console.log('方法装饰器:', val, target, propertyKey, descriptorOrIndex);
    } else {
      // 类装饰器
      console.log('类装饰器:', val, target);
    }
  };
}

@Echo('类的装饰器4')
@Echo('类的装饰器3')
@Echo('类的装饰器2')
@Echo('类的装饰器1')
class Flow {
  // 装饰器是下往上执行
  constructor(@Echo('构造器参数装饰') val: string) {
    console.log('构造器');
  }
  @Echo('原型方法装饰')
  handle(@Echo('原型方法参数装饰') val: string) {
    console.log('方法');
  }
  @Echo('静态属性装饰器')
  static age: number = 18;
  @Echo('静态方法装饰')
  static handle(@Echo('静态方法参数装饰') val: string) {
    console.log('静态方法');
  }
  @Echo('实例属性装饰器')
  name: string = '张三';
  @Echo('实例原型方法')
  handler(@Echo('实例原型方法参数装饰') val: string) {
    console.log('实例原型方法');
  }
}

// 【实例属性、方法、属性访问,定义在前端的先执行】 【静态属性、方法、属性访问,定义在前面的先执行】 【类装饰器】
// 本质 就是一个函数对内容不停的包裹,洋葱模型
// 装饰器一般会搭配反射来使用
// 什么是元数据(描述数据的数据)
// 装饰器是元数据的一种实现
// 反射 需要第三方库来使用reflect-metadata
export {};

Container Dependency Injection (IoC-DI)

// 控制反转与依赖注入
// 什么是控制反转
// 控制反转(Inversion of Control,IoC)是一种设计原则,用于减少类之间的耦合度。它将对象的创建和依赖关系的管理从类内部转移到外部容器或框架中。
// 控制反转的目的是:
// 1. 降低类之间的耦合度
// 2. 提高代码的可维护性和可扩展性
// 3. 实现松耦合的模块化设计

interface IMonitor {
  display(): void;
}

interface IHost {
  run(): void;
}

class Monitor27Inch implements IMonitor {
  display() {
    console.log('27寸显示器');
  }
}
class AppleHost implements IHost {
  run() {
    console.log('苹果主机');
  }
}

// class Computer {
//   public monitor: IMonitor;
//   public host: IHost;
//   constructor(monitor: IMonitor, host: IHost) {
//     this.monitor = monitor;
//     this.host = host;
//   }

//   start() {
//     // 启动
//     this.monitor.display();
//     this.host.run();
//   }
// }

// const computer = new Computer(new Monitor27Inch(), new AppleHost());
// computer.start(); // 控制正转 都写死了

class Computer {
  constructor(public monitor: IMonitor, public host: IHost) {}
  start() {
    this.monitor.display();
    this.host.run();
  }
}

const computer = new Computer(new Monitor27Inch(), new AppleHost());
computer.start(); // 如果有好多参数就会很麻烦了
// 定义一个容器 来管理对象的创建
class Container {
  private binds: Map<string, any> = new Map(); // 存储创建的实例
  private properties: Map<string, any> = new Map(); // 存储注入的属性
  bind<T>(key: string, creater: () => T) {
    if (!this.binds.has(key)) {
      this.binds.set(key, creater());
    }
  }
  get<T>(key: string): T {
    // 将记录的属性自动的注入到当前的实例上
    let instance = this.binds.get(key);
    let properties = this.properties.get(key);
    if (properties) {
      properties.forEach((property) => {
        instance[property] = this.get(property);
      });
    }

    return instance;
  }
}
const container = new Container();
container.bind('monitor', () => new Monitor27Inch());
container.bind('host', () => new AppleHost());
container.bind(
  'computer',
  () => new Computer(container.get('monitor'), container.get('host'))
);

computer.start();

// 注解的写法
interface IMonitor {
  display(): void;
}
interface IHost {
  run(): void;
}

// 提供到容器中,自动会创建实例在容器中
@Provide('Monitor')
class Montior27inch implements IMonitor {
  display() {
    console.log('27寸显示器');
  }
}
@Provide('Host')
class AppleHost implements IHost {
  run() {
    console.log('苹果主机');
  }
}

function Provide(key: string) {
  return (target: any) => {
    container.bind(key, () => new target());
  };
}

function Inject(key: string) {
  return (target: any, propertyKey: string) => {
    // 当前哪个原型上注入了哪些属性,做一个映射关系,稍后解析电脑的时候自动解释它所依赖的属性
    // container.bind(propertyKey, () => container.get(key));
    container.properties.set(
      `${target.prototype.constructor.name}.${propertyKey}`,
      key
    );
    // 关联就是哪个类,对应哪个属性,用哪个哪个标识找到实例来进行赋值
  };
}

// DI 依赖注入, 不需要在类中硬编码
@Provide('Computer')
class Computer {
  // 注入进来
  @Inject('Monitor')
  monitor: IMonitor;
  @Inject('Host')
  host: IHost;

  start() {
    this.monitor.display();
  }
}

export {};

Simulating a NestJS Route

// nestjs 的依赖注入 @Controller('articles'/add)
// articles 是路由,add 是方法
import 'reflect-metadata';

// 定义装饰器工厂函数
function methodDecorator(method: string) {
  return function (path: string) {
    return function (target: any, propertyKey: string, descriptor: any) {
      Reflect.defineMetadata('method', method, descriptor.value);
      Reflect.defineMetadata('path', path, descriptor.value);
    };
  };
}

// 定义装饰器
export const Post = methodDecorator('post');
export const Get = methodDecorator('get');
export const Delete = methodDecorator('delete');

// 类装饰器
function Controller(path: string = '') {
  return (target: any) => {
    Reflect.defineMetadata('path', path, target); // 给类添加了一个元数据
  };
}

// 使用装饰器
@Controller('/articles')
class ArticlesController {
  @Post('/add')
  addArticle() {
    console.log('add article');
  }
  @Get('/detail')
  getArticle() {
    console.log('get article');
  }
  @Delete('/remove')
  removeArticle() {
    console.log('remove article');
  }
}
// 最终生成一个路由 /articles/add 和 /articles/detail 和 /articles/remove 然后触发对应的逻辑
const controller = new ArticlesController();
function createRoutes(instance: any) {
  const protops: any = Reflect.getPrototypeOf(instance) || {};
  const classPath = Reflect.getMetadata('path', protops.constructor);
  let keys = Reflect.ownKeys(protops!).filter((key) => key !== 'constructor');
  let routes: any[] = [];
  keys.forEach((key) => {
    const path = Reflect.getMetadata('path', protops[key]);
    const method = Reflect.getMetadata('method', protops[key]);
    console.log(path, method);
    routes.push({
      path: `${classPath}${path}`,
      method,
      handler: protops[key],
    });
  });
  return routes;
}
const routes = createRoutes(controller);

console.log(routes);

export {};

Type Guards

// 类型保护
// 通过判断,去识别类型、核心就是进行类型的收窄
function doubel(val: number | string) {
  if (typeof val === 'number') {
    return val * 2;
  } else {
    return val + val;
  }
}

class Person {}
class Dog {}

function getInstance(clazz: new () => Dog | Person) {
  return new clazz();
}

const instance = getInstance(Person);

if (instance instanceof Person) {
  console.log('instance is a Person');
} else {
  console.log('instance is a Dog');
}

interface Bird {
  kind: 'bird';
  fly: string;
}
interface Fish {
  kind: 'fish';
  swim: string;
}

// 类型谓词 自定义的类型保护
function isBird(animal: Bird | Fish): animal is Bird {
  return animal.kind === 'bird';
}

function getAnimal(animal: Bird | Fish) {
  if (isBird(animal)) {
    return animal.fly;
  } else {
    return animal.swim;
  }
}

function ensureArray<T>(value: T | T[]): T[] {
  if (Array.isArray(value)) {
    return value;
  }
  return [value];
}

export {};

Template Literal Types

// 模板字符串类型 类似于js中的es6的模板字符串

// 可以将多个字符串类型进行组装
type name = 'jiangwen';
type age = 30;
type sayName = `handsome ${name} ${age}`; // 就是es6的模板字符,但它产生的是一个类型

// 模板字符串也是具备分发能力的
type Direction = 'top' | 'bottom' | 'right' | 'left';
type AllMargin = `margin-${Direction}`;
type AllPadding = `padding-${Direction}`;

// 也可以使用多对多
// 购物 sku 1.0 2.0 3.0 20.0
type Sku = '1.0' | '2.0' | '3.0';
type Sku2 = 20 | 30 | 40;

type IRL = `${Sku}-${Sku2}`;

// 放到字符串内的东西,需要结束,必须得能转化成字符串
type sayHello<T extends string | boolean | null | undefined | number | bigint> =
  `hello, ${T}`;

type R1 = sayHello<'jiang'>;
type R2 = sayHello<1>;
type R3 = sayHello<number>;

let val: R3 = 'hello 333';
type IFlog = R2 extends R3 ? true : false; // 所有的基础类型的模板字符串都是字面量类型的父类型(类型相同)

// 交对象的属性进行重新命名 {name,age,address} -> {r_name,r_age,r_address}
type Person = {
  name: string;
  age: number;
  address: string;
};

type Rename<T> = {
  [K in keyof T as `r_${string & K}`]: T[K];
};

type R11 = Rename<Person>;
let person: Person = {
  name: 'jiangwen',
  age: 30,
  address: 'beijing',
};
// 字符串可以支持工具类型 UpperCase 大写 LowerCase 小写 Capitalize 首字母大写 Uncapitalize 首字母小写
type WithGetter<T> = {
  [K in keyof T as `get${string & Capitalize<K>}`]: () => T[K];
};
let personGetter: WithGetter<Person> = {
  getName: () => {
    return person.name;
  },
  getAge: () => {
    return person.age;
  },
  getAddress: () => {
    return person.address;
  },
};

type PersonGetter = typeof personGetter;

// 根据模式匹配符来取类型 jinag wen
// infer 可以进行位置推断
type GetNameFirstChar<T> = T extends `${infer First}${infer Rest}`
  ? First
  : never;

type R12 = GetNameFirstChar<'jiangwen'>;

//

export {};

Modules and Namespaces

// 模块和命名空间

// 模块叫外部模块 命名空间叫内部模块
//  目前我们主要采用es6的模块来创建作用域(按照文件来创建作用域) 当我们使用import 和 export 来导入和导出模块时,ts会自动帮我们进行类型推断 将其变成一个模块

// 模块的导出和导入
// 1. 导出
// 2. 导入
// 3. 导出和导入
// 4. 导出和导入
// 打包后会有一些常见的模块规范 esm(es6 module) commonjs 和 umd(universal module definition) amd(asynchronous module definition)
// commonjs 是nodejs的模块规范 它使用require来导入模块 使用module.exports来导出模块
// esm 是es6的模块规范 它使用import来导入模块 使用export来导出模块
// umd 是通用模块定义 它可以在多种环境下使用 包括浏览器和nodejs
// amd 是异步模块定义 它使用define来定义模块 使用require来导入模块
// commonjs 不能转换为 esm 需要使用babel来转换
// commonjs 不能转换为amd 需要使用babel来转换
// 模块不会混用,es6下引用commonjs的模块引用
//在ts语法中有一种模块化方式 (export = | import xx = )
export {};

Type Declarations

主题测试文章,只做测试使用。发布者:Walker,转转请注明出处:https://walker-learn.xyz/archives/4411

(0)
Walker的头像Walker
上一篇 Mar 27, 2025 15:01
下一篇 Mar 27, 2025 15:01

Related Posts

  • Node: In-depth and Accessible (Shensiyuan Education) 003 [Study Notes]

    WebSocket and SSE Overview WebSocket Basics Definition: WebSocket is a full-duplex connection upgraded after an HTTP handshake, allowing clients and servers to push data bidirectionally over the same TCP channel, eliminating the need for repeated polling. Handshake Process: The client initiates an HTTP request with the Upgrade: websocket header; The server responds with 101 Switching Protocols, and both parties agree...

    Personal Nov 24, 2025
    31700
  • From 0 to 1: Implementing Micro-frontend Architecture 001 [Study Notes]

    Micro-frontends, JS isolation, CSS isolation, element isolation, lifecycle, preloading, data communication, application navigation, multi-level nesting. Note: This uses Mermaid's flowchart syntax, which is supported by Markdown renderers such as Typora, VitePress, and some Git platforms. Retained: Host application main-vue3; child applications: child-nuxt2-home, child-vue2-job, child-vu...

    Apr 20, 2025
    1.5K00
  • In-depth Understanding of ES6 001 [Study Notes]

    Block-Level Scope Binding
    Previously, `var` variable declarations, regardless of where they were declared, were considered to be declared at the top of their scope. Since functions are first-class citizens, the typical order was `function functionName()`, followed by `var variable`.

    Block-Level Declarations
    Block-level declarations are used to declare variables that cannot be accessed outside the scope of a specified block. Block-level scope exists in:
    - Inside functions
    - Within blocks (the region between `{` and `}`)

    Temporal Dead Zone
    When the JavaScript engine scans code and finds variable declarations, it either hoists them to the top of the scope...

    Personal Mar 8, 2025
    1.6K00
  • Go Engineer System Course 010 [Study Notes]

    Install Elasticsearch (understand as a database) and Kibana (understand as a connection tool). The versions of ES and Kibana (port 5601) must be consistent.

    Learning Elasticsearch (ES) by comparison with MySQL: Terminology Mapping
    MySQL | Elasticsearch
    database | index (索引)
    table | type (fixed as _doc from 7.x, multiple types completely removed in 8.x...)

    Personal Nov 25, 2025
    32400
  • In-depth Understanding of ES6 005 [Study Notes]

    Destructuring: Making data access more convenient. If you declare variables using `var`, `let`, or `const` with destructuring, you must provide an initializer (i.e., the value on the right side of the equals sign). The following will cause an error:
    // Syntax error `var {tyep,name}`
    // Syntax error `let {type,name}`
    // Syntax error `const {type,name}`
    To assign values to already declared variables using destructuring, consider the following:
    `let node = { type:&qu...`

    Personal Mar 8, 2025
    1.2K00
EN
简体中文 繁體中文 English
欢迎🌹 Coding never stops, keep learning! 💡💻 光临🌹