簡體   English   中英

在 Typescript 中混合接口

[英]Making a mixin of interfaces in Typescript

我有一個包含 80 多個方法的類,每個方法都接受一個包含一些定義接口的對象。

class Stuff {
   /* many more */
   getAccount(req: IAccount, callback: ICallback) {
      return this._call('getAccount', req, callback);
   }

   getIds(req: IIDs, callback: ICallback) {
      return this._call('getIds', req, callback);
   }
   /* many more */
}

相當“無聊”的東西,因為它只是映射到底層_call方法並使其對每個方法都類型安全。

但有時這些req param 對象由 2 個或更多接口組成,而不是每次出現“尷尬”時都創建另一個接口,如下所示:

export interface ILoled extends IAccount {
   loled: boolean;
}

export interface IRofloled extends ILoled {
   rofled: boolean;
}

class Stuff {
  getLols(req: ILoled){
  }

  getRofls(req: IRofloled){
  }
}

有什么辦法可以把它作為方法參數列表中接口的“內聯”混合? 喜歡(這顯然不起作用):

class Stuff {
  getMoreStuff(req: <{} extends IAccount, ITime>) {
  }
}

是的,你可以, 從 Typescript 1.6 開始 稱為Intersection types ,使用&運算符來組合類型。

function extend<T, U>(first: T, second: U): T & U {
  let result = <T & U> {};
  for (let id in first) {
    result[id] = first[id];
  }

  for (let id in second) {
    if (!result.hasOwnProperty(id)) {
      result[id] = second[id];
    }
  }
  return result;
}

var x = extend({ a: "hello" }, { b: 42 });
x.a; // works
x.b; // works 

有什么辦法可以把它作為方法參數列表中接口的“內聯”混合

否。您不能內聯擴展接口

我不確定這是否有幫助,但是您是否了解 Kotlin 的接口實現委托與by關鍵字?
在 Kotlin 中,您基本上可以創建一個實現一個接口的類,並使用它來將同一接口的實現委托給另一個類,如下所示:

interface Abc {
    val foo: Int
}

class HelloAbc : Abc {
    val foo = 5
}

class MyClass : Abc by HelloAbc() {
    val bar = "Hello world"
}

上面的代碼創建了一個名為Abc的接口、一個實現該接口的類HelloAbc和另一個類MyClass ,它使用HelloAbc來實現Abc

我剛剛意識到它在 TypeScript 中也很有用,我可以想出一個類型安全的混合實現,如下所示:

type UnionToIntersectionUnchecked<T> =
    (T extends any
        ? (k: T) => void
        : never
        ) extends ((k: infer I) => void)
            ? I
            : never

type IsUnion<T> = [T] extends [UnionToIntersectionUnchecked<T>] ? false : true

type UnionToIntersection<T> =
    IsUnion<T> extends true
        ? UnionToIntersectionUnchecked<T>
        : T

function deepCopy<T, U>(target: T, source: U): T & U {

    const chain = []

    for (let proto = source; source !== Object.prototype; source = Object.getPrototypeOf(source))
        chain.unshift(proto)

    for (const proto of chain)
        Object.defineProperties(target, Object.getOwnPropertyDescriptors(proto))

    return target as T & U
}

function mixin<
    TBase extends object,
    T extends object[]
    >(
        baseClass: { new (...args: any[]): TBase },
        ...objects: T
    ): { new (): TBase & UnionToIntersection<T[number]> } {

    const proto = Object.assign(Object.create(baseClass.prototype), ...objects)

    const ctor = (function(this: TBase, ...args: any[]) {

        const thisProto = Object.getPrototypeOf(this)
        const instance = new baseClass(...args)

        Object.setPrototypeOf(instance, deepCopy(proto, thisProto))

        return instance
    }) as any

    Object.setPrototypeOf(ctor, baseClass)

    return ctor
}

下面的演示代碼說明了如何使用這種技術來委托接口實現:

interface Abc {
    readonly foo: number
}

interface Def {
    readonly bar: string
}

class HelloAbc implements Abc {
    readonly foo = 5
}

class HelloDef implements Def {
    readonly bar = "Hello world"
}

class GreetingBase {

    sayHello() {
        console.log("Hello!")
    }
}

class Greeting extends mixin(GreetingBase, new HelloAbc(), new HelloDef()) implements Abc, Def {

    printTest() {
        console.log("test")
    }
}

const o = new Greeting()

o.sayHello()
o.printTest()
console.log(o.foo)
console.log(o.bar)

您甚至可以在操場上對其進行測試。

您會希望在性能關鍵代碼中避免這種情況,因為創建所有這些原型可能很昂貴,但我不希望它對整體產生太大影響。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM