typescript - Rozšíriť `Record` o vlastnosť iného typu


V TypeScripte sa zdá, že nasledujúce by malo zabezpečiť vytvorenie požadovaného typu:

interface RecordX extends Record<string, string[]> {
  id: string
}

ale toto sa sťažuje na:

Vlastnosť 'id' typu 'string' nie je priraditeľná k typu indexu reťazca 'string[]'. ts(2411)

Ako možno pridať vlastnosť iného typu k utilitnému typu Record<>?

Detaily a všeobecný prípad + ukážka

Všeobecne, ako by sa dal opísať objekt s fixnými vlastnosťami rôznych typov hodnôt, ale s vlastnosťami rovnakého typu hodnôt, ktoré sa pridávajú dynamicky.

S, napríklad daný tento objekt:

const a = {
   // some properties with known "hard-coded" types
   id: '123',
   count: 123,
   set: new Set<number>(),

   // and some dynamic properties
   dynamicItemList: ['X', 'Y']
   anotherDynamicallyAddedList: ['Y', 'Z']
} as ExtensibleRecord

Ako teda možno definovať typ alebo rozhranie ExtensibleRecord, kde:

  1. typy a kľúče vlastností id, count a set sú pevne dané ako string, number a Set<number>
  2. typy dynamicItemList a anotherDynamicallyAddedList a všetky ďalšie vlastnosti pridané do objektu sú string[]

Skúšal som mnoho variantov, o ktorých by som si myslel, že by mohli fungovať, vrátane:

type ExtensibleRecord = {
  id: string, count: number, set: Set<number>
} & Record<string, string[]>

type ExtensibleRecord = {
  id: string, count: number, set: Set<Number>
} & Omit<Record<string, string[]>, 'id'|'count'|'set'>

interface ExtensibleRecord = {
  id: string,
  count: number,
  set: Set<number>,
  [k: string]: string[]
}

ale každý sa zdá viesť k chybám.

Toto pôsobí ako niečo bežné a zrejmé, ale nemôžem nájsť príklad alebo odkaz.

detské ihrisko

Odpoveď č. 1

Z oficiálnej dokumentácie nie je možné dosiahnuť to, čo chcete:

Hoci indexové podpisy reťazcov sú silným spôsobom, ako popísať vzor „slovníka“, tiež vynucujú, aby všetky vlastnosti zodpovedali svojmu návratovému typu. Je to preto, že index reťazcov deklaruje, že obj.property je tiež dostupné ako obj["property"]. V nasledujúcom príklade typ premennej name nezodpovedá typu indexu reťazca a kontrolór typov zobrazí chybu:

interface NumberDictionary {
    [index: string]: number;
    length: number;    // ok, length is a number
    name: string;      // error, the type of 'name' is not a subtype of the indexer
}

Avšak podľa tohto webu je možné vylúčiť niektoré vlastnosti z indexového podpisu v jednej situácii: keď chcete modelovať deklarovanú premennú. Kopírujem celý úsek zo stránky.

Niekedy je potrebné kombinovať vlastnosti do podpisu indexu. Toto sa neodporúča a mali by ste použiť vzor vnoreného podpisu indexu, ktorý je uvedený vyššie. Ak však modelujete existujúci JavaScript, môžete sa tomu vyhnúť pomocou typu prieniku. Nasledujúci príklad ukazuje chybu, na ktorú natrafíte bez použitia typu prieniku:

type FieldState = {
  value: string
}

type FormState = {
  isValid: boolean  // Error: Does not conform to the index signature
  [fieldName: string]: FieldState
}

Tu je obchádzka pomocou priesečníkového typu:

type FieldState = {
  value: string
}

type FormState =
  { isValid: boolean }
  & { [fieldName: string]: FieldState }

Všimnite si, že aj keď ho môžete deklarovať, aby modeloval existujúci JavaScript, nemôžete vytvoriť takýto objekt pomocou TypeScriptu:

type FieldState = {
  value: string
}

type FormState =
  { isValid: boolean }
  & { [fieldName: string]: FieldState }


// Use it for some JavaScript object you are gettting from somewhere 
declare const foo:FormState; 

const isValidBool = foo.isValid;
const somethingFieldState = foo['something'];

// Using it to create a TypeScript object will not work
const bar: FormState = { // Error `isValid` not assignable to `FieldState
  isValid: false
}

Nakoniec, ako obchádzku, ak to sedí, môžete vytvoriť vnorené rozhranie (skontrolujte sekciu „Dizajnový vzor: Vnorený indexový podpis“ na stránke, v ktorom sú dynamické polia v vlastnosti rozhrania. Napríklad

interface RecordX {
  id: string,
  count: number,
  set: Set<number>,
  dynamicFields: {
    [k: string]: string[]
  }
}

Odpoveď č. 2

Ak to nepoužívate pre class, môžete to popísať pomocou type:

type RecordWithID = Record<string, string[]> & {
  id: string
}

let x: RecordWithID = {} as any

x.id = 'abc'

x.abc = ['some-string']

http://www.typescriptlang.org/play/#code/C4TwDgpgBAShDGB7ATgEwOoEtgAsCSAIlALywIqoA8AzsMpgHYDmANFLfcwNoC6AfFABkUAN4BYAFBQomVAC52dRk0kBfSZIA2EYFAAeCuEjRZchEqNVQAhtRsMQGiXoB0siwHJrAI3genrj7wFlxe1KgAZh48TkA

Súvisiace články