Typescript 接口可以表达属性的共现约束吗

Can Typescript Interfaces express co-occurrence constraints for properties

在单一的 Typescript 接口或类型定义中是否有标准模式来断言属性是一起出现还是根本不出现?

例如,如果一件商品看起来像这样,则它可能是有效的...

{
  id:"ljklkj",
  spellcheck:true,
  spellcheckModel:"byzantine",
}

...或者这个...

{
  id:"ljklkj",
}

但是,如果任何一个拼写检查属性单独出现,它将无效。

{
  id:"ljklkj",
  spellcheckModel:"byzantine",
}
{
  id:"ljklkj",
  spellcheck:true,
}

单片

当然,可以通过创建数据和拼写检查数据类型或接口来解决上面的简单情况。然而,在我的应用案例中,将有不止一个 'cluster' 的同现属性。为每个共现组合定义一个新类型将导致类型爆炸以表达这种情况。

因此,我将该解决方案称为 'monolithic' 接口。当然可能需要用某种组合形式来定义。

我试过的

我试图在 Typescript 语言参考中找到这样的示例,但不知道该功能可能被称为什么(或者实际上,如果它是一个可以表达的功能),我正在努力。属性可以单独可选,但我看不到表达共现的方式。

相关技术

此处讨论了 XML 数据验证的等效功能... https://www.w3.org/wiki/Co-occurrence_constraints

对于JSON,我了解 Schematron 等模式语言,Json 内容规则能够表达共同约束。

工作示例

如果我想象适用于 Solr 搜索引擎的 HTTP 参数集的共同约束情况的打字稿语法,它可能看起来像这样,表明您可以选择完全满足 Spell 或 Group 参数,或根本不是 - 一个联合,其中每种类型都是可选的(由 ? 表示)...

type SolrPassthru =
  SolrCoreParams & (
    SolrSpellParams? |
    SolrGroupParams?  
  )

这与下面的示例形成对比,我认为这是正确的 Typescript,但需要每组参数中的所有参数。

type SolrCoreParams = {
  defType: SolrDefType,
  boost: SolrBoostType,
}

type SolrSpellParams = {
  spellcheck: "true" | "false",
  "spellcheck.collate": "true" | "false",
  "spellcheck.maxCollationTries": 1,
}

type SolrGroupParams = {
  group: "true" | "false",
  "group.limit": '4'
  "group.sort": 'group_level asc,score desc,published desc,text_sort asc'
  "group.main": 'true'
  "group.field": 'group_uri'
}

type SolrPassthru =
  SolrCoreParams & 
  SolrSpellParams &
  SolrGroupParams

请尝试以下操作。它似乎在正确的地方显示了错误。

type None<T> = {[K in keyof T]?: never}
type EitherOrBoth<T1, T2> = T1 & None<T2> | T2 & None<T1> | T1 & T2

interface Data {
  id: string;
}

interface SpellCheckData {
  spellcheck: boolean,
  spellcheckModel: string,
}

// Two interfaces
var z1: EitherOrBoth<Data, SpellCheckData> = { id: "" };
var z2: EitherOrBoth<Data, SpellCheckData> = { spellcheck: true,  spellcheckModel: 'm'};
var z3ERROR: EitherOrBoth<Data, SpellCheckData> = { spellcheck: true};
var z4: EitherOrBoth<Data, SpellCheckData> = { id: "", spellcheck: true,  spellcheckModel: 'm'};

interface MoreData {
  p1: string,
  p2: string,
  p3: string,
}

type Monolith = EitherOrBoth<Data, EitherOrBoth<SpellCheckData, MoreData>>

var x1: Monolith  = { id: "" };
var x2: Monolith  = { spellcheck: true,  spellcheckModel: 'm'};
var x3ERROR: Monolith  = { spellcheck: true};                       
var x4: Monolith  = { id: "", spellcheck: true,  spellcheckModel: 'm'};
var x5ERROR: Monolith  = { p1: ""};                                  
var x6ERROR: Monolith  = { p1: "", p2: ""};
var x7: Monolith  = { p1: "", p2: "", p3: ""};
var x8: Monolith  = { id: "", p1: "", p2: "", p3: ""};
var x9ERROR: Monolith  = { id: "", spellcheck: true, p1: "", p2: "", p3: ""};
var x10: Monolith  = { id: "", spellcheck: true, spellcheckModel: 'm', p1: "", p2: "", p3: ""};

Playground link

更新

如果您更喜欢将类型作为元组传递,您可以使用以下实用程序:

type CombinationOf<T> = T extends [infer U1, infer U2] ? EitherOrBoth<U1, U2> :
                        T extends [infer U1, infer U2, infer U3] ? EitherOrBoth<U1, EitherOrBoth<U2, U3>> :
                        T extends [infer U1, infer U2, infer U3, infer U4] ? EitherOrBoth<U1, EitherOrBoth<U2, EitherOrBoth<U3, U4>>> :
                        never;

type Monolith = CombinationOf<[Data, SpellCheckData, MoreData]>

如果需要一些属性:

type Monolith = Data & CombinationOf<[Data, SpellCheckData, MoreData]>