我有一组可以相互计算的属性:
A = B * C
B = A / C
C = A / B
A,B和C都是我的模型的属性,因此有一个函数可以接受缺少其中一个模型的模型,并返回指定了所有三个模型的模型。
因此,存在一个“可解决的”模型的类型概念,该模型缺少0或1个属性,而一个“无法解决的”模型的类型概念缺失了2个或更多属性。也就是说,可解模型是可以通过上述功能之一(或不做任何更改)转换为完整模型的模型。
如何在类型系统中为这些概念建模?到目前为止,我已经尝试使用Partial
或Pick
为每个概念创建单独的类型,但是它非常冗长,我无法使用需要正确编译这些功能的应用程序其他部分。
答案 0 :(得分:2)
我不确定以下内容是否算作“令人难以置信的冗长”(它内部使用Pick<>
,也许吗?),或者是否遇到与您看到的相同的编译问题,但是:
type MissingOneProperty<O extends object> = {
[K in keyof O]: Pick<O, Exclude<keyof O, K>>
}[keyof O];
type MissingAtMostOneProperty<O extends object> =
O | MissingOneProperty<O>;
这个想法是MissingAtMostOneProperty<O>
要么是O
,要么恰好是O
中缺少一个属性。这可能仅适用于没有索引签名的对象类型(您在乎吗?)。
所以,如果我这样定义您的模型:
interface Model {
a: number,
b: number,
c: number
}
我可以声明一个仅接受最多缺少一个属性的模型的函数:
declare function solveModel(
solvableModel: MissingAtMostOneProperty<Model>
): Model;
solveModel({ a: 1, b: 2 }); // okay
solveModel({ b: 2, c: 0.5 }); // okay
solveModel({ a: 1, c: 0.5 }); // okay
solveModel({ a: 1, b: 2, c: 0.5 }); // okay
solveModel({ a: 1 }); // error, property "b" is missing
solveModel({ b: 2 }); // error, property "a" is missing
solveModel({ c: 0.5 }); // error, property "a" is missing
solveModel({}); // error, property "a" is missing
我觉得合理。
要了解其工作原理,让我们来看一下MissingAtMostOneProperty<Model>
的评估结果:
MissingAtMostOneProperty<Model>
根据MissingAtMostOneProperty
的定义变为:
Model | MissingOneProperty<Model>
根据MissingOneProperty
的定义:
Model | {[K in keyof Model]: Pick<Model, Exclude<keyof Model, K>>}[keyof Model]
,即mapping覆盖'a'
的{{1}},'b'
和'c'
属性:
Model
这是通过注意Model | {
a: Pick<Model, Exclude<keyof Model, 'a'>,
b: Pick<Model, Exclude<keyof Model, 'b'>,
c: Pick<Model, Exclude<keyof Model, 'c'>
}[keyof Model]
是keyof Model
和Exclude<T, U>
是conditional type来从联合中删除元素的方法:
'a'|'b'|'c'
通过注意到Model | {
a: Pick<Model, 'b'|'c'>,
b: Pick<Model, 'a'|'c'>,
c: Pick<Model, 'a'|'b'>
}['a'|'b'|'c']
的工作方式将变为:
Pick<>
,最后,通过注意到looking up类型中的属性键的并集与属性类型的并集相同,并根据Model | {
a: { b: number, c: number },
b: { a: number, c: number },
c: { a: number, b: number }
}['a'|'b'|'c']
的定义,它变为:
Model
完成!您可以看到如何最终得到{a: number, b: number, c: number}
| { b: number, c: number }
| { a: number, c: number }
| { a: number, b: number }
的并集以及从Model
删除一个属性的所有方式。
希望能给您一些指导。祝你好运!