如何在Typescript中为相互关联的属性建模

时间:2018-11-18 19:19:09

标签: typescript types

我有一组可以相互计算的属性:

 A = B * C
 B = A / C
 C = A / B

A,B和C都是我的模型的属性,因此有一个函数可以接受缺少其中一个模型的模型,并返回指定了所有三个模型的模型。

因此,存在一个“可解决的”模型的类型概念,该模型缺少0或1个属性,而一个“无法解决的”模型的类型概念缺失了2个或更多属性。也就是说,可解模型是可以通过上述功能之一(或不做任何更改)转换为完整模型的模型。

如何在类型系统中为这些概念建模?到目前为止,我已经尝试使用PartialPick为每个概念创建单独的类型,但是它非常冗长,我无法使用需要正确编译这些功能的应用程序其他部分。

1 个答案:

答案 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 ModelExclude<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删除一个属性的所有方式。

希望能给您一些指导。祝你好运!