alp123
alp123

Reputation: 189

Typescript: Define if property should be included based on value of other property

I'm using Typescript with React and struggling to get a type correct.. What I am trying to achieve is that for the interface Car the property colorId is required if carColor is 'blue' otherwise it should not be included. Any feedback on how to achieve this?

interface Car {
    carBrand: string;
    carColor: 'black' | 'blue';
    colorId?: string;
}

Upvotes: 0

Views: 50

Answers (2)

Som Shekhar Mukherjee
Som Shekhar Mukherjee

Reputation: 8168

You can use generics and Omit.

Create a BaseCar interface that has carBrand, carColor and carId properties and then create a Car type that conditionally decides the colorId property.

interface BaseCar {
  carBrand: string;
  carColor: "black" | "blue";
  colorId: string;
}

type Car<T> = T extends "black" ? Omit<BaseCar, "colorId"> : BaseCar;

const blueCar: Car<"blue"> = {
  carBrand: "tesla",
  carColor: "blue",
  colorId: "123",
};

const blackCar: Car<"black"> = {
  carBrand: "honda",
  carColor: "black",
};

// @ts-expect-error
const blueCarWithoutId: Car<"blue"> = {
  carBrand: "tesla",
  carColor: "blue",
};

const blackCarWithId: Car<"black"> = {
  carBrand: "honda",
  carColor: "black",
  // @ts-expect-error
  colorId: "123",
};

Upvotes: 3

Ali Habibzadeh
Ali Habibzadeh

Reputation: 11558

type CarColors = "black" | "blue";

// create generic that passed color as asgument
interface Car<C extends CarColors = "black"> {
  carBrand: string;
  carColor: C;
  colorId: string;
}

// create conditional type that omits carColor when color is black
type ColoredCar<C extends CarColors = "black"> = C extends "blue" ? Car<"blue"> : Omit<Car, "carColor">;

// use agrument blue to require color
const myCar: ColoredCar<"blue"> = {
  carBrand: "bmw",
  carColor: "blue",
  colorId: "123"
};

// otherwise it is omitted 
const myCar2: ColoredCar = {
  carBrand: "bmw",
  colorId: "123"
};

Upvotes: 2

Related Questions