Reputation: 517
I want to reuse this pattern for a number of argument object types, and for the return type of the function to be the same as the value of the object types
type IntentColorCategory = 'brand' | 'neutral' | 'semantic'
type IntentColor = 'primary' | 'secondary' | 'neutral' | 'error' | 'success'
// Understandably this doesn't work - intention is to have a function that
// has a return type corresponding to the value of the argument object
// values.
const getByIntentColorCategory<T extends any> = ({
brand,
neutral,
semantic
}: Record<IntentColorCategory, T>) => (intent: IntentColor): T => {
switch (intent) {
case 'primary':
case 'secondary':
return brand;
case 'neutral':
return neutral;
case 'error':
case 'success':
default:
return semantic;
}
}
// E.g.
getByIntentColorCategory<number>({ brand: 1, semantic: 2, neutral: 3 })
// I want to specify the object must have number values when used here and
//that the return type is a number
getByIntentColorCategory<string>({ brand: 'a', semantic: 'b', neutral: 'c' })
// I want to specify the object must have strings when used here and that
// the return type is a string
Upvotes: 0
Views: 75
Reputation: 517
Thanks @mbdavis, his solution works correctly:
type IntentColorCategory = 'brand' | 'neutral' | 'semantic'
type IntentColor = 'primary' | 'secondary' | 'neutral' | 'error' | 'success'
const getByIntentColorCategory = <T extends any>({
brand,
neutral,
semantic
}: Record<IntentColorCategory, T>) => (intent: IntentColor): T => {
switch (intent) {
case 'primary':
case 'secondary':
return brand;
case 'neutral':
return neutral;
case 'error':
case 'success':
default:
return semantic;
}
}
type NumberIntents = Record<IntentColorCategory, number>
type StringIntents = Record<IntentColorCategory, string>
const numberIntents: NumberIntents = {
brand: 1,
neutral: 2,
semantic: 3,
}
const stringIntents: StringIntents = {
brand: 'a',
neutral: 'b',
semantic: 'c',
}
getByIntentColorCategory(numberIntents); // returns number
getByIntentColorCategory(stringIntents); // returns string
Upvotes: 0
Reputation: 4010
If I understand your question correctly - I think you're only off by where you're putting the generic parameter. It should go next to the arguments (on the function side, as opposed to the assignment side).
type IntentColorCategory = 'brand' | 'neutral' | 'semantic'
type IntentColor = 'primary' | 'secondary' | 'neutral' | 'error' | 'success'
const getByIntentColorCategory = <T extends any>({
brand,
neutral,
semantic
}: Record<IntentColorCategory, T>) => (intent: IntentColor): T => {
switch (intent) {
case 'primary':
case 'secondary':
return brand;
case 'neutral':
return neutral;
case 'error':
case 'success':
default:
return semantic;
}
}
You could then even drop the explicit type argument when using the function:
Upvotes: 1