1

I have some generic interface:

interface IProps<T> {
    item: T;
    key: keyof T;
}

How can I ensure that item[key] is of type string or number such that item[key] can be used to index Record<string | number, string>?

I'm using typescript v3.9

kaya3
  • 47,440
  • 4
  • 68
  • 97
Bugbeeb
  • 2,021
  • 1
  • 9
  • 26
  • Possible duplicate of [Typescript: How do you filter a type's properties to those of a certain type?](https://stackoverflow.com/questions/56863875/typescript-how-do-you-filter-a-types-properties-to-those-of-a-certain-type), where using the answer there gives you [this code](https://tsplay.dev/wgrB1W). – jcalz Apr 12 '21 at 19:00
  • Yes it was, but I updated the question to be more specific than that – Bugbeeb Apr 12 '21 at 19:06
  • So now it is p̶o̶s̶s̶i̶b̶l̶e̶ ̶d̶u̶p̶l̶i̶c̶a̶t̶e̶ ̶o̶f̶ related to [TypeScript: Accept all Object keys that map to a specific type](https://stackoverflow.com/questions/61764867/typescript-accept-all-object-keys-that-map-to-a-specific-type) alth, where the code example looks like [this](https://tsplay.dev/WYJyxw). Does that work for you? Also note that it's helpful for your code to constitute a [mcve] that people can test; indeed, `item[key]` can index a `Record` if `item` is of a specific type. A unit test case in the code would prevent rounds of modification. – jcalz Apr 12 '21 at 19:17

3 Answers3

2

Instead of using keyof T, you presumably want a stricter type which only allows keys with string | number values. Here's a solution:

type KeysAssignableTo<T, V> = {[K in keyof T]: T[K] extends V ? K : never}[keyof T]

interface IProps<T> {
    item: T;
    key: KeysAssignableTo<T, string | number>;
}

Examples:

const item = {foo: 1, bar: true}

// OK
const testOK: IProps<typeof item> = {item, key: 'foo'}
// error: Type 'bar' is not assignable to type 'foo'
const testBad: IProps<typeof item> = {item, key: 'bar'}

Playground Link

kaya3
  • 47,440
  • 4
  • 68
  • 97
  • This definitely answered my original question, but I made a small edit that represents my actual use case better and I still run into the problem here -> https://www.typescriptlang.org/play?#code/C4TwDgpgBA0hIGcCCCEEsDmA7AhgIwBsIAVAewB5iAaKANQD4oBeKAbwG0Yo0soBreKQBmUYgF0AXKM5ioEAB7AIWACYI6UAPywoUrBABuEAE4BfdgJDDRYgLAAoBzyXGhOAMbQAkgAVjpMARKRlYHKHDuJQBbBClidjEAbjCIyyk4RBR0bHwiMkoaBGBjHgwoAB8oLABXKLwTemT7UwcHd1IsIqghaqx3ZigC+gAKVjRohBpLUylff0DggEpmEJTw9s7gKGMIduMVKQAlXdJ98iKSrDLKmrqTQuLSxhZWFvsIqA2uoTRjLpZxhAYuwAAxJNafDpdAw4AjVaAsHZ7FTsH5-YAWeBicHNBxAA – Bugbeeb Apr 12 '21 at 19:04
0

Instead of generic type maybe use a custom type like this:

interface PropType {
   [key: string]: string | number;
}
Berk Kurkcuoglu
  • 1,453
  • 1
  • 9
  • 11
  • I wouldn't know which key to index by. What is the x in `item[x]`? – Bugbeeb Apr 12 '21 at 18:29
  • In this example x can be anything, but if you have known properties just declare them as fields than you can use them for indexing and also have unlimited other properties, I assumed you don't know which properties exist since you are using a generic type here. – Berk Kurkcuoglu Apr 12 '21 at 18:32
0

I think you are looking for smth like this:

interface IProps<T extends Record<string, string | number>> {
  item: T;
  key: keyof T;
}

type Test1 = IProps<{ age: 1 }> // ok
type Test2 = IProps<{ age: '42' }> // ok
type Test3 = IProps<{ age: ['42'] }> // error