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');