TS珠峰 003【學習筆記】

裝飾器

// 裝飾器// 只能在類中使用(類本身,類成員使用)// 裝飾器,類的裝飾器,屬性裝飾器,訪問裝飾器 參數裝飾器// 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 {};

裝飾器的執行流程

使用它時要開啟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
上一篇 2026年3月10日 00:00
下一篇 2026年3月8日 15:40

相關推薦

  • 熱愛運動,挑戰極限,擁抱自然

    熱愛 在這個快節奏的時代,我們被工作、生活的壓力所包圍,常常忽略了身體的需求。而運動,不只是一種健身方式,更是一種釋放自我、挑戰極限、與自然共舞的生活態度。無論是滑雪、攀岩、衝浪,還是跑步、騎行、瑜伽,每一種運動都能讓我們找到內心的激情,感受到生命的躍動。 運動是一場自我挑戰。挑戰極限,不只是職業運動員的專屬,而是每一個熱愛運動的人都可以追求的目標。它可…

    個人 2025年2月26日
    1.4K00
  • Go工程師體系課 011【學習筆記】

    查詢的倒排索引 1. 什麼是倒排索引? 倒排索引(Inverted Index)是一種資料結構,用於快速查找包含特定詞彙的文件。它是搜尋引擎的核心技術之一。 1.1 基本概念 正排索引:文件 ID → 文件內容(詞列表) 倒排索引:詞 → 包含該詞的文件 ID 列表 1.2 為什麼叫"倒排"? 倒排索引將傳統的"文件包含哪些詞"的關係倒轉為"詞出現在哪些文件…

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

    改進的陣列功能 new Array() 的怪異行為,當建構函式傳入一個數值型的值,那麼陣列的 length 屬性會被設為該值;如果傳入多個值,此時無論這些值是不是數值型的,都會變為陣列的元素。這個特性令人困惑,你不可能總是注意傳入資料的類型,所以存在一定的風險。 Array.of() 無論傳多少個參數,不存在單一數值的特例(一個參數且數值型),總是返回包含所有參數…

    個人 2025年3月8日
    1.3K00
  • 【開篇】

    我是Walker,生於八十年代初,程式碼與生活的旅者。全端開發工程師,遊走於前端與後端的邊界,執著於技術與藝術的交會點。程式碼,是我編織夢想的語言;專案,是我刻畫未來的畫布。在鍵盤的敲擊聲中,我探索技術的無限可能,讓靈感在程式碼裡永恆綻放。深度咖啡愛好者,迷戀每一杯手沖的詩意與儀式感。在咖啡的醇香與苦澀中,尋找專注與靈感,亦如在開發的世界中追求極致與平衡。騎…

    2025年2月6日 個人
    2.3K00
  • Go工程師體系課 007【學習筆記】

    商品微服務 實體結構說明 本模組包含以下核心實體: 商品(Goods) 商品分類(Category) 品牌(Brands) 輪播圖(Banner) 品牌分類(GoodsCategoryBrand) 1. 商品(Goods) 描述平台中實際展示和銷售的商品資訊。 欄位說明 欄位名 類型 說明 name String 商品名稱,必填 brand Pointer …

    個人 2025年11月25日
    28300
簡體中文 繁體中文 English