Oväntat beteende med snittyptyper i Typescript


Jag har försökt bryta ner det enklaste testfallet jag kan av ett verkligt problem jag har. Jag försöker bygga upp några användbara typobjekt, men jag hindras av ett fel som jag egentligen inte förstår. I nedanstående exempel typkontrollerar palette3 och palette4, men jag måste vara mycket mer explicit än jag skulle vilja, och jag skulle vilja att palette2 typkontrollerar. Finns det några enkla justeringar jag kan göra för att få palette2 att fungera?

enum Color {
  Red,
  White,
  Blue,
  Green,
  Black,
}

interface Attr {
  foo: unknown;
  bar: unknown;
  baz: unknown;
};

type WhichColors = Attr & Record<string, Color >;
const palette1 : WhichColors = { foo: Color.Blue, bar: Color.Green, baz: Color.Black };

type Monochrome = WhichColors & Record<string, Color.White | Color.Black>;

// Doesn't type-check
const palette2: Monochrome = {
  foo: Color.Black,
  bar: Color.White,
  baz: Color.Black
};

// Typechecks but clunky
const palette3: Monochrome = {
  foo: Color.Black as Color.White | Color.Black,
  bar: Color.White as Color.White | Color.Black,
  baz: Color.Black as Color.White | Color.Black,
};

// Also typechecks but also clunky
const palette4 = {
  foo: Color.Black,
  bar: Color.White,
  baz: Color.Black,
} as Monochrome;

Svar #1

Du bör använda keyof Attr istället för string för att ta bort behovet av snittet:

type WhichColors = Record<keyof Attr, Color>;

Lekplats (ändrade namn från Attr till Attributes eftersom det krockar med Attr från DOM API)


Din ursprungliga lösning fungerade inte, eftersom snittet faktiskt resulterar i detta:

type WhichColors = {
    [x: string]: Color;
    foo: unknown;
    bar: unknown;
    baz: unknown;
};

vilket du kan se om du använder detta "knep" för att förenkla typen:

type WhichColors = (Attributes & Record<string, Color>) extends infer O ? ({ [K in keyof O]: O[K] }) : never;