I'm storing some settings into local storage and I would like to type the responses when I get (and ideally also insert) values from/to the storage.
From what I've seen, the best way seems to be to use function overloading. So this is what I have now and it works:
export enum SettingsKey {
hasOnboarded = 'hasOnboarded',
phoneNumber = 'phoneNumber'
}
export async function getSetting(storage: Storage, key: SettingsKey.phoneNumber): Promise<string>
export async function getSetting(storage: Storage, key: SettingsKey.hasOnboarded): Promise<boolean>
export async function getSetting(storage: Storage, key: any) {
return storage.get(key)
}
The thing that I don't like about this solution is that it's possible to forget adding a new element in the enum to the overload type definitions. Is there a way to enforce that all enum values are handled? Or is there maybe a better way to do this altogether?
I thought this would be a simple thing, a mapping from value hasOnboarded
to return type boolean
etc, but it's obviously not that easy.
It looks to me like conditional types might solve this problem, but I can't quite wrap my head around how it works.
I also saw this approach, but this seems like a little too much overhead.
Any insight would be greatly appreciated!
You can use an extra type to map between the enum and the promise return type. We then add a generic parameter to getSettings
that extends the SettingsKey
enum and use the generic type to index into the mapping type. The generic parameter will be inferred based on the enum member we specify as an argument.
If the mapping type does not contain all keys of the enum we will get an error on the function.
export enum SettingsKey {
hasOnboarded = 'hasOnboarded',
phoneNumber = 'phoneNumber'
}
type SettingsKeyReturnType = {
[SettingsKey.hasOnboarded]: boolean,
[SettingsKey.phoneNumber]: string
}
export async function getSetting<K extends SettingsKey>(storage: Storage, key: K): Promise<SettingsKeyReturnType[K]> {
return storage.get(key)
}
let a = getSetting(window.localStorage, SettingsKey.phoneNumber); // Promise<string>
let b = getSetting(window.localStorage, SettingsKey.hasOnboarded); // Promise<booelan>
// We can also define a set function in a similar way
export async function setSetting<K extends SettingsKey>(storage: Storage, key: K, value: SettingsKeyReturnType[K]): Promise<void> {
///...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With