如何在 TypeScript 中键入带有类的数组?
Posted
技术标签:
【中文标题】如何在 TypeScript 中键入带有类的数组?【英文标题】:How to type an array with classes in TypeScript? 【发布时间】:2016-10-05 18:17:54 【问题描述】:我有一个应用程序,它通过运行它的方法 .init(params)
来初始化,如下所示:
app.init([TopBar, StatusBar, MainArea]);
其中TopBar
、StatusBar
和MainArea
是类,而不是类的实例。这些类中的每一个都实现了相同的接口IComponent
。
我想在 .init(params)
方法中从传递的类中实例化对象,如下所示:
init(params: IComponent[]): void
params.map(function (component)
let comp = new component();
this.components[comp.constructor.name] = comp;
, this);
问题在于,由于这些不是实例,TypeScript 不知道它们的类型并引发错误:
错误 TS2345:类型参数 '(typeof TopBar | typeof StatusBar | typeof MainArea)[]' 不可分配给类型参数 'IComponent[]'。
如何修复代码,以便将实现某个接口的类数组传递给方法?
【问题讨论】:
为什么不使用工厂函数而不是尝试在类上使用 new? 【参考方案1】:我发现了两种不同的方法可以为这种情况创建类型:
// Interface style:
export default interface IConstructor<T> extends Function
new (...args: any[]): T;
// Union Type style:
export type ConstructorUnion<T> = new(...args : any[]) => T;
这就是IConstructor
类型的外观:
interface IComponent
class TopBar implements IComponent
class StatusBar implements IComponent
class MainArea
class App
public components: [key: string]: IComponent = ;
public init(params: IConstructor<IComponent>[]): void
params.forEach((Component: IConstructor<IComponent>) =>
const comp: IComponent = new Component();
this.components[comp.constructor.name] = comp;
);
const app = new App();
app.init([TopBar, StatusBar, MainArea]);
console.clear();
console.log(app);
代码如下: https://stackblitz.com/edit/how-to-type-an-array-with-classes-in-typescript?file=index.ts
【讨论】:
【参考方案2】:尽管这是一个老问题:这就是你的做法:
interface IComponent something(): void;
class TopBar implements IComponent something() console.log('in TopBar');
class StatusBar implements IComponent something() console.log('in StatusBar');
class MainArea implements IComponent something() console.log('in MainArea');
interface ComponentClass
new(): IComponent;
const components: [name: string]: IComponent = ;
function init(params: ComponentClass[])
params.map((component) =>
let comp = new component();
components[component.name] = comp;
);
init([TopBar, StatusBar, MainArea]);
for (const c in components)
console.log('Component: ' + c);
components[c].something();
【讨论】:
【参考方案3】:Typescript 支持类类型泛型 (TypeScript Docs)。他们的例子是:
function create<T>(c: new(): T; ): T
return new c();
上面写着“将一个类传递到我的create
方法中,该类在构造时将返回我想要的类型 T”。此签名将阻止您尝试传入任何非 T 类型的类类型。
这与我们想要的很接近,我们只需要对其进行调整,使其成为您IComponent
的项目和项目的数组。
public init(components: new(): IComponent;[]): void
// at this point our `components` variable is a collection of
// classes that implement IComponent
// for example, we can just new up the first one;
var firstComponent = new components[0]();
, this);
有了方法签名,我们现在可以像这样使用它了
app.init([TopBar, StatusBar, MainArea]);
我们传入实现IComponent
的类型数组的地方
【讨论】:
【参考方案4】:有一个working typescript playground(运行它以获得结果警报)
我们需要创建一个自定义的type InterfaceComponent
。这将是init()
方法的数组
interface IComponent
class TopBar implements IComponent
class StatusBar implements IComponent
class MainArea implements IComponent
// this is a type we want to be passed into INIT as an array
type InterfaceComponent = (typeof TopBar | typeof StatusBar | typeof MainArea);
class MyClass
components: [key:string] : IComponent = ;
init(params: (InterfaceComponent)[]): void
params.map((component) =>
let comp = new component();
this.components[comp.constructor["name"]] = comp;
, this);
let x = new MyClass();
x.init([TopBar, StatusBar, MainArea])
alert(JSON.stringify(x.components))
查看here
【讨论】:
这是最优雅的解决方案,谢谢!不过,这意味着,如果我有很多组件,比如 100+,我是否必须将它们全部添加到InterfaceComponent
类型中?
好吧,如果不深入研究就很难说......遵循上述方法......我们需要扩展它......这对于 100+ 来说是不行的。一些工厂方法,使用传递的未知类型(不是那么类型安全)会更合适,我会说......var instance = Object.create(objectType.prototype); instance.constructor.apply(instance, constructorParameters);
继承是错误的——InterfaceComponent 需要知道所有实现它的类,这肯定不是一件好事吗?【参考方案5】:
也许您可以将comp
的类型指定为InterfaceComponent
。
var comp: InterfaceComponent = new component();
this.components[comp.constructor.name] = comp;
【讨论】:
我对 TypeScript 做的最后一件事就是将所有内容的类型分配给 any。 如何分配InterfaceComponent【参考方案6】:改用工厂方法。声明有点笨拙,但想法有效:
interface InterfaceComponent
name: string;
class TopBar implements InterfaceComponent
name: string;
class StatusBar implements InterfaceComponent
name: string;
class MainArea implements InterfaceComponent
name: string;
interface InterfaceComponentFactory
create: () => InterfaceComponent;
function init(params: InterfaceComponentFactory[]): void
params.map(function (component)
let comp = component.create();
this.components[comp.name] = comp;
, this);
init([ create: () => new TopBar() , create: () => new StatusBar() , create: () => new MainArea() ]);
【讨论】:
以上是关于如何在 TypeScript 中键入带有类的数组?的主要内容,如果未能解决你的问题,请参考以下文章
如何在带有 TypeScript 的 React-Native 中使用 styled-component 键入无状态功能组件?
如何使用 Typescript 在 mongoose 中正确键入对象 ID 数组
如何使用 Typescript 在 mongoose 中正确键入对象 ID 数组
如何在带有 React 的 Typescript/JSX 中使用带有箭头函数的泛型?
Object.entries() 和 Object.values() 在 WebStorm/PhpStorm 中没有被键入为数组