I have the following code structure to manage some settings :
interface Settings {}
interface ServerSettings extends Settings {
host: string;
port: number;
valid: boolean;
}
interface SomeOtherSettings extends Settings {
enabled: boolean;
}
class SettingsProvider {
constructor(private settings: Settings) {}
public get<S extends Settings>(
key: keyof S,
): S[typeof key] {
return (this.settings as unknown as S)[key];
}
}
const settings: ServerSettings & SomeOtherSettings = {
host: 'localhost',
port: 8080,
valid: true,
enabled: false,
};
const provider = new SettingsProvider(settings);
const port = provider.get<ServerSettings>('port');
The problem is that the type of port
is const port: string | number | boolean
(the union of all the possible types of ServerSettings
), and not number
, as one would expect, given the return type of the function being S[typeof key]
.
How can I get the right type narrowed ?
One solution could be to pass a second generic to the function like Key extends keyof S
but this seems very redundant.
One solution could be to pass a second generic to the function like
Key extends keyof S
but this seems very redundant.
This sounds like typescript - Providing only some types for generic type - Stack Overflow.
A workaround is to curry the function:
class SettingsProvider {
constructor(private settings: Settings) {}
public get<S extends Settings>() {
return <K extends keyof S>(key: K) => {
return (this.settings as S)[key];
}
}
}
const settings: ServerSettings & SomeOtherSettings = {
host: 'localhost',
port: 8080,
valid: true,
enabled: false,
};
const provider = new SettingsProvider(settings);
const port = provider.get<ServerSettings>()('port');
const getter = provider.get<ServerSettings>();
const host = getter('host');
const valid = getter('valid');