Last active
January 5, 2021 09:55
-
-
Save jpkempf/6643e0dee18f1c8d32a6f83823051c3f to your computer and use it in GitHub Desktop.
Using TypeScript function overloads and generics to narrow down return types
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* interactive playground link: | |
* https://www.typescriptlang.org/play?#code/C4TwDgpgBAwg9gWzAQwHYgAoCcIDMI6oDGEAzlALxQDeAUFFMmGFnAG7IA2AXDfQ1GAALHKSFxOAE150BAgKIBVAEq9UAVwQAjAgG5+DAL76jJqDgDmAS1LAsyYFbioZBqFdRsrwCLy1wJCDQzBlIITlw-AM4g1BCoAHchb3CbYF5bLA8LM2NaPNpQSChsPAIIYggAaQgQSigAa1q4XFhEFHRS-EISUn1aAHoBqFx1YkdnKHYCTjhkSXIuWYSoABUAZUE4KFRkLFYVyTgE1EEhaBxgdSxToogAOkHhokQECuAp9Q-haB1lwQS204HjIWygYWgPygkisuG6FRI3FoEAAHmA4FgPshSCBiCMxkQJqcLBBgPAkGhMDh4ZVSAAKACUvGwiBsEAAPOSOlSyj0yAA+fSo9GYxg4vGjcZOYmkrmUrrlWnsqpQVE+VALErUxUkGogfl0pogXhVJla1lhTnteXavmkADaVQAuoLaE8ztBJYTpe4kDE3qhgA4fQh1LYoC82ARGJxOPipZNplhZvNSABCZFojFY8VEePeyYksnWzq2hFkZWqlHqzUKvl6g38I0AfhNtDNLIQbKtFNLvPL5AAPm1ezyab1HS6+AwXqhwyx+7TeHK++PQVRkAlkN4RqSiEI6QByXDIJoAWgXa9IZ4qknRHmAh4Z9x+qDpdNE6LnEAZlH55jIL8wnuAArUhnEZBl+gYS5rlOI0oGbKBLx1Mh7SNJ0oF4FC7X0Qw3WFbMxVxPMvSJcFZRLMdUNISs1Vvcg63LBs6SbWoTQAGn4DhOHUXwR25JjaUndtmVYLtLTYOArEkf9ZEYLcd3wYB9yPE9zxwgcbw1e9A0PDjpwEN5hDgaQoEPDBFFWfS3H8SRjSgAApdYAHkADl7kybJYRAOl5LkdDaidXgeL4ri5EMBlwqgSK8LdIZEnOW5znzcikxTTU9mgF4EADHxJE+YADKhO5yFwDEPTCAFtg4LJkC0GJyASKxYygHREhkghaFncMli6cgN0Uj4ixXai7UZfQeqxZhWB4rp6k3bdhso0chN6I8mBYdguCffoEqSCoPVSn10rmTKcAjV53ggAq4C+Yrziq5rWuEA5VX2DFSFoPrqVIe5LDSewiXuDwvB8fRNtmrguhfEQyHEKQ9uGKFrCjU5Qs9CqoVwKwsF6rALE0d5oVJAgu1QUEoWQIgSDAfLBHATGsCOsJZwKvZCby2gwmLVay1pDaZu2zh9JoM5RARszqCgJRVCgABGAAmABmGLIqgBKeJk7mVsE-n1sPSHhdF6XZd4JXVcMdWEtBrhtZ50a1rII8AcyYNnBN8X4YkKWZZUc2VbV38bc8O3JFoIA | |
*/ | |
type CompanyPreferences = { | |
approval: { | |
threshold: { | |
EUR: number; | |
}; | |
}; | |
registration: { | |
invite: boolean; | |
self: boolean; | |
whitelist: string; | |
}; | |
}; | |
type PreferenceKey = keyof CompanyPreferences; | |
// function overloads allow TS to narrow down the return type. | |
// comment out the below two lines to see the difference: | |
export async function getCompanyPreferences(): Promise<CompanyPreferences>; | |
export async function getCompanyPreferences<K extends PreferenceKey>(key: K): Promise<CompanyPreferences[K]>; | |
// the function implementation must cover all function overloads! | |
export async function getCompanyPreferences<K extends PreferenceKey>( | |
key?: K | |
): Promise<CompanyPreferences | CompanyPreferences[K]> { | |
const preferences: CompanyPreferences = await fetch('fake-preferences-endpoint').then((response) => response.json()); | |
return key ? preferences[key] : preferences; | |
} | |
export async function setCompanyPreferences<K extends PreferenceKey>( | |
key: K, | |
value: CompanyPreferences[K] | |
): Promise<void> { | |
await fetch('fake-preferences-endpoint', { | |
method: 'PUT', | |
body: JSON.stringify({ | |
[key]: value, | |
}), | |
}); | |
} | |
// when the function overloads are commented out, the types for these two variables will be wider | |
const allPrefs = await getCompanyPreferences(); | |
const approvalPref = await getCompanyPreferences('approval'); | |
// when the function overloads are commented out, these will throw errors | |
allPrefs.registration.invite; | |
approvalPref.threshold; | |
// the given value for the first argument determines the accepted type for the second argument | |
setCompanyPreferences('approval', { threshold: { EUR: 123 }}) // valid | |
setCompanyPreferences('approval', { EUR: 123 }}) // invalid | |
setCompanyPreferences('registration', { threshold: { EUR: 123 }}) // invalid |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment