简体   繁体   English

在 Typescript 中混合接口

[英]Making a mixin of interfaces in Typescript

I have a class with over 80 methods, and each method accepts an object containing some defined interface.我有一个包含 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 */
}

pretty 'boring' stuff, since it's just mapping to the underlaying _call method and making it type safe for each of the methods.相当“无聊”的东西,因为它只是映射到底层_call方法并使其对每个方法都类型安全。

But sometimes these req param objects are made up from 2 interfaces or more, and instead of creating another interface for each time there's an "awkward", like this:但有时这些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){
  }
}

is there any way I can just put it as an "inline" mixin of interfaces inside the method parameter list?有什么办法可以把它作为方法参数列表中接口的“内联”混合? like (which obviously don't work):喜欢(这显然不起作用):

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

Yes you can, as of Typescript 1.6 .是的,你可以, 从 Typescript 1.6 开始 Called Intersection types , use the & operator to combine types.称为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 

is there any way I can just put it as an "inline" mixin of interfaces inside the method parameter list有什么办法可以把它作为方法参数列表中接口的“内联”混合

No. You cannot extend an interface inline否。您不能内联扩展接口

I'm not sure if this can help, but did you know about Kotlin's interface implementation delegation with the by keyword?我不确定这是否有帮助,但是您是否了解 Kotlin 的接口实现委托与by关键字?
In Kotlin, you can basically create a class implementing one interface and use it to delegate the implementation of the same interface for another class, like this:在 Kotlin 中,您基本上可以创建一个实现一个接口的类,并使用它来将同一接口的实现委托给另一个类,如下所示:

interface Abc {
    val foo: Int
}

class HelloAbc : Abc {
    val foo = 5
}

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

The above code creates an interface named Abc , a class HelloAbc implementing that interface, and one more class, MyClass , which uses HelloAbc to implement Abc .上面的代码创建了一个名为Abc的接口、一个实现该接口的类HelloAbc和另一个类MyClass ,它使用HelloAbc来实现Abc

I just realized it can be useful in TypeScript as well, and I could come up with a type–safe, mixin implementation, which follows:我刚刚意识到它在 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
}

The demo code below illustrates how to use this technique to delegate interface implementation:下面的演示代码说明了如何使用这种技术来委托接口实现:

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)

You can even test it on the playground .您甚至可以在操场上对其进行测试。

You'll want to avoid this in performance–critical code, since creating all these prototypes can be expensive, but I don't expect it to affect that much overall.您会希望在性能关键代码中避免这种情况,因为创建所有这些原型可能很昂贵,但我不希望它对整体产生太大影响。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM