我有一个负责生成不同类型图表的服务。
可生成的图表空间具有两个维度chartType
和dataType
,它们都是有限的值集,如下所示:
enum ChartType {
ChartTypeA,
ChartTypeB,
ChartTypeC
}
enum DataType {
DataTypeA,
DataTypeB,
DataTypeC
}
该服务公开了一个公共方法generateChart(chartType: ChartType , dataType: DataType)
,然后根据传入的chartType
调用相关的私有方法。
private方法的确切实现取决于另一个参数dataType
。
到目前为止很好。
以下是我的问题,(ChartType, DataType)
的某些组合是不可能的(即我无法使用ChartTypeA
和DataTypeC
生成图表),这使我对当前的实现方式产生了疑问
有什么更好的方法来组织我的数据,以便编译器可以强制仅将可能的参数对传递给函数?
答案 0 :(得分:1)
您可以对每个可能的有效组合使用重载:
enum ChartType {
ChartTypeA,
ChartTypeB,
ChartTypeC
}
enum DataType {
DataTypeA,
DataTypeB,
DataTypeC
}
function generateChart(chartType: ChartType.ChartTypeA, dataType: DataType.DataTypeA)
function generateChart(chartType: ChartType.ChartTypeC, dataType: DataType.DataTypeC)
function generateChart(chartType: ChartType.ChartTypeB, dataType: DataType.DataTypeB)
function generateChart(chartType: ChartType, dataType: DataType) { // Implementation signature
}
generateChart(ChartType.ChartTypeA, DataType.DataTypeA)
generateChart(ChartType.ChartTypeA, DataType.DataTypeC) // Error
或者我们可以使用映射类型来减少仪式:
interface EnuMap {
[ChartType.ChartTypeA]: DataType.DataTypeA,
[ChartType.ChartTypeB]: DataType.DataTypeB,
[ChartType.ChartTypeC]: DataType.DataTypeC,
}
function generateChart<T extends ChartType>(chartType: T, dataType: EnuMap[T])
function generateChart(chartType: ChartType, dataType: DataType) { // Implementation signature
}
generateChart(ChartType.ChartTypeA, DataType.DataTypeA)
generateChart(ChartType.ChartTypeA, DataType.DataTypeC) // Error
注意:如果我们将接口用于映射类型,则插件可以根据需要扩展该接口,例如,如果该插件广告支持新的类型组合。
修改
如果大多数组合都是可行的,并且应该排除少数组合,我们可以使用其他方法。首先创建一个包含所有可能的参数组合的类型,并使用Exclude
取出不可能的组合:
function generateChart<T extends Excluded>(...a: T)
function generateChart(chartType: ChartType, dataType: DataType) { // Implementation signature
}
type AllCombinations = {
[C in ChartType]: {
[D in DataType]: [C, D]
}
}[ChartType][DataType]
// Exclude unwanted combinations
type Excluded = Exclude<AllCombinations, [ChartType.ChartTypeA, DataType.DataTypeC]>;
generateChart(ChartType.ChartTypeA, DataType.DataTypeA)
generateChart(ChartType.ChartTypeB, DataType.DataTypeA)
generateChart(ChartType.ChartTypeA, DataType.DataTypeC) // Error
通过这种方法,我们在参数名称中会失去一些提示性,并且编译器会建议重载(只是代码完成,它可以按预期工作)
可以使用here中的UnionToIntersection
来构建一种可以更好地使用智能感知并保留参数名称的解决方案。我们首先创建所有可能签名的并集,然后使用UnionToIntersection
创建具有所有重载的函数。
type AllCombinations = {
[C in ChartType]: {
[D in DataType]: [C, D]
}
}[ChartType][DataType]
type Excluded = Exclude<AllCombinations, [ChartType.ChartTypeA, DataType.DataTypeC]>;
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
type SignatureHelper<T> = T extends [infer C, infer D] ? (chartType: C, dataType: D) => void : never;
type GenerateChartType = UnionToIntersection<SignatureHelper<Excluded>>
const generateChart:GenerateChartType = (chartType: ChartType, dataType: DataType) => { // Implementation signature
}
generateChart(ChartType.ChartTypeA, DataType.DataTypeA)
generateChart(ChartType.ChartTypeB, DataType.DataTypeA)
generateChart(ChartType.ChartTypeA, DataType.DataTypeC) // Error