Element implicitly has an 'any' type because expression of type 'string' can't be used to index

后端 未结 9 1491
滥情空心
滥情空心 2020-11-27 15:42

Trying out TypeScript for a React project and I\'m stuck on this error:

Element implicitly has an \'any\' type because expression of type \'string\' can\'t b         


        
相关标签:
9条回答
  • 2020-11-27 16:36
    // bad
    const _getKeyValue = (key: string) => (obj: object) => obj[key];
    
    // better
    const _getKeyValue_ = (key: string) => (obj: Record<string, any>) => obj[key];
    
    // best
    const getKeyValue = <T extends object, U extends keyof T>(key: U) => (obj: T) =>
      obj[key];
    

    Bad - the reason for the error is the object type is just an empty object by default. Therefore it isn't possible to use a string type to index {}.

    Better - the reason the error disappears is because now we are telling the compiler the obj argument will be a collection of string/value (string/any) pairs. However, we are using the any type, so we can do better.

    Best - T extends empty object. U extends the keys of T. Therefore U will always exist on T, therefore it can be used as a look up value.

    Here is a full example:

    I have switched the order of the generics (U extends keyof T now comes before T extends object) to highlight that order of generics is not important and you should select an order that makes the most sense for your function.

    const getKeyValue = <U extends keyof T, T extends object>(key: U) => (obj: T) =>
      obj[key];
    
    interface User {
      name: string;
      age: number;
    }
    
    const user: User = {
      name: "John Smith",
      age: 20
    };
    
    const getUserName = getKeyValue<keyof User, User>("name")(user);
    
    // => 'John Smith'
    

    Alternative Syntax

    const getKeyValue = <T, K extends keyof T>(obj: T, key: K): T[K] => obj[key];
    
    0 讨论(0)
  • 2020-11-27 16:42

    This happens because you try to access plotOptions property using string name. TypeScript understands that name may have any value, not only property name from plotOptions. So TypeScript requires to add index signature to plotOptions, so it knows that you can use any property name in plotOptions. But I suggest to change type of name, so it can only be one of plotOptions properties.

    interface trainInfo {
        name: keyof typeof plotOptions;
        x: Array<number>;
        y: Array<number>;
        type: string;
        mode: string;
    }
    

    Now you'll be able to use only property names that exist in plotOptions.

    You also have to slightly change your code.

    First assign array to some temp variable, so TS knows array type:

    const plotDataTemp: Array<trainInfo> = [
        {
          name: "train_1",
          x: data.filtrationData.map((i: any) => i["1-CumVol"]),
          y: data.filtrationData.map((i: any) => i["1-PressureA"]),
          type: "scatter",
          mode: "lines"
        },
        // ...
    }
    

    Then filter:

    const plotData = plotDataTemp.filter(({ name }) => plotOptions[name]);
    

    If you're getting data from API and have no way to type check props at compile time the only way is to add index signature to your plotOptions:

    type tplotOptions = {
        [key: string]: boolean
    }
    
    const plotOptions: tplotOptions = {
        train_1: true,
        train_2: true,
        train_3: true,
        train_4: true
    }
    
    0 讨论(0)
  • 2020-11-27 16:42

    I use this:

    interface IObjectKeys {
      [key: string]: string | number;
    }
    
    interface IDevice extends IObjectKeys {
      id: number;
      room_id: number;
      name: string;
      type: string;
      description: string;
    }
    
    0 讨论(0)
提交回复
热议问题