Typescript: Obține cheia dintr-un obiect cu semnătură indexată


Import următorul tip și funcție dintr-un modul TypeScript:

type Attributes = {
  [key: string]: number;
};

function Fn<KeysOfAttributes extends string>(opts: { attributes: Attributes }): any {
  // ...
}

Nu pot modifica codul de mai sus.

Apoi implementez următorul cod în propriul meu modul:

// variant 1
const attributes = { // this object is hard coded (not dynamically generated)
  foo: 1,
  bar: 2,
  baz: 3
};

type Type = typeof attributes;
type Keys = keyof Type;

Fn<Keys>({
  attributes
});

Totul funcționează perfect. Acum vreau să atribui tipul Attributes constantei attribute, pentru că vreau să fiu sigur că cheile sunt stringuri și valorile sunt numere. Așa că îmi modific codul:

// variant 2
const attributes: Attributes = {
  foo: 1,
  bar: 2,
  baz: 3
};

type Type = typeof attributes;// equals {[key: string]: number;}
type Keys = keyof Type;// equals string | number. Why ?

Fn<Keys>({// Here, I would like Keys to be "foo" | "bar" | "baz", instead I have string | number
  attributes
});

Primesc următoarea eroare pe linia Fn<Keys>({:

Type 'string | number' does not satisfy the constraint 'string'.
Type 'number' is not assignable to type 'string'.ts(2344)

Nu înțeleg de ce tipul Keys este egal cu string | number, când semnătura indexului specifică în mod clar că cheia este un șir de caractere?

Cum pot să mă asigur că tipul "foo" | "bar" | "baz" este transmis ca argument de tip în loc de string | number?

Pot să mă descurc cu prima variantă, dar nu înțeleg de ce a doua nu funcționează. Vreo idee?

Mulțumesc mult

Răspuns #1

Cum pot să mă asigur că tipul „foo” | „bar” | „baz” este trecut ca argument de tip în loc de string | number?

Puteți realiza acest lucru prin introducerea unui parametru de tip generic pentru attributes:

type Attributes = {
  [key: string]: number;
};

function Fn<KeysOfAttributes extends keyof T, T extends Attributes>(opts: { attributes: T }): any {
  // ...
}

const attributes = {
  foo: 1,
  bar: 2,
  baz: 3,
};

Fn({
  attributes
});

Typescript va putea deduce tipurile de unul singur, așa că nici măcar nu vei avea nevoie de Type și Keys

Teren de joacă