我正在将功能性 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
您可以在此屏幕截图中看到 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/