'Typescript subset of enum

In Typescript, I have an enum like

export enum CarBrands {
   Toyota = "TOYOTA"
   Ford = "FORD"
   .....
}

I would like to create a subset of this enum like this but cannot seem to do it

enum JapaneseCars {
    CarBrands.Toyota
}

or create an object with a subset

const JapaneseCars = {
    Carbrands.Toyota
}

Is there anyway i can create an object or enum that uses that values from another existing enum?

I cannot change the CarBrands enum to be some other data type



Solution 1:[1]

You can create a type and const combination that will behave like an enum that is the subset of another enum.

Drawback: this solution forces you to have any enum's key match their value (or I have not found a solution that handles that case yet)

// Given an enum
export enum CarBrand {
  TOYOTA = 'TOYOTA',
  FORD = 'FORD',
  PEUGEOT = 'PEUGEOT',
  RENAULT = 'RENAULT'
}

// We can create the type of the subset
export type FrenchCarBrand = Extract<CarBrand, CarBrand.PEUGEOT | CarBrand.RENAULT>;

// And the const that goes with it
export const FrenchCarBrand: Readonly<Record<FrenchCarBrand, FrenchCarBrand>> = {
  [CarBrand.PEUGEOT]: CarBrand.PEUGEOT,
  [CarBrand.RENAULT]: CarBrand.RENAULT
};

// With that you can :

// assign a CarBrand to a FrenchCarBrand ...
const a: FrenchCarBrand = CarBrand.RENAULT;

// ... as long as the CarBrand is in the type
const a_invalid: FrenchCarBrand = CarBrand.TOYOTA; // Type 'CarBrand.TOYOTA' is not assignable to type 'FrenchCarBrand'.

// assign a FrenchCarBrand to a CarBrand ...
const b: CarBrand = FrenchCarBrand.PEUGEOT;

// ... as long as the key exists
const b_invalid: CarBrand = FrenchCarBrand.TOYOTA; // Property 'TOYOTA' does not exist on type 'Readonly<Record<FrenchCarBrand, FrenchCarBrand>>'

// You cannot reassign the value of a FrenchCarBrand ...
FrenchCarBrand.PEUGEOT = CarBrand.RENAULT; // Cannot assign to 'PEUGEOT' because it is a read-only property.

// ... just like you cannot reassign the value of an enum
CarBrand.TOYOTA = 'MAZDA'; // Cannot assign to 'TOYOTA' because it is a read-only property.

If you want to map an unknown value of type CarBrand to a value of type FrenchCarBrand, it will not work though :

declare const brand: CarBrand;
const frenchBrand: FrenchCarBrand = brand; // Type 'CarBrand' is not assignable to type 'FrenchCarBrand'

// You can use the `as` keyword but it will hide bugs, so I do not recommend at all.
const frenchBrandNoError: FrenchCarBrand = brand as FrenchCarBrand; // No error, but a possible bug in your application.

For that you need a type guard :

export const isFrenchCarBrand = (brand: CarBrand): brand is FrenchCarBrand => {
  return brand in FrenchCarBrand;
}

It will allow you to decide what to do in case you encounter an unwanted value

if (isFrenchCarBrand(brand)) {
  // You can safely assume that `brand` is a `FrendCarBrand`
} else {
  // `brand` is definitely not a `FrendCarBrand`
}

I have made a TypeScript Playground that show all this code with type checking in action

Solution 2:[2]

You could make a variable for every single enum member, which would be tedious if you have a lot of enum members but it would look like this.

export enum CarBrands {
    Toyota = "TOYOTA",
    Ford = "FORD"
}

const Toyota = CarBrands.Toyota;
console.log("The Supra is made by", Toyota)

or you can use a simple method

class JapaneseCar {
    make: string;
    // Additional Methods / Properties

    setMake(brand:CarBrands){
        this.make = brand;
    }
}

const ToyotaSupra = new JapaneseCar();
ToyotaSupra.setMake(CarBrands.Toyota);
console.log(ToyotaSupra.make)

Solution 3:[3]

I think you want to make an indexed object with optional values:

enum CarBrand {
  TOYOTA = "TOYOTA",
  FORD = "FORD"
}

type CarsSubset = {
  [key in CarBrand]?: boolean
}

const JapaneseCars: CarsSubset  = {
  TOYOTA: true,
}

For this particular example it may be clearer to just use Set<CarBrand> though:

const JapaneseCars = new Set<CarBrand>([CarBrand.TOYOTA]);

Solution 4:[4]

I think you're looking for something like this -

export enum CarBrands {
  Toyota = "TOYOTA",
  Ford = "FORD"
}

type JapaneseCars = CarBrands.Toyota;

const car: JapaneseCars;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Magador
Solution 2
Solution 3 Nacho Coloma
Solution 4 gatsbyz