0

I have an object containing multiple types of values:

interface Foo {
  id: number;
  data: FooData;
  username: string;
  notes: string;
}

const foo: Foo = {
  ...
}

I have a function that requires a string, and am iterating through a specific list of fields in my object to use in that function, all of which contain string values:

const renderString = (value: string) => {
  ...
}

const fooKeys: keyof Foo = ["username", "notes"];

fooKeys.map((key) => {
  renderString(foo[key])
}

The issue is that foo[key] can be a string, number, or FooData object, so I want to specify that foo[key] will ONLY be a field value of Foo with a key matching one in fooKeys, since they are all going to be string values.

We can assert foo[key] as string but that doesn't protect us against bad keys in fooKeys.

Kavin2468
  • 208
  • 2
  • 10
  • Why not just `const fooKeys = ["username", "notes"] as const;` and forget `keyof Foo` at all? (and it would be `Array` anyway, not just `keyof Foo`) – jcalz Sep 16 '19 at 18:01

2 Answers2

2

One option is to use a conditional type to only allow string properties since that appears to be the only ones allowed? For example:

type StringPropsOnly<T> = {
  [Key in keyof T]: T[Key] extends string ? Key : never;
}[keyof T]

const fooKeys: StringPropsOnly<Foo>[] = ["username", "notes"];

fooKeys.map((key) => {
  renderString(foo[key])
});

TypeScript Playground

skovy
  • 5,430
  • 2
  • 20
  • 34
1

This answer I found mentions as const:

const fooKeys = ["username", "notes"] as const;
Ry-
  • 218,210
  • 55
  • 464
  • 476
  • This could be a nice solution, but isn't scalable, and is prone to breaking changes, since it doesn't have any link to `Foo`. @skovy's solution is the best once - gets all `string`-typed property names of `Foo`, and whenever such are added - will support them. – Amit Beckenstein Sep 16 '19 at 18:05
  • 1
    @AmitB.: As long as some use like the `fooKeys.map` exists, it will produce an error there, which could be good enough for such an easy solution. – Ry- Sep 16 '19 at 18:06