Skip to content

Instantly share code, notes, and snippets.

@jpkempf
Last active January 5, 2021 09:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpkempf/6643e0dee18f1c8d32a6f83823051c3f to your computer and use it in GitHub Desktop.
Save jpkempf/6643e0dee18f1c8d32a6f83823051c3f to your computer and use it in GitHub Desktop.
Using TypeScript function overloads and generics to narrow down return types
/**
* 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