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

Created on 26 Dec 2019  路  10Comments  路  Source: microsoft/TypeScript

TypeScript Version: 3.0.1

Code


import Sequelize from 'sequelize';
import { DbInterface } from'typings/DbInterface';
import { UserFactory } from './User';
import { PostFactory } from './Post';
import { CommentFactory } from './Comment';

export const createModels = (sequelizeConfig: any): DbInterface => {
  const { database, username, password, params } = sequelizeConfig;
  const sequelize = new Sequelize(database, username, password, params);

  const db: DbInterface = {
    sequelize,
    Sequelize,
    Comment: CommentFactory(sequelize, Sequelize),
    Post: PostFactory(sequelize, Sequelize),
    User: UserFactory(sequelize, Sequelize)
  };

  Object.keys(db).forEach(modelName => {
    if (db[modelName].associate) { //Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'DbInterface'. No index signature with a parameter of type 'string' was found on type 'DbInterface'.
      db[modelName].associate(db); //Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'DbInterface'. No index signature with a parameter of type 'string' was found on type 'DbInterface'.
    }
  });

  return db;
};
Question

Most helpful comment

// 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.

All 10 comments

This is a question, not a bug, please use SO for questions. (see this it will help)

im sorry, thanks btw for the tips. im a newbie

// 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.

@alexandermckay What if I have this scenario:

type props = {
  type: string
}

const style = styles[props.type]

const styles = StyleSheet.create({
  base: {
    ...spacing.medium,
    padding: 10,
    textAlign: "center",
    textAlignVertical: "center",
    fontWeight: "bold",
    fontSize: 18,
    height: 50,
  },
  primary: {
    backgroundColor: colors.purple,
    color: colors.white,
  },
  secondary: {
    backgroundColor: "transparent",
    color: colors.purple,
    borderColor: colors.purple,
    borderWidth: 2,
  },
}

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

Same question here, I have

const LEVEL_DEBUG = 1;
const LEVEL_FINE = 2;

const LOG_LEVELS = {
  info: LEVEL_INFO,
  debug: LEVEL_DEBUG,
  fine: LEVEL_FINE,
};

let logLevel: string = environment.logLevel;
if (!logLevel) {
  logLevel = 'info';
}
let _level = LOG_LEVELS[logLevel.toLowerCase()]; // here it complains 

@greenlaw110
Try to get the key value by using @alexandermckay's method:

const LEVEL_DEBUG = 1;
const LEVEL_FINE = 2;


const LOG_LEVELS: {[key:string]: number} = {
  debug: LEVEL_DEBUG,
  fine: LEVEL_FINE,
};

let logLevel:string = 'debug';
if (!logLevel) {
  logLevel = 'info';
}

const getKeyValue = <T extends object, U extends keyof T>(obj: T) => (key: U) =>
  obj[key];
let _level = getKeyValue(LOG_LEVELS)(logLevel);
console.log(_level); // 1

Just found this blog post, which was very helpful, and wanted to paste some code here that I think is slightly more straight forward than the code included here so far. If you have the following object:

const unitsOfTime = {
  millisecond: 1,
  second: 60,
  hour: 60 * 60,
  day: 24 * 60 * 60,
  month: 30 * 24 * 60 * 60,
  year: 365 * 24 * 60 * 60
};

You can declare types for the keys and values in that object like so:

const unitsOfTime: { [unit: string]: number } = {
  millisecond: 1,
  second: 60,
  hour: 60 * 60,
  day: 24 * 60 * 60,
  month: 30 * 24 * 60 * 60,
  year: 365 * 24 * 60 * 60
};

Hey, if I do this my object will have the following shape when I hove over it:

const unitsOfTime: {
    [unit: string]: number;
}

so auto-complete is no longer available, am I missing something?

The workarounds provided here are not recommended for Object.keys. If you really want to iterate over known values on an object, you can't know that the object doesn't have additional properties at runtime. I've provided a TypeScript Playground example that demonstrates the runtime error that can occur.

In addition to the answers, people could also try to use interfaces :)
Here is an example, hope it helps:

export interface IMapping {
  [propertyName: string]: string;
}

const mapping: IMapping = {
  apples: '$5',
  oranges: '$4',
}

// some code

const fruit: string = await getFruitFromInput();
const price: string = mapping[fruit];
return price;
Was this page helpful?
0 / 5 - 0 ratings

Related issues

dlaberge picture dlaberge  路  3Comments

Roam-Cooper picture Roam-Cooper  路  3Comments

uber5001 picture uber5001  路  3Comments

DanielRosenwasser picture DanielRosenwasser  路  3Comments

CyrusNajmabadi picture CyrusNajmabadi  路  3Comments