Can I use a generic type as object index type in TypesScript?

Viewed 147

I have a use case similar to the following:



type StringKeysOf<T> = [{
  [K in keyof T]-?: T[K] extends string ? K : never
}[keyof T]];

const createDictionary = <T>(key: StringKeysOf<T>) => {

  let dictionary: { [key: string]: T } = {};

  const addToDictionary = (value: T) => {
    dictionary = { ...dictionary, [value[key]]: value }
  }

  return { dictionary, addToDictionary }
}


When trying to use the key attribute as an index type, TypeScript complains with the following message: Type 'StringKeysOf<T>' cannot be used as an index type.

I assume this is not possible because T could eventually hold a type which does not have any properties of type string (which I am filtering on StringKeysOf conditional type).

Does anyone knows if there is a way to make this implementation possible?

1 Answers

There are a couple of things you can do to fix this. First, note that StringKeysOf<T> is an Array type. Given the name key and not keys, I assume you mean for the argument to be a single value. You need to remove the []:

type StringKeysOf<T> = {
  [K in keyof T]-?: T[K] extends string ? K : never
}[keyof T];

Then, you need to make sure that your T has the shape that you expect:

T extends { [K in keyof T]: any }

Putting it all together: TS Playground

type StringKeysOf<T> = {
  [K in keyof T]-?: T[K] extends string ? K : never
}[keyof T];

const createDictionary = <T extends { [K in keyof T]: any }>(key: StringKeysOf<T>) => {

  let dictionary: { [key: string]: T } = {};

  const addToDictionary = (value: T) => {
    dictionary = { ...dictionary, [value[key]]: value }
  }

  return { dictionary, addToDictionary }
}
Related