Typescript: Hämta nyckel från objekt med indexerad signatur


Jag importerar följande typ och funktion från en TypeScript-modul:

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

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

Jag kan inte ändra koden ovan.

Sedan implementerar jag följande kod i min egen 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
});

Allt fungerar perfekt. Nu vill jag tilldela typen Attributes till konstanten attribute, eftersom jag vill vara säker på att nycklarna är strängar och värdena är siffror. Så jag ändrar min kod:

// 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
});

Jag får följande fel på raden Fn<Keys>({:

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

Jag förstår inte varför typen Keys är lika med string | number, när indexsignaturen specifikt anger att nyckeln är en sträng?

Hur kan jag säkerställa att typen "foo" | "bar" | "baz" skickas som en typargument istället för string | number?

Jag kan leva med den första varianten, men jag förstår inte varför den andra inte fungerar. Någon idé?

Tack så mycket

Svar #1

Hur kan jag säkerställa att typen "foo" | "bar" | "baz" passerar som ett typargument istället för string | number?

Du kan uppnå detta genom att införa en generisk typparameter för 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 kommer att kunna härleda typerna på egen hand, så du kommer inte ens att behöva Type och Keys

Lekplats