Use key value to look up variable when doing import as namespace

175 Views Asked by At

Perhaps what I'm asking to do is impossible, or I'm taking the wrong approach but is it possible to use a string to do a lookup on an imported namespace?

If I have a file: my_file.ts and its contents are many of these:

export const MyThing: CustomType = {
    propertyOne: "name",
    propertyTwo: 2
}
export const AnotherThing: CustomType = {
    propertyOne: "Another",
    propertyTwo: 3
}

Then I have a file that is going to import all these, but needs to find and use them dynamically based on a string:

import * as allthings from "dir/folder/my_file"


function doStuff() {
   let currentThing = allthings['MyThing']; // works
   let name = 'MyThing';
   let currentThing2 = allthings[name]; // doesnt work

}

The error I get here is:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'typeof import("dir/folder/my_file")'. No index signature with a parameter of type 'string' was found on type 'typeof import("dir/folder/my_file")'.

Why does the literal string work but not a variable of type string?

2

There are 2 best solutions below

6
Ruan Mendes On

The literal string doesn't work because someone could reassign any string to your let name variable.

It will work if your string is const. Casting it is possible:

let name = 'MyThing' as const;

But you should just define a const variable which has the same effect. There's no reason to define a variable with let unless you intend to change it.

const name = 'MyThing';

See this TS playground example

If you don't know the key ahead of time, then if all the objects in my_file are of the same type (CustomType), then you can safely use keyof typeof allthings as the type

export const allthings = {
  MyThing: {
    propertyOne: "name",
    propertyTwo: 2
  },
  AnotherThing: {
    propertyOne: "Another",
    propertyTwo: 3
  }
};

type CustomType = typeof allthings[keyof typeof allthings];


function getValue(key: keyof typeof allthings): CustomType {
    return allthings[key]; 
}

See TS example

For these enum like objects, you can use satisfies Record<string, MyType> to enforce they are all of the same type

interface CustomType {
  propertyOne: string,
  propertyTwo: number
}

const allthings = {
  MyThing: {
    propertyOne: "name",
    propertyTwo: 2
  },
  AnotherThing: {
    propertyOne: "Another",
    propertyTwo: 3
  }
} satisfies Record<string, CustomType>;


function getValueFromAllThings(key: keyof typeof allthings): CustomType {
    return allthings[key]; 
}
6
user2616166 On

I reread a similar question here: Element implicitly has an 'any' type because expression of type 'string' can't be used to index

And was finally able to get it to work by doing this:

import * as allthings from "dir/folder/my_file"


function doStuff() {
   let currentThing = allthings['MyThing']; // works
   let name = 'MyThing';
   let currentThing2 = allthings[name as keyof typeof allthings]; // does work

}