ctwheels
ctwheels

Reputation: 22837

How to use enum to determine which item to use from an import in TypeScript?

Overview

We have a module that exports objects from other files.

For simplicity sake, let's call this Vehicle. It exports Car, Boat, and Plane.

We have an enum that corresponds to these vehicles, let's say the following:

enum Vehicles {
    Car,
    Boat,
    Plane,
}

We are looking to use the enum to specify which imported object we should then use.


Question

We're aware that we can do a switch/case, but this becomes unnecessarily long as our enum grows. Some of our classes export dozens of variations of objects that we then use elsewhere (we are unable to use types/interfaces to simplify).

switch(vehicle) {
    case Vehicles.Car: {
        return Car;
    }
    case Vehicles.Boat: {
        return Boat;
    }
    case Vehicles.Plane: {
        return Plane;
    }
}

More info

We are using TypeScript with Svelte. The import is a package (e.g. Google Charts [charts], fortawesome [icons], etc.). We are looking to create a sort of wrapper to easily initialize specific components.

Example:

<script lang="ts">
    import { a, b, c } from x
    const y = () => {
        // logic here
    }
</script>

<y/>

This question seems to be TypeScript-specific, so I've purposely left out svelte tag from my question

Upvotes: 7

Views: 841

Answers (4)

Hassan Naqvi
Hassan Naqvi

Reputation: 1061

My solution is largely inspired by blaumeise20.

Note: React is just used as an example here. Your imports can be anything

Heres what I came up with:

import { Component, useCallback, useEffect } from 'react';

const imports = { Component, useCallback, useEffect } as const;

function getCorrectImport<K extends keyof typeof imports>(key: K): typeof imports[K] {
  return imports[key];
}

You can then call this function like this:

getCorrectImport('Component'); // You should get completion hints and correct typings using this

The Downside is you will need to stuff your imports into an object like I created the imports object.

A cool way if you import everything from a module could be something like this:

import * as React from 'react';

function getCorrectImport<K extends keyof typeof React>(key: K): typeof React[K] {
  return React[key];
}

const correctObject = getCorrectImport('Component');

This can allow you to not create an object but the downside would be you would need to import everything from the module you are importing from. This works because the * as React syntax creates an object for you.

If you can do that, this would be an ideal solution.

Playground link

Upvotes: 0

NBaua
NBaua

Reputation: 684

Why can't you use this one?

enum Vehicle {
Car= 'CAR',
Boat= 'BOAT',
Plane= 'PLANE',

}

Regards, N Baua

Upvotes: -1

blaumeise20
blaumeise20

Reputation: 2220

So I tried some things, here you can see them:


What I originally wanted to do

I tried to assign the classes to the enum properties, but it didn't work. How I imagined it is like this:

enum Vehicle {
    Car = Car,
    Boat = Boat,
    Plane = Plane,
}

But TypeScript gives me an error. You can try it here. Why did I think about that? Because you can assign value to enum properties like this:

enum Char {
    A = 65,
    B = 66,
    C = 67,
    D = 68,
    // ...
}

But as you can see, it doesn't work in your case.


A possible solution

You could just forget using an enum. There is a special feature in TypeScript, as const. It makes an array or object literal constant. That would look somehow like this:

const Vehicle = {
    Car,
    Boat,
    Plane
} as const;

Why don't I use a value? That is called property shorting (or something like that). When you have a variable or some named thing and want to use it with the same property name, you don't have to specify one. Here is the example using the as const version.


What when you want to get the class by an index?

You can use Object.keys to get an array of keys. It look somehow like this:

const index = 1;
const keys = Object.keys(Vehicle);
const vehicle = Vehicle[keys[index]];

I'm not going to add a link here because it is not really important.

Upvotes: 3

Stevie
Stevie

Reputation: 421

You can use a map of

const vehicleComponentsMap: Record<Vehice, SvelteComponent> = {
   [Vehicle.Car]: Car,
   ....
}

And then if you have a convention for the Vehicle implementation files, you can use it to dynamically build this map (by collecting data from files by convention).

Upvotes: 0

Related Questions