1

I've got this complex object

const classes = {
  Barbarian: {
    armorAndWeoponProficianies: [
      'lightAmour',
      'mediumArmour',
      'Sheilds',
      'simpleWeopons',
      'martialWeopons',
    ],
    class: 'Barbarian',
    description:
      'A fierce warrior of primitive background who can enter a battle rage',
    hitDie: '1d12',
    primaryAbility: 'STR',
    savingThrowProficianies: ['STR', 'CON'],
  },
  Bard: {
    armorAndWeoponProficianies: [
      'lightArmor',
      'simpleWeapons',
      'handCrossbows',
      'longswords',
      'rapiers',
      'shortswords',
    ],
    class: 'Bard',
    description:
      'An inspiring magician whose power echoes the music of creation',
    hitDie: '1d8',
    primaryAbility: 'CHA',
    savingThrowProficianies: ['DEX', 'CHA'],
  },
}

and here I want to use the argument classType as a key but TypeScript doesnt like it

  const hitPointGenerator = (classType: string) => {
    const selectedClass = classes[classType].hitDie;
//                                  ^ Error
    console.log(selectedClass)
  };

and the error i get is

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ Barbarian: { armorAndWeoponProficianies: string[]; class: string; description: string; hitDie: string; primaryAbility: string; savingThrowProficianies: string[]; }; Bard: { armorAndWeoponProficianies: string[]; ... 4 more ...; savingThrowProficianies: string[]; }; ... 9 more ...; Wizzard: { ...; }; }'.
  No index signature with a parameter of type 'string' was found on type '{ Barbarian: { armorAndWeoponProficianies: string[]; class: string; description: string; hitDie: string; primaryAbility: string; savingThrowProficianies: string[]; }; Bard: { armorAndWeoponProficianies: string[]; ... 4 more ...; savingThrowProficianies: string[]; }; ... 9 more ...; Wizzard: { ...; }; }'.ts(7053)

2 Answers 2

3

There are different ways to tackle the problem:

Use correct type

Change the classType parameter to keyof typeof classes

const hitPointGenerator = (classType: keyof typeof classes) => {
    const selectedClass = classes[classType].hitDie;
    console.log(selectedClass)
};

This has the advantage that you cannot accidentially call it with a wrong key, e.g.

hitPointGenerator('Bard'); // OK
hitPointGenerator('Bart'); // compile error - notice the 't' at the end

TypeScript Playground Example

When you hoover the mouse over the classType parameter in typescript-playground you can see the type is "Barbarian" | "Beard": this is a Union Type, in this case a subtype of string (i.e. it is more specific)

Use string index signature

const classes: {
        [key: string]: any;
} = {
  Barbarian: {
  ...

TypeScript Playground Example

With this string index signature, you can add new items dynamically to the classes object:

classes['new'] = {
  ...
}

more info on index-signature

index signature with interface

To also use types for the values, you can define an interface, e.g.:

interface IMember {
  armorAndWeoponProficianies: string[];
  class: string;
  description: string;
  hitDie: string;
  primaryAbility: string;
  savingThrowProficianies: string[];
}

// we use an index signature to tell typescript that keys of type string are allowed
const classes: {
  [key: string]: IMember;
} = {
...
}

TypeScript Playground Example

Cast to any

  const hitPointGenerator3 = (classType: string) => {
    const selectedClass = (classes as any)[classType].hitDie;
//                                  ^ cast
    console.log(selectedClass)
  };

This is just for the sake of completion: I'd avoid this.

TypeScript Playground Example

Disable `noImplicitAny`

Disable the noImplicitAny option in your tsconfig.json - ref

This can be useful when you migrate some javascript code to typescript: I would not use it for new project and try to activate noImplicitAny whenever possible

TypeScript Playground Example

Sign up to request clarification or add additional context in comments.

3 Comments

using the string index signature, how would I avoid using any?
you define an interface, I've updated my answer, see index signature with interface
I've bookmarked your answer I'm going to be coming back to this allot for a while, thank you so much
2

This is happening because there are only two valid keys for classes, but string could be literally any string.

Maybe try changing your function signature to;

const hitPointGenerator = (classType: keyof typeof classes) => {
    const selectedClass = classes[classType].hitDie;
    console.log(selectedClass)
  };

so that only "Barbarian" | "Bard" are valid inputs for classType

1 Comment

ben your solution works great, can you recommend any reading where I can understand this more, I keep coming up against this indexing issue

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.