typescript - 为什么 typescript mixins 需要一个带有单个 rest 参数 any[] 的构造函数?

标签 typescript webpack typescript-typings

我正在将功能性 mixin 项目从 javascript 迁移到 typescript。我所有的 javascript minxins 都有带有单个参数的构造函数签名 constructor(props){} .
在 typescript 中,我按照 https://www.typescriptlang.org/docs/handbook/mixins.html 的官方文档定义了一个 mixin 构造函数类型。 :

export type ConstrainedMixin<T = {}> = new (...args: any[]) => T;
即使我将该 mixin 签名更改为:
export type ConstrainedMixin<T = {}> = new (props: any) => T;
并更新实现 TSC 将引发错误:

TS2545: A mixin class must have a constructor with a single rest parameter of type 'any[]'.


这是不幸的,因为它不能为传递给构造函数的参数创建唯一的类型签名。我现在还需要迁移所有现有的构造函数。如何为 mixin 构造函数创建更显式的类型接口(interface)?
我创建了一个 playground example
enter image description here
您可以在此屏幕截图中看到 MIXIN 定义上的编译器错误,并说 mixin 类必须有一个 rest 参数。
即使类型定义是:
type ConstrainedMixin<T = {}> = new (props: any) => T;
在示例中,我有 const mg = new MixedGeneric({cool: 'bro'});我想为 {cool: 'bro'} 创建一个接口(interface)对象并从 mixin 定义中强制执行它。我不确定如何正确执行此操作。如果构造函数是 ...args: any[]
更新
听起来这可能是一些反模式,所以这里有进一步的解释。我正在构建一个实体组件系统。在我目前的实现中,我有混合链,例如:
const MixedEntity = RenderMixin(PhysicsMixin(GeometryMixin(Entity));
const entityInstance = new MixedEntity({bunch: "of", props: "that mixins use"});
当最终的 MixedEntity 被实例化时,它会传递一个 props数据包对象。所有 Mixin 在其构造函数中都有自己的初始化逻辑,用于查找 props 上的特定属性。目的。
我以前的 mixin 类有如下构造函数:
constructor(props){
  super(props);
  if(props.Thing) // do props.thing
}
我现在必须将构造函数迁移到:
constructor(...args: any[]){
  const props = args[0]
  super(props);
  if(props.Thing) // do props thing
}   

最佳答案

TS 中的 Mixin 模式旨在使用额外的方法或属性扩展基类,但不会篡改构造函数签名。因此,派生类应该保持其构造函数签名与其扩展的基类相同。
这就是这背后的原因A mixin class must have a constructor with a single rest parameter of type 'any[]'限制 cus TS 不在乎,它只会将构造传递给 super(…args)让它完成工作。
所以如果你想约束构造函数参数,你只需在基类构造函数签名中进行。


type Constructor = new (...args: any[]) => {};
function MixinGeneric<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    // …mixin traits
  }
}

class Generic {
  // ctor param constraint goes here:
  constructor<P extends { cool: string }>(props: P) {}
}

// and it’ll check:
const mg = new MixedGeneric({cool: 'bro'});

更新 响应OP的更新
是的,篡改构造函数签名被认为是一种反模式。但是程序员总是打破规则。只要您了解自己在做什么,就一定要有创意。
查看 Playground .
诀窍是通过使用类型断言和一堆实用程序类型来绕过 TS 限制。
// Utility types:

type GetProps<TBase> = TBase extends new (props: infer P) => any ? P : never
type GetInstance<TBase> = TBase extends new (...args: any[]) => infer I ? I : never
type MergeCtor<A, B> = new (props: GetProps<A> & GetProps<B>) => GetInstance<A> & GetInstance<B>


// Usage:
// bypass the restriction and manually type the signature
function GeometryMixin<TBase extends MixinBase>(Base: TBase) {
  // key 1: assert Base as any to mute the TS error
  const Derived = class Geometry extends (Base as any) {
    shape: 'rectangle' | 'triangle'
    constructor(props: { shape: 'rectangle' | 'triangle' }) {
      super(props)
      this.shape = props.shape
    }
  }

  // key 2: manually cast type to be MergeCtor
  return Derived as MergeCtor<typeof Derived, TBase>
}

关于typescript - 为什么 typescript mixins 需要一个带有单个 rest 参数 any[] 的构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64396668/

相关文章:

typescript - 如何将对象键的值映射到 TypeScript 中的同一对象?

typescript - 如何在 vscode 中全局引用 *.d.ts

angular - AG-Grid:使用 ngx-translate( Angular )翻译 headerName

typescript - 带有 mousedown、mousemove 和 mouseup 的 Cypress .trigger 命令不起作用

node.js - 将我的库分为库、核心和插件

Webpack 在最终包中包含未使用的导出(不是摇树)

javascript - 如果它们的 Prop 不同,如何流动类型组件 Prop 和getDerivedStateFromProps?

javascript - 用于分页的 RXJS while 循环

node.js - 使用 React 进行 Webpack 配置

TypeScript:元组类型到对象类型