I have a helper function that I'm porting from JavaScript:
function extractDistinct(data, fieldName) {
const uniques = data.reduce(
(set, x) => set.add(x[fieldName]), new Set()
);
return [...uniques];
}
This function simplify extract a list of unique values for property fieldName
in a an array of items. While the JavaScript code actually allows for a collection of possibly different data types —as long as they have the intended field— I guess the version I'm trying to get for TypeScript would limit to an array of a single type.
Here's what I have so far:
function extractDistinct<T>(data: T[], fieldName: keyof T): unknown[] {
const uniques = data.reduce((set, x) => set.add(x[fieldName]), new Set());
return [...uniques];
}
It works in that the TS compiler is checking to make sure fieldName
is indeed a property of T
but I have to cast the whole thing as the intended type which is a bit clunky:
const people: Person[] = .... // doesn't matter where it comes from.
const hairColor = extractDistinct(people, 'hair_color') as string[]);
I currently have it as returning unknown[]
but I'm trying to return the type declared for this key/field in type T
. How can that be achieved?
CodePudding user response:
You want extractDistinct()
to be generic in the string literal type K
of the fieldName
parameter as well as in the type T
of the elements of data
:
function extractDistinct<T, K extends keyof T>(data: T[], fieldName: K): T[K][] {
const uniques = data.reduce((set, x) => set.add(x[fieldName]), new Set<T[K]>());
return [...uniques];
}
This uses the indexed access type T[K]
to represent the property type of T
at key K
. That compiles with no error, and now you get your desired behavior:
interface Person {
hair_color: string;
number_of_limbs: number;
actually_three_hedgehogs_in_a_trenchcoat: boolean;
}
declare const people: Person[];
const hairColor = extractDistinct(people, 'hair_color');
// const hairColor: string[]
const numberOfLimbs = extractDistinct(people, 'number_of_limbs');
// const numberOfLimbs: number[]