Home > Software design >  Key can't be used to index type when it should
Key can't be used to index type when it should

Time:07-10

I've got the following TS code

type Fruit = { kind: "apple" } | { kind: "grape"; color: "green" | "black" };
type FruitTaste<TFruit extends Fruit> = TFruit["kind"] extends "apple"
  ? "good"
  : TFruit["color"] extends "green"
  ? "good"
  : "bad";

Playground link

It errors at TFruit["color"] with

Type '"color"' cannot be used to index type 'TFruit'.

but it shouldn't, because we're in the ternary side where TFruit should only be restricted to { kind: "grape"; color: "green" | "black" }, and the color key should exist.

Weirdly enough TS doesn't have to have any issue with the "runtime" version of it:

type Fruit = { kind: "apple" } | { kind: "grape"; color: "green" | "black" };
const fruitTaste = (fruit: Fruit) =>
  fruit.kind === "apple" ? "good" : fruit.color === "green" ? "good" : "bad";

Playground link

Why is that? How can I implement the FruitTaste type?

CodePudding user response:

I think you can implement it by seperating the type { kind: "apple" } and { kind: "grape"; color: "green" | "black" }.

type Apple = { kind: "apple" };
type Grape = { kind: "grape"; color: "green" | "black" };
type Fruit = Apple | Grape;
type FruitTaste<TFruit extends Fruit> = TFruit extends Grape
    ? TFruit["color"] extends "green"
        ? "good"
        : "bad"
    : "good";

CodePudding user response:

To the "why" question: TypeScript does not discriminante unions in generic conditionals based on specific properties. TFruit can not be indexed because at the point of this check, TFruit is still a union with two elements of which one does not have a color property.

My workaround would look like this:

type FruitTaste<TFruit extends Fruit> = TFruit extends { kind: "apple" }
  ? "good"
  : (TFruit & { color: string })["color"] extends "green"
    ? "good"
    : "bad";

Before accessing color, we can use an intersection to remind TypeScript that this property should exist.

Playground

  • Related