Home > Back-end >  Typescript require property with certain type but any name
Typescript require property with certain type but any name

Time:08-26

I have a utility (a React hook) which takes an array of items. The items can be any object but requires one property that is type number. The specific case is I need these objects to have an index property, but the consumer of this hook can define which property this index should be. I was hoping this could be accomplished by a utility type of some sort, maybe something like this where IndexedProperty would be the utility type:

Some interfaces

interface SomeObject {
  id: number;
  index: number;
  name: string;
}

interface AnotherObject {
  id: number;
  priority: number;
  name: string;
}

The hook

function useIndexedHook<T extends IndexedProperty>(
  items: Array<T> = []
){
  // ...
}

Calling the hook

// valid, 'index' is defined and is type number
const validItems: IndexedProperty<Array<SomeObject>, 'index'> = [ /* ... */ ];
useIndexedHook(validItems);

// valid, 'priority' is defined and is type number
const validItems2: IndexedProperty<Array<AnotherObject>, 'priority'>  = [ /* ... */ ];
useIndexedHook(validItems2);

// invalid, 'name' is defined but is type string
const invalidItems: IndexedProperty<Array<SomeObject>, 'name'> = [ /* ... */ ];
useIndexedHook(invalidItems);

CodePudding user response:

If useIndexedHook is going to make use of this property, it will need to receive the string at runtime, not merely in its erased type signature:

function useIndexedHook<P extends string>(
  items: IndexedProperty<P>[],
  propertyName: P
){
  for (const item of items) {
    const propertyValue: number = item[propertyName]
  }
}

You can implement IndexedProperty like this:

type IndexedProperty<P extends string> = {
  [index in P]: number;
}

You can also substitute Record<P, number> if you prefer.

Usage:

// valid, 'index' is defined and is type number
const validItems: SomeObject[] = [ /* ... */ ];
useIndexedHook(validItems, 'index');

// valid, 'priority' is defined and is type number
const validItems2: AnotherObject[] = [ /* ... */ ];
useIndexedHook(validItems2, 'priority');

// invalid, 'name' is defined but is type string
const invalidItems: SomeObject[] = [ /* ... */ ];
useIndexedHook(invalidItems, 'name');

Playground

  • Related