TS珠峰 003【學習筆記】

裝飾器

// 裝飾器
// 只能在類中使用(類本身,類成員使用)
// 裝飾器,類的裝飾器,屬性裝飾器,訪問裝飾器 參數裝飾器

// 1. 類型裝飾器 給類進行擴展,也可以返回一個子類

// 先要在tsconfig.json中開啓experimentalDecorators
const 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';
  };
};

@classDecorator1
class Animal {}

@classDecorator2
class Person {}

// 2. 方法裝飾器

function Enum(isEnum: boolean) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    // descriptor.value = isEnum ? 'enum' : 'not enum';
    descriptor.enumerable = isEnum;
  };
}

export {};

裝飾器的運行流程

使用它時要開啓experimentalDecorators: true, 裝飾器就是一個個函數,用來對類和成員進行操作

現在我們已經:
啓用了 experimentalDecorators 支持
啓用了 emitDecoratorMetadata 支持
設置了目標為 ES5(裝飾器在這個版本上工作得最好)
這樣配置後,IDE 中的波浪線警告應該會消失。裝飾器現在可以正確地用於:
類裝飾器(@Echo('類的裝飾器'))
方法裝飾器(@Echo('原型方法裝飾'))
參數裝飾器(@Echo('參數裝飾'))
如果你仍然看到波浪線,可能需要重啓 IDE 或重新加載 TypeScript 服務器。

/*
 * @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 {};

容器的依賴注入(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 {};

模擬一個nestjs的路由

// 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 {};

類型保護

// 類型保護
// 通過判斷,去識別類型、核心就是進行類型的收窄
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 {};

模板字符串

// 模板字符串類型 類似於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 {};

模塊和命名空間

// 模塊和命名空間

// 模塊叫外部模塊 命名空間叫內部模塊
//  目前我們主要採用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 {};

類型聲明

主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://walker-learn.xyz/archives/4411

(0)
Walker的頭像Walker
上一篇 2025年3月27日 15:01
下一篇 2025年3月27日 15:01

相關推薦

  • Node深入淺出(聖思園教育) 003【學習筆記】

    WebSocket 與 SSE 總覽 WebSocket 基礎 定位:WebSocket 是一條在 HTTP 握手後升級的全雙工連接,允許客戶端與服務器在同一 TCP 通道上雙向推送數據,省去了反復輪詢。 握手流程: 客戶端通過 Upgrade: websocket 頭髮起 HTTP 請求; 服務器響應 101 Switching Protocols,雙方協…

    個人 2025年11月24日
    31400
  • 從0到1落地微前端架構 001【學習筆記】

    微前端 js 隔離css 隔離元素隔離生命週期預加載數據通信應用跳轉多層嵌套 說明 使用的是 Mermaid 的 flowchart 語法,Markdown 渲染器如 Typora、VitePress、一些 Git 平台都支持。 保留了: 基座應用 main-vue3 各子應用:child-nuxt2-home、child-vue2-job、child-vu…

    2025年4月20日
    1.5K00
  • 深入理解ES6 001【學習筆記】

    塊級作用域綁定 之前的變量聲明var無論在哪裡聲明的都被認為是作域頂部聲明的,由於函數是一等公民,所以順序一般是function 函數名()、var 變量。 塊級聲明 塊級聲明用於聲明在指定塊的作用域之外無法訪問的變量。塊級作用域存在於: 函數內部 塊中(字符和{和}之間的區域) 臨時死區 javascript引擎在掃描代碼發現變量聲明時,要麼將它們提升至作…

    個人 2025年3月8日
    1.6K00
  • Go工程師體系課 010【學習筆記】

    es 安裝 elasticsearch(理解為庫) kibana(理解為連接工具)es 和 kibana(5601) 的版本要保持一致 MySQL 對照學習 Elasticsearch(ES) 術語對照 MySQL Elasticsearch database index(索引) table type(7.x 起固定為 _doc,8.x 徹底移除多 type…

    個人 2025年11月25日
    32400
  • 深入理解ES6 005【學習筆記】

    解構:使用數據訪問更便捷 如果使用var、let或const解構聲明變量,則必須要提供初始化程序(也就是等號右側的值)如下會導致錯誤 // 語法錯誤 var {tyep,name} // 語法錯誤 let {type,name} // 語法錯誤 const {type,name} 使用解構給已經聲明的變量賦值,哪下 let node = { type:&qu…

    個人 2025年3月8日
    1.2K00
簡體中文 繁體中文 English
歡迎🌹 Coding never stops, keep learning! 💡💻 光臨🌹