1
votes

Why do I get this error? How do I give languages an interface when its a object literal in another external module?

Element implicitly has an any type because expression of type string can't be used to index type { en: { title: string; }; es: { title: string; }; fi: { title: string; }; }.

No index signature with a parameter of type string was found on type { en: { title: string; }; es: { title: string; }; fi: { title: string; }; }.ts(7053)

enter image description here

export const languages = {
  en: {
    title: "English"
  },
  es: {
    title: "Español"
  },
  fi: {
    title: "Suomalainen"
  }
}
import React from 'react';
import { languages } from './utils/languageData'

interface IProps {
  lang: string
}

function LanguageSelect(props: IProps) {
  const { lang } = props

  const getText = (langId: string, propVal: string): string => {
    const result = languages[langId][propVal]
    const txt = result ? 'error' : result
    return txt
  }

  return (
    <div className="LanguageSelect" data-test="languageSelect-box">
      <div className="languageArea">{getText(lang, 'title')}</div>
    </div>
  );
}

export default LanguageSelect;
3

3 Answers

1
votes

You should declare it as a dictionary type

interface LangTitle {
    title: string;
}

export const languages: { [id: string] : LangTitle } = {
    en: {
       title: "English"
    },
    es: {
       title: "Español"
    },
    fi: {
       title: "Suomalainen"
    }
};

And access it like this

const result = languages[langId].title;

1
votes

You can export the languages variable like this,

interface Lang {
    [key: string] : {
        [title: string]: string
    }
}


export const languages: Lang = {
    en : {
        title: "English"
    },
    es: {
        title: "Español"
    },
    fi: {
        title: "Suomalainen"
    }
}

Accessing the values like const result = languages[langId][propVal] should not produce the error. Try on typescript playground Playground Link

0
votes

You can make your type check stricter all the way up to the component props.

export const languages = {
  en : {
    title: "English"
  },
  es: {
    title: "Español"
  },
  fi: {
    title: "Suomalainen"
  }
};
export type LanguageKeys = keyof typeof languages;

and then:

    import React from 'react';
    import { languages, LanguageKeys } from './utils/languageData'

    interface IProps {
      lang: LanguageKeys
    }

    function LanguageSelect(props: IProps) {
      const { lang } = props


      const getText = (langId: LanguageKeys, propVal: string): string => {
        const result = languages[langId][propVal]
        const txt = result ? 'error' : result
        return txt
      }


      return (
        <div className="LanguageSelect" data-test="languageSelect-box">
          <div className="languageArea">{getText(lang, 'title')}</div>
        </div>
      );
    }

    export default LanguageSelect;

This way, you wouldn't be able to pass languages that don't exist to the LanguageSelect components and you will have auto-complete and all of that good stuff too.