Reputation: 1218
Suppose I have the following JS code:
export const giveMeFunctions = (namePrefix, number) => {
const functionA = (x) => number + x;
const functionB = (x, y) => number * x - y;
return {
[`${namePrefix}Add`]: functionA,
[`${namePrefix}MultSub`]: functionB,
};
}
// This is handy because I can then do:
const {piAdd, piMultSub} = giveMeFunctions('pi', Math.PI);
const {eAdd, eMultSub} = giveMeFunctions('e', Math.E);
// No confusion between variable names, and object interpolation makes it easy to use
I cannot, however, think of a good way to type the return value of this function in Typescript. As you can see, the two keys have different types, so I can't simply do something like
const giveMeFunctions<N extends string> = (number: number): {
[key: string]: Function;
} => {...}
as this would allow accidental interchanging of the arguments of functionA
and functionB
. It seems logical to me that this should be possible with Typescript, as it's all possible at compile-time. For instance, if we simply took out the concatenation step, this would be easily possible:
export const giveMeFunctions<T extends string> = (number: number): Record<T, string> => {...}
Something I expected to work would be:
const giveMeFunctions<N extends string> = (number: number): {
[N + 'Add']: string,
[N + 'MultSub']: string,
} => {...}
or even:
const giveMeFunctions<N extends string> = (namePrefix: N, number: number): {
[N + 'Add']: string,
[N + 'MultSub']: string,
} => {...}
but both of these complain that:
TS1170: A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.
Is there any way to achieve the desired effect of an object type with keys defined by a concatenation of a user-specified string and a hard-coded suffix?
Upvotes: 1
Views: 3327
Reputation: 33041
You can do smth like that:
const record = <
Prop extends PropertyKey,
Value extends (...args: any[]) => any
>(prop: Prop, value: Value) =>
({ [prop]: value }) as Record<Prop, Value>
export const giveMeFunctions = <
T extends string,
N extends number
>(namePrefix: T, number: N) =>
({
...record(`${namePrefix}Add`, (x: N) => number + x,),
...record(`${namePrefix}MultSub`, (x: N, y: N) => number * x - y),
})
const result = giveMeFunctions('pi', Math.PI);
result.piAdd // (x: number) => number
result.piMultSub // (x: number, y: number) => number
record
function does the trick. Object with computed proeprties like {[prop]:key}
is resolved as {[props:string]:any type}
by the default.
As you might have noticed, I used type casting as Record<Prop, Value>
to avoid this behavior
Upvotes: 2
Reputation: 431
If i understood correctly, you want to do something like this
type keyAdd = `${string}Add`
type keyMultSub = `${string}MultSub`
Guess it should work
const giveMeFunctions = (number: number): {
[keyAdd]: Function,
[keyMultSub]: Function
} => {/*some function*/}
Upvotes: 1