Le interfacce Can Typescript esprimono vincoli di ricorrenza per le proprietà

Aug 17 2020

Esiste un modello standard all'interno di un'interfaccia dattiloscritta monolitica o definizioni di tipo per affermare che le proprietà appaiono insieme o non appaiono affatto?

Ad esempio un articolo potrebbe essere valido se avesse questo aspetto ...

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

...o questo...

{
  id:"ljklkj",
}

Tuttavia, non sarebbe valido se una delle proprietà del controllo ortografico si verificasse isolatamente.

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

Monolitico

Ovviamente il semplice caso sopra potrebbe essere risolto creando un Data e un SpellcheckData Type o Interface. Nel mio caso di domanda, tuttavia, ci sarà più di un "cluster" di proprietà ricorrenti. Definire un nuovo tipo per ogni combinazione di co-occorrenza porterebbe a un'esplosione di tipi per esprimere il caso.

Per questo motivo ho definito la soluzione un'interfaccia "monolitica". Ovviamente potrebbe essere necessario utilizzare una qualche forma di composizione per definirla.

Quello che ho provato

Ho provato a trovare esempi come questo all'interno del riferimento al linguaggio Typescript, ma senza sapere come potrebbe essere chiamata la funzione (o addirittura se è una caratteristica che può essere espressa del tutto), sto lottando. Le proprietà possono essere individualmente opzionali ma non riesco a vedere un modo per esprimere la co-occorrenza.

Tecnologie correlate

Una funzionalità equivalente per la convalida dei dati XML è discussa qui ... https://www.w3.org/wiki/Co-occurrence_constraints

Per JSON capisco che i linguaggi dello schema come Schematron e Json Content Rules sono in grado di esprimere co-vincoli.

Esempio funzionante

Se dovessi immaginare la sintassi del dattiloscritto per il caso di co-vincolo applicato ai set di parametri HTTP per il motore di ricerca Solr, potrebbe apparire così, indicando che potresti optare per soddisfare pienamente i parametri Spell o Group, o per niente - un'unione in cui ogni tipo è facoltativo (indicato dal ? ) ...

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

Ciò contrasta con l'esempio seguente, che credo sia corretto Typescript, ma richiede TUTTI i parametri da ogni gruppo di parametri.

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

Risposte

3 Lesiak Aug 17 2020 at 15:45

Si prega di provare quanto segue. Sembra che mostri gli errori nei posti corretti.

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

Collegamento al parco giochi

Aggiornare

Se preferisci passare i tipi come tupla, puoi utilizzare la seguente utilità:

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

Se sono richieste alcune proprietà:

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