Могут ли интерфейсы машинописного текста выражать ограничения совместного появления для свойств

Aug 17 2020

Есть ли стандартный шаблон в монолитном интерфейсе машинописного текста или определениях типов для утверждения свойств, которые либо отображаются вместе, либо не отображаются вообще?

Например, предмет может быть действительным, если он выглядит так ...

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

...или это...

{
  id:"ljklkj",
}

Однако было бы неверно, если бы любое из свойств проверки орфографии выполнялось изолированно.

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

Монолитный

Конечно, простой случай, описанный выше, можно решить, создав Data и SpellcheckData Type или Interface. В моем случае, однако, будет несколько «кластеров» сопутствующих свойств. Определение нового типа для каждой комбинации совпадения приведет к взрывному росту типов, чтобы выразить случай.

По этой причине я назвал это решение «монолитным» интерфейсом. Конечно, для его определения может потребоваться некоторая форма композиции.

Что я пробовал

Я пытался найти подобные примеры в справочнике по языку Typescript, но, не зная, как эта функция может вызываться (или действительно, если это функция, которая вообще может быть выражена), я борюсь. Свойства могут быть индивидуально необязательными, но я не вижу способа выразить совместное возникновение.

Связанные технологии

Здесь обсуждается эквивалентная функция проверки данных XML ... https://www.w3.org/wiki/Co-occurrence_constraints

Я понимаю, что для JSON языки схем, такие как Schematron и Json Content Rules, могут выражать сопутствующие ограничения.

Пример работы

Если бы я представил синтаксис машинописного текста для случая совместного ограничения, применяемого к наборам параметров HTTP для поисковой системы Solr, он мог бы выглядеть так, указывая на то, что вы можете выбрать полное соответствие параметрам 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

Ответы

3 Lesiak Aug 17 2020 at 15:45

Пожалуйста, попробуйте следующее. Кажется, он показывает ошибки в правильных местах.

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: ""};

Ссылка на игровую площадку

Обновить

Если вы предпочитаете передавать типы в виде кортежа, вы можете использовать следующую утилиту:

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]>