国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

巧用 TypeScript(二)

bang590 / 2075人閱讀

摘要:這有一些偏方,能讓你順利從遷移至顯式賦值斷言修飾符,即是在類里,明確說明某些屬性存在于類上采用聲明合并形式,多帶帶定義一個,把用擴展的屬性的類型,放入中是的一個提案,它主要用來在聲明的時候添加和讀取元數據。

Decorator

Decorator 早已不是什么新鮮事物。在 TypeScript 1.5 + 的版本中,我們可以利用內置類型 ClassDecoratorPropertyDecoratorMethodDecoratorParameterDecorator 更快書寫 Decorator,如 MethodDecorator

declare type MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor) => TypedPropertyDescriptor | void;

使用時,只需在相應地方加上類型注解,匿名函數的參數類型也就會被自動推導出來了。

function methodDecorator (): MethodDecorator {
  return (target, key, descriptor) => {
    // ...
  };
}

值得一提的是,如果你在 Decorator 給目標類的 prototype 添加屬性時,TypeScript 并不知道這些:

function testAble(): ClassDecorator {
  return target => {
    target.prototype.someValue = true
  }
}

@testAble()
class SomeClass {}

const someClass = new SomeClass()

someClass.someValue() // Error: Property "someValue" does not exist on type "SomeClass".

這很常見,特別是當你想用 Decorator 來擴展一個類時。

GitHub 上有一個關于此問題的 issues,直至目前,也沒有一個合適的方案實現它。其主要問題在于 TypeScript 并不知道目標類是否使用了 Decorator,以及 Decorator 的名稱。從這個 issues 來看,建議的解決辦法是使用 Mixin:

type Constructor = new(...args: any[]) => T

// mixin 函數的聲明,還需要實現
declare function mixin(...MixIns: [Constructor, Constructor]): Constructor;

class MixInClass1 {
    mixinMethod1() {}
}

class MixInClass2 {
    mixinMethod2() {}
}

class Base extends mixin(MixInClass1, MixInClass2) {
    baseMethod() { }
}

const x = new Base();

x.baseMethod(); // OK
x.mixinMethod1(); // OK
x.mixinMethod2(); // OK
x.mixinMethod3(); // Error

當把大量的 JavaScript Decorator 重構為 Mixin 時,這無疑是一件讓人頭大的事情。

這有一些偏方,能讓你順利從 JavaScript 遷移至 TypeScript:

顯式賦值斷言修飾符,即是在類里,明確說明某些屬性存在于類上:

function testAble(): ClassDecorator {
  return target => {
    target.prototype.someValue = true
  }
}

@testAble()
class SomeClass {
  public someValue!: boolean;
}

const someClass = new SomeClass();
someClass.someValue // true

采用聲明合并形式,多帶帶定義一個 interface,把用 Decorator 擴展的屬性的類型,放入 interface 中:

interface SomeClass {
  someValue: boolean;
}

function testAble(): ClassDecorator {
  return target => {
    target.prototype.someValue = true
  }
}

@testAble()
class SomeClass {}

const someClass = new SomeClass();
someClass.someValue // true

Reflect Metadata

Reflect Metadata 是 ES7 的一個提案,它主要用來在聲明的時候添加和讀取元數據。TypeScript 在 1.5+ 的版本已經支持它,你只需要:

npm i reflect-metadata --save

tsconfig.json 里配置 emitDecoratorMetadata 選項。

它具有諸多使用場景。

獲取類型信息

譬如在 vue-property-decorator 6.1 及其以下版本中,通過使用 Reflect.getMetadata API,Prop Decorator 能獲取屬性類型傳至 Vue,簡要代碼如下:

function Prop(): PropertyDecorator {
  return (target, key: string) => {
    const type = Reflect.getMetadata("design:type", target, key);
    console.log(`${key} type: ${type.name}`);
    // other...
  }
}

class SomeClass {
  @Prop()
  public Aprop!: string;
};

運行代碼可在控制臺看到 Aprop type: string。除能獲取屬性類型外,通過 Reflect.getMetadata("design:paramtypes", target, key)Reflect.getMetadata("design:returntype", target, key) 可以分別獲取函數參數類型和返回值類型。

自定義 metadataKey

除能獲取類型信息外,常用于自定義 metadataKey,并在合適的時機獲取它的值,示例如下:

function classDecorator(): ClassDecorator {
  return target => {
    // 在類上定義元數據,key 為 `classMetaData`,value 為 `a`
    Reflect.defineMetadata("classMetaData", "a", target);
  }
}

function methodDecorator(): MethodDecorator {
  return (target, key, descriptor) => {
    // 在類的原型屬性 "someMethod" 上定義元數據,key 為 `methodMetaData`,value 為 `b`
    Reflect.defineMetadata("methodMetaData", "b", target, key);
  }
}

@classDecorator()
class SomeClass {

  @methodDecorator()
  someMethod() {}
};

Reflect.getMetadata("classMetaData", SomeClass);                         // "a"
Reflect.getMetadata("methodMetaData", new SomeClass(), "someMethod");    // "b"
例子 控制反轉和依賴注入

在 Angular 2+ 的版本中,控制反轉與依賴注入便是基于此實現,現在,我們來實現一個簡單版:

type Constructor = new (...args: any[]) => T;

const Injectable = (): ClassDecorator => target => {}

class OtherService {
  a = 1
}

@Injectable()
class TestService {
  constructor(public readonly otherService: OtherService) {}

  testMethod() {
    console.log(this.otherService.a);
  }
}

const Factory = (target: Constructor): T  => {
  // 獲取所有注入的服務
  const providers = Reflect.getMetadata("design:paramtypes", target); // [OtherService]
  const args = providers.map((provider: Constructor) => new provider());
  return new target(...args);
}

Factory(TestService).testMethod()   // 1
Controller 與 Get 的實現

如果你在使用 TypeScript 開發 Node 應用,相信你對 ControllerGetPOST 這些 Decorator,并不陌生:

@Controller("/test")
class SomeClass {

  @Get("/a")
  someGetMethod() {
    return "hello world";
  }

  @Post("/b")
  somePostMethod() {}
};

這些 Decorator 也是基于 Reflect Metadata 實現,不同的是,這次我們將 metadataKey 定義在 descriptorvalue 上:

const METHOD_METADATA = "method";
const PATH_METADATA = "path";

const Controller = (path: string): ClassDecorator => {
  return target => {
    Reflect.defineMetadata(PATH_METADATA, path, target);
  }
}

const createMappingDecorator = (method: string) => (path: string): MethodDecorator => {
  return (target, key, descriptor) => {
    Reflect.defineMetadata(PATH_METADATA, path, descriptor.value);
    Reflect.defineMetadata(METHOD_METADATA, method, descriptor.value);
  }
}

const Get = createMappingDecorator("GET");
const Post = createMappingDecorator("POST");

接著,創建一個函數,映射出 route

function mapRoute(instance: Object) {
  const prototype = Object.getPrototypeOf(instance);
  
  // 篩選出類的 methodName
  const methodsNames = Object.getOwnPropertyNames(prototype)
                              .filter(item => !isConstructor(item) && isFunction(prototype[item]));
  return methodsNames.map(methodName => {
    const fn = prototype[methodName];

    // 取出定義的 metadata
    const route = Reflect.getMetadata(PATH_METADATA, fn);
    const method = Reflect.getMetadata(METHOD_METADATA, fn);
    return {
      route,
      method,
      fn,
      methodName
    }
  })
};

我們可以得到一些有用的信息:

Reflect.getMetadata(PATH_METADATA, SomeClass);  // "/test"

mapRoute(new SomeClass())

/**
 * [{
 *    route: "/a",
 *    method: "GET",
 *    fn: someGetMethod() { ... },
 *    methodName: "someGetMethod"
 *  },{
 *    route: "/b",
 *    method: "POST",
 *    fn: somePostMethod() { ... },
 *    methodName: "somePostMethod"
 * }]
 * 
 */

最后,只需把 route 相關信息綁在 express 或者 koa 上就 ok 了。

至于為什么要定義在 descriptorvalue 上,我們希望 mapRoute 函數的參數是一個實例,而非 class 本身(控制反轉)。

更多

巧用 TypeScript(一)

深入理解 TypeScript

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/98994.html

相關文章

  • 王下邀月熊_Chevalier的前端每周清單系列文章索引

    摘要:感謝王下邀月熊分享的前端每周清單,為方便大家閱讀,特整理一份索引。王下邀月熊大大也于年月日整理了自己的前端每周清單系列,并以年月為單位進行分類,具體內容看這里前端每周清單年度總結與盤點。 感謝 王下邀月熊_Chevalier 分享的前端每周清單,為方便大家閱讀,特整理一份索引。 王下邀月熊大大也于 2018 年 3 月 31 日整理了自己的前端每周清單系列,并以年/月為單位進行分類,具...

    2501207950 評論0 收藏0
  • 【速查手冊】TypeScript 高級類型 cheat sheet

    摘要:官方文檔高級類型優先閱讀,建議閱讀英文文檔。關鍵字這個關鍵字是在版本引入的在條件類型語句中,該關鍵字用于替代手動獲取類型。源碼解釋使用條件判斷完成示例官方作用該類型可以獲得函數的參數類型組成的元組類型。 學習 TypeScript 到一定階段,必須要學會高階類型的使用,否則一些復雜的場景若是用 any 類型來處理的話,也就失去了 TS 類型檢查的意義。 本文羅列了 TypeScript...

    LoftySoul 評論0 收藏0
  • 每日 30 秒 ? 巧用可視區域

    簡介 可視區域、頁面優化、DOM節點多、圖片懶加載、性能 可視區域是一個前端優化經常出現的名詞,不管是顯示器、手機、平板它們的可視區域范圍都是有限。在這個 有限可視區域 區域里做到完美顯示和響應,而在這個區域外少做一些操作來減少渲染的壓力、網絡請求壓力。在 每日 30 秒之 對海量數據進行切割 中的使用場景,我們就是利用了 有限可視區域 只渲染一部分 DOM 節點來減少頁面卡頓。 既然 可視區域 ...

    DevYK 評論0 收藏0
  • 前端每周清單年度總結與盤點

    摘要:前端每周清單年度總結與盤點在過去的八個月中,我幾乎只做了兩件事,工作與整理前端每周清單。本文末尾我會附上清單線索來源與目前共期清單的地址,感謝每一位閱讀鼓勵過的朋友,希望你們能夠繼續支持未來的每周清單。 showImg(https://segmentfault.com/img/remote/1460000010890043); 前端每周清單年度總結與盤點 在過去的八個月中,我幾乎只做了...

    jackwang 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<