kevstev01
kevstev01

Reputation: 340

Typescript object variable reference

I have defined a static object :

const instruments = {
    "guitar": {
        tunings: ["E","A","D","G","B","E"]
    },
    "ukulele": {
        tunings: ["G","C","E","A"]
    },
    "baritone": {
        tunings: ["D","G","B","E"]
    },
    "mandolin": {
        tunings: ["G","G","D","D","A","A","E","E"]
    },
   "bass": {
        tunings: ["E","A","D","G"]
    }
}

and want to call a function with the name of the instrument I am referencing to obtain appropriate 'tunings' array :

    constructor(canvas : HTMLCanvasElement, tuningName : string, startFret = 0, noFrets : number) : any {
        const tuningsArr = instruments[tuningName].tunings;
        ...
}

however VSCode tells me that's not valid Typescript. How can I achieve getting a reference to the correct tunings array from the passed string?

Upvotes: 0

Views: 1144

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1075219

There are a couple of ways to do that.

You can use the type inferred for the instruments const, but to do that, you have to tell TypeScript that the state of the instruments object is constant using as const:

const instruments = {
    "guitar": {
        tunings: ["E","A","D","G","B","E"]
    },
    "ukulele": {
        tunings: ["G","C","E","A"]
    },
    "baritone": {
        tunings: ["D","G","B","E"]
    },
    "mandolin": {
        tunings: ["G","G","D","D","A","A","E","E"]
    },
   "bass": {
        tunings: ["E","A","D","G"]
    }
} as const;
// ^^^^^^^

Then, constrain the type of tuningName to keyof typeof instruments (a valid key for the type that instruments has), requiring that it be a valid key for that (now constant) object:

class Example {
    constructor(canvas : HTMLCanvasElement, tuningName : keyof typeof instruments, startFret = 0, noFrets : number) : any {
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^^^^^^
        const tuningsArr = instruments[tuningName].tunings;
        // ...
    }
}

Playground link

Alternatively, you could declare types explicitly and assign instruments the type for instruments:

interface Tuning {
    tunings: string[];   // You could even nail this type down more if you wanted
}
interface Instruments {
    guitar: Tuning;
    ukulele: Tuning;
    baritone: Tuning;
    mandolin: Tuning;
    bass: Tuning;
}
const instruments: Instruments = {
    "guitar": {
        tunings: ["E","A","D","G","B","E"]
    },
    "ukulele": {
        tunings: ["G","C","E","A"]
    },
    "baritone": {
        tunings: ["D","G","B","E"]
    },
    "mandolin": {
        tunings: ["G","G","D","D","A","A","E","E"]
    },
   "bass": {
        tunings: ["E","A","D","G"]
    }
};

Then it's keyof Instruments rather than keyof typeof instruments:

class Example {
    constructor(canvas : HTMLCanvasElement, */tuningName : keyof Instruments, startFret = 0, noFrets : number) : any {
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^
        const tuningsArr = instruments[tuningName].tunings;
        // ...
    }
}

Playground link

Upvotes: 1

Related Questions