2
votes

It's my first post on Stack Overflow, so i'll try to explain the best I can ! I have this JSON file that is nammed phrases.JSON :

{
  "start": {
    "affirmative": [
      some strings
    ],
    "interrogative": [
     some strings
    ]
  },
  "mid": [
    some strings
  ]
}

So I import it in another file as phrases, with import phrases from '../utils/phrases.json', and declare in modules.d.ts with that.

declare module '*.json' {
  const data: any
  export default data
}

I made an interface in the file where I imported phrases.json, just like that :

interface Phrases {
  [key: string]: TypePhrases | string[]
  start: TypePhrases
  mid: string[]
}
interface TypePhrases {
  [key: string]: string[]
  affirmative: string[]
  interrogative: string[]
}

In my class, I created a function :

private getPhrases(position: string | number) {
    return phrases[position]
  }

So if I call this function in my class, i want to get the start object if I give the string 'start', or the string array if I give 'mid', just like that :

const MID_STRING: string = 'mid'
console.log(this.getPhrases(MID_STRING)[0])

But in my return function, I get this error :

Element implicitly has an 'any' type because expression of type 'string | number' can't be used to index type '{ "start": { "affirmative": string[]; "interrogative": string[]; }; "mid": string[]; }'. No index signature with a parameter of type 'string' was found on type '{ "start": { "affirmative": string[]; "interrogative": string[]; }; "mid": string[]; }'.

Could you help me please ? I tried so much things, I don't know how to resolve it... Thanks !

2
How is phrases defined ?Titian Cernicova-Dragomir
The post is edited with the informationsValentin

2 Answers

4
votes

The type of the import object will be determined by the object in the .json file not by the types you defined (there is no link I can see between the two). Also the definition declare module '*.json' is not what the compiler is using since it finds the file on disk.

The problem you are having is really simple though. Typescript can't prove that phrase is a valid key to index phrases.

You can use type assertions to actually use the types you defined:

private getPhrases(position: string) {
    return (phrases as Phrases)[position]
}

m() {
    const MID_STRING = 'mid'
    var a = this.getPhrases(MID_STRING); // a is TypePhrases | string[]
    if (a instanceof Array) { // must use type guard to index
        a[0]
    }
}

Play

You could also take a better safer option and use keyof coupled with generics to actually get the correct type of the value in the object. This is possible only if you generally use constants.

private getPhrases<K extends keyof typeof phrases>(position: K) {
    return phrases[position]
}

m() {
    const MID_STRING = 'mid' // no string annotation, MID_STRING is typed as 'mid'
    this.getPhrases(MID_STRING)[0]; // ts knows this returns string[] and we can index iderctly into it
}

Play

If you have a string you want to use to index a type you can also assert the string is a keyof the type. This is of course not type safe but sometimes necessary:

private getPhrases(position: string) {
    return phrases[position as keyof typeof phrases]
}
0
votes

The array index should be an integer, not a string

private getPhrases(phrase:string, position: number) {
    return phrase[position]
  }

and

const MID_STRING: string = 'mid'
console.log(this.getPhrases(MID_STRING,0))