1

In TypeScript, how do I access a member of a namespace without hard-coding the member name?

As an example, I have one externally defined file:

//This file (tsDemoTypes.d.ts) is externally defined
//and CANNOT BE CHANGED, but the exported 'ValidName' union
//is guaranteed to be consistent (so it can e.g. be used in a cast).
export interface Helicopter {
    name: "Helicopter";
    hover: (height: number, spin: boolean) => Promise<void>;
    maxAirspeed: 123;
}
export interface Airplane {
    name: "Airplane";
    barrelRoll: (radius: number) => Promise<void>;
    maxAirspeed: 600;
}
export interface Submarine {
    name: "Submarine";
    dive: (depth: number, twists: number) => Promise<void>;
}
export type ValidName = 'Helicopter' | 'Airplane' | 'Submarine';

and a file which uses that to try to derive and use some related types:

//This file (tsDemo.ts) can be freely changed.
import type * as Movers from './tsDemoTypes';
type Demo = Movers.ValidName; //"Helicopter" | "Airplane" | "Submarine" as expected
type ChopperWrong = Movers['Helicopter']; //Error ts(2709): Cannot use namespace 'Movers' as a type.
type Chopper = Movers.Helicopter; //Works, but only if you know the name when writing this code.
type MoverMap = {
    //Simple example; more complex version adds filtering and type manipulation after this but
    //if this won't work, the more complex version won't either.
    [T in Movers.ValidName] : Movers[T] //Error ts(2709) as above
}

Playground link here.

How would I build up MoverMap or otherwise dynamically access members of this imported namespace?

WBT
  • 2,249
  • 3
  • 28
  • 40
  • 1
    Not possible as long as it's a namespace. – kelsny Sep 07 '22 at 18:22
  • Can the import be done in such a way so that it's not a namespace, without hardcoding all the member names? – WBT Sep 07 '22 at 18:23
  • There is, but you might as well be hard-coding your map (involves importing all members manually), so no. – kelsny Sep 07 '22 at 18:23
  • If you're importing all members manually, you have to know all their names. The question is if there's a way to avoid that. – WBT Sep 07 '22 at 18:25
  • 1
    Well yeah there's no way to import all of them without knowing all of their names, without it being a namespace. Think about it; you are importing all the types. What would you represent these types as? A tuple (`[Member1, Member2, Member3]`)? An object type (`{ Member1: Member1 ... }`)? Currently the only thing that makes sense is to provide a namespace for the types to exist in. There is no way to avoid it that doesn't force you to know all members beforehand. – kelsny Sep 07 '22 at 18:27
  • The object type option would seem to make sense, something similar to [this](https://tsplay.dev/NngZvm). – WBT Sep 07 '22 at 18:33
  • 1
    My point was that none of the representations make sense except for a namespace. An object type *would* be nice, but it'd be undesirable for a lot of people. What I'm trying to say is that there is no way around it that doesn't force you to know all members. – kelsny Sep 07 '22 at 18:36

0 Answers0