我有一个Chart类,它有几个子类(BarChart,TimeseriesChart.)它扩展了Chart。我使用一个名为buildChart的方法来构建这些图表。它使用Map将枚举ChartsEnum (例如,stackedTimeseries或barChart)映射到正确的类:
export function buildChart(type: Charts, data: Array<TimeseriesData>) {
var chartsMap = new Map<Charts, InstantiableAbstractClass<typeof Chart>([
[Charts.stackedTimeseries, TimeseriesChart],
[Charts.barChart, BarChart],
])
const chart = chartsMap.get(type)
return new chart(data).chart;
}InstantiableAbstractClass类型如下所示:
export declare type InstantiableClass<T> = (new ( ...args: any) => { [x: string]: any }) & T;例如,TimeseriesChart的类和构造函数如下所示:
export class TimeseriesChart extends Chart{
constructor(data: Array<TimeseriesData>) {
super(data);
}
}现在,我想向chart-class添加第二个属性,名为options (在现有属性data旁边)。现在的问题是,options需要针对每个ChartType (BarChart,TimeseriesChart)不同的属性。例如,BarChart需要以下属性:
{
start: number;
end?: number;
}TimeseriesChart需要这样的类型:
{
description: string;
}然后,TimeseriesChart的构造函数将如下所示:
export class TimeseriesChart extends Chart{
constructor(data: Array<TimeseriesData>, options: TimeseriesChartOptions) {
super(data, options);
}
}这意味着方法buildChart需要一个新的参数options,然后这个参数options可以传递给特定的类(就像参数data所做的那样)。
做这件事最好的方法是什么?我考虑过使用泛型,然后为n个子类为options定义n种类型,但我想不出如何正确地更改InstantiableAbstractClass类型。
您可以找到一个完整的示例,其中包含一些描述这里。
我真的很感激你的帮助!如果您需要更多的信息,我很乐意提供他们。
谢谢你和所有最棒的卢卡斯
发布于 2021-10-30 20:19:52
首先,您似乎希望您的Chart类是abstract,因为您只打算使用它的具体子类,如果没有重写,您希望抛出buildChart()方法。我还将使用参数性质作为声明字段和idential构造函数参数的缩写,并将后者分配给前者。我在添加一个options参数类型,因为我们实际上不需要在基类中像这样检查任何类型。
abstract class Chart {
constructor(protected data: Array<TimeseriesData>, protected options: any) { }
abstract buildChart(): void;
}无论如何,对于子类,我们将通过使用options将属性修饰符属性缩小到适当的类型,并在构造函数参数中指定该类型:
interface BarChartOptions {
start: number,
end: number
}
class BarChart extends Chart {
declare options: BarChartOptions;
constructor(data: Array<TimeseriesData>, options: BarChartOptions) {
super(data, options);
}
buildChart() { return {}; }
protected getChartData() { }
}
interface TimeseriesChartOptions {
description: string
}
class TimeseriesChart extends Chart {
declare options: TimeseriesChartOptions;
constructor(data: Array<TimeseriesData>, options: TimeseriesChartOptions) {
super(data, options);
}
buildChart() { return { }; }
protected getChartData() { }
}现在,我们需要表达Charts枚举和不同子类之间的关系。让我们让一个包含枚举的chartConstructors对象作为键,构造函数作为值:
const chartConstructors = {
[Charts.stackedTimeseries]: TimeseriesChart,
[Charts.barChart]: BarChart
}您应该为您所关心的Chart的每个子类添加一个条目。
最后,我们将编写您的buildChart()函数。让我们做一些助手类型:
type ChartConstructorParameters<C extends Charts> =
ConstructorParameters<typeof chartConstructors[C]>;
type ChartInstance<C extends Charts> =
InstanceType<typeof chartConstructors[C]>;
type ChartConstructor<C extends Charts> =
new (...args: ChartConstructorParameters<C>) => ChartInstance<C>;ChartConstructorParameters<C>类型将接受枚举成员作为C,并使用实用程序类型将其转换为相关的类构造函数的元组论点。
ChartInstance<C>类型使用实用程序类型对相关类构造函数的实例类型执行相同的操作。
最后,ChartConstructor<C>表示该类的相关构造器签名。
现在是buildChart()
function buildChart<C extends Charts>(
type: C, ...ctorArgs: ChartConstructorParameters<C>
) {
const chartConstructor = chartConstructors[type] as ChartConstructor<C>;
return new chartConstructor(...ctorArgs);
}它是一个属函数,它接受与枚举成员对应的泛型类型C的type参数。对于其余的参数,它接受相关类的构造函数参数。这将是一对data和options,用于BarChart和TimeseriesChart,但是如果添加带有其他构造函数参数的子类,它将接受这些子类。
输出是类的一个实例,它通过从chartConstructors获取相关的构造函数来构造这个类(我们需要使用类型断言来使编译器确信chartConstructors[type]实际上是ChartConstructor<C>类型;这是编译器无法看到的,因为它无法对未指定的泛型(如C)进行过多的推理)。
那么它起作用了吗?
const barChart = buildChart(Charts.barChart, [], { start: 1, end: 2 });
// const barChart: BarChart
const timeSeriesChart = buildChart(Charts.stackedTimeseries, [], { description: "" });
// const timeSeriesChart: TimeseriesChart是的,看上去不错!
https://stackoverflow.com/questions/69738102
复制相似问题