Reputation: 3400
I have some formData object like this (I'm getting it in runtime):
const formData = {
xAxisColumn: 'abc',
periodColumn: 'cde'
};
And data that I'm getting from the server that has the next structure:
const response = {
abc: 'some data 1',
cde: 123,
otherColumn: 'bla...'
}
I'm trying in Typescript to create a type for the response object where the keys are dynamic and based on formData values.
Something like:
type TResponse<T, U> = {
[T]: string;
[U]: number;
}
But this is incorrect for TS, so how can I do it? Thanks.
Upvotes: 1
Views: 122
Reputation: 330086
Without knowing exactly how the rest of your code would make use of it, the type you're looking for could be expressed like this:
type TResponse<KS extends PropertyKey, KN extends PropertyKey> =
Record<KS, string> & Record<KN, number>;
that's an intersection of two Record
types. It says that TResponse<KS, KN>
has a string
property at the key of type KS
, and a number
property at the key of type KN
.
You could possibly then use it like this:
const formData = {
xAxisColumn: 'abc',
periodColumn: 'cde'
} as const;
const response = {
abc: 'some data 1',
cde: 123,
otherColumn: 'bla...'
}
function processResponse<KS extends PropertyKey, KN extends PropertyKey>(
formData: { xAxisColumn: KS, periodColumn: KN },
response: TResponse<KS, KN>
) {
const xAxis: string = response[formData.xAxisColumn];
const period: number = response[formData.periodColumn];
// ... process them here
}
processResponse(formData, response); // okay
processResponse({ xAxisColumn: "x", periodColumn: "p" }, { x: "okay", p: 123 }); // also okay
processResponse({ xAxisColumn: "x", periodColumn: "p" }, { x: 123, p: 456 }); // error!
// number is not assignable to string -------------------> ~
Or maybe
function keyVal<K extends PropertyKey, V>(key: K, val: V) {
return { [key]: val } as Record<K, V>;
}
function makeResponse<KS extends PropertyKey, KN extends PropertyKey>(
formData: { xAxisColumn: KS, periodColumn: KN }
): TResponse<KS, KN> {
return Object.assign(keyVal(formData.xAxisColumn, "x axis data"), keyVal(formData.periodColumn, 123));
}
The point of the example code is to show that the compiler will enforce the constraint that response
is of the right shape given the values of formData
... as long as the compiler knows the literal values of formData
. You will find TResponse<KS, KN>
much easier to use when KS
and KN
are known literal types.
Inside the implementation of processResponse()
and makeResponse()
, where KS
and KN
are unspecified generic types, it becomes more difficult for the compiler to reason about TResponse<KS, KN>
and you need to find roundabout ways to express manipulating properties of unspecified keys, possibly involving type assertions as in keyVal()
above.
So while you can express TResponse
, it might not be worth it to do so.
Anyway, hope that helps; good luck!
Upvotes: 1