使用mixin的打字稿钻石

typescript diamond using mixin

我有一组类可以"naively"表示为菱形:

  OneBase
 /      \
OneDer   TwoBase
 \      /
  TwoDer

[注意——由于@mitch 评论已更改名称。]

其实OneTwo就是"base"类,除了OneDer和[=之外还有几个不同的类 19=] 使用相同的模式派生自它们。假设 (OneBase,TwoBase) 在模块 base 中,而 (OneDer, TwoDer) 在模块 derived.

另外 OneBases 包含一个结构 Part,其中包含一个 TwoBase 实例;在 OneDer 中,等效结构应包含一个 TwoDer 实例。

我把TwoBase转换成mixin,下面的代码可以编译:

模块base

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

export class One {
  p: Part
  constructor (readonly a: number) {}
}

export class TwoPre extends One {
  constructor (readonly a: number, readonly c: M[]) {
    super(a)
  }
}
export function TwoMix<
    T extends Constructor<TwoPre>> (Base: T): Constructor<TwoPre> {
  class Two extends Base {

  }
  return Two
}
export const Two = TwoMix(TwoPre)
export interface Part {
  u: typeof Two
}

模块 derived:

import { Part, One, TwoMix } from './base'

export class OneDer extends One {
  p: Part
}

export class TwoDerPre extends OneDer {
  constructor (readonly a: number, readonly c: M[]) {
    super(a)
  }
}

export const TwoDer = TwoMix(TwoDerPre)

请注意,TwoPreTwoDerPre 是必需的,因为 "naive" Two 具有与 One 不同的构造函数签名,并且 mixin 函数无法定义 一个构造函数。这有点痛苦,因为它不必要地增加了原型链——所以变通办法值得赞赏。除此之外,mixin 执行我想要的方法解析顺序。

真正的问题是当我试图强制执行 PartDerPart 的区别时;此版本的模块 derived 无法编译:

import { Part, One, TwoMix } from './base'

export class OneDer extends One {
  p: PartDer
}

export class TwoDerPre extends OneDer {
  constructor (readonly a: number, readonly c: M[]) {
    super(a)
  }
}

export const TwoDer = TwoMix(TwoDerPre)
export interface PartDer extends Part {
  u: typeof TwoDer
}

我收到以下错误: src/derived.ts(13,14): error TS7022: 'TwoDer' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. src/derived.ts(15,3): error TS2502: 'u' is referenced directly or indirectly in its own type annotation.

我想知道为什么编译器在 base 中没有抱怨这个循环,而我却抱怨这个循环,我能做些什么呢?

NOTE 这是我能想到的表达问题的最短方式。当然,TwoDer 真的不仅仅是 mixin 的实例化,所以 derived 末尾的代码看起来更像是:

export class TwoDer extends TwoMix(TwoDerPre) {

}
export interface PartDer extends Part {
  u: TwoDer
}

在这个版本中,我得到的错误略有不同: src/derived.ts(17,6): error TS2304: Cannot find name 'TwoDer'. src/derived.ts(17,6): error TS4033: Property 'u' of exported interface has or is using private name 'TwoDer'. 如果我将 PartDer 转换为使用 typeof TwoDer 我会得到原始错误:

export interface PartDer extends Part {
  u: typeof TwoDer
}

我不太明白你想做什么,但 TypeScript 在这里丢失了一些类型信息:

export const TwoDer = TwoMix(TwoDerPre)

这引起了连锁反应。您可以通过添加一些类型信息来解决这个特定问题:

export const TwoDer: Constructor<TwoPre> = TwoMix(TwoDerPre)

这解决了 'u' is referenced directly or indirectly in its own type annotation. 混乱的问题。我不知道它是否让一切都适合你。