-
-
Notifications
You must be signed in to change notification settings - Fork 142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Typed config.get based on default.json #1136
Comments
Thanks @enepeti !
and developers can extend their own config interface to this file. And there are already some type utilities library to support object string path with hinting. E.g. https://millsp.github.io/ts-toolbelt/modules/function_autopath.html |
Originally posted by @kingdun3284 in #1027 (comment) |
@kingdun3284 thank you for your reply! I'm trying to explore the possibilities of typescript, and learn as much as I can, that's why I try to implement stuff myself, not checking for already made solutions :). I'll check the library and I think if it has the same functionality, we should definitely use that instead of a diy solution. But I'm not sure about having the |
It’s true that it is tedious not to have auto-completion for the configuration path. And having type inference could be interested as well. There are some concerns that I have through: Path guess
Yes, this is a limitation and I’d rather not to have to specify all the config keys in the
Based on this, we might want to define some parameters only for production in Maybe the Typescript inference
Yes, this would not include types such as Type conversion Another point that I see here is that, by only inferring the TypeScript type, we don’t check nor convert the JS type which is currently done in the framework. For example, if we specify |
I'm also a bit worried about the stability of very complex TypeScript types such as But maybe, we could end up on something more customizable that any one could adjust. For example, based on the example here, we could add more generic types on Config.get<P extends string, V extends ExpectedTypeBasedOnSecondParameter, ...>(key: P, ...)
Config.get<AutoPath<some types>, AutoKey(some types)>() |
Or maybe something like this: function getConfig(key: AutoPath<some types>, type, ...) {
return Config.get(key, type)
} |
It seems that AutoPath is a bit buggy in latest typescript version,what if writting a proxy object for that purpose?We can do the type casting ourself too. let ConfigObj:FoalsEnv//interface from foals-env.d.ts
const handler=(path:string[]=[])=>({
get: (target, key) => {
const value=Config.get([...path,key].join("."));
if(typeof value==="object")
return new Proxy({},handler([...path,key]))
else return value
},
})
ConfigObj=new Proxy({},handler())
console.log(ConfigObj.settings.serverPath) |
I have thought about another better design using proxy pattern as well. const TARGET_SYMBOL = Symbol('proxied-target');
function toPath<T extends object>(obj: T): T {
function createProxy(path: any[] = []) {
const proxy = new Proxy(path, {
get: (target, key) => {
if (key == TARGET_SYMBOL) return target;
path.push(key);
return proxy;
},
});
return proxy;
}
return createProxy();
}
function typedConfig<T>(path: (obj: FoalsEnv) => T): T {
return Config.get((path(toPath(obj)) as any)[TARGET_SYMBOL].join("."));
} to use it, simply typedConfig((config)=>config.settings.serverPath) |
Also, it would be better to support typescript config file as well. |
I may have a way to make it work all together. With this solution the goals are to:
Each Foal project would have a new import { Config, IConfigSchema } from '@foal/core';
const configSchema = {
database: {
uri: { type: 'string', required: true }
},
myOtherCustomConfig: {
type: 'boolean|string',
}
} satisfies IConfigSchema;
export const config = Config.load(configSchema); Then anywhere in the application code, the configuration would be accessible like a regular object: import { config } from '../relative-path';
// Application configuration
console.log(config.database.uri) // TypeScript type: string
console.log(config.myOtherCustomConfig) // TypeScript type: boolean | string | undefined
// Framework configuration (optional)
console.log(config.settings.social.facebook.clientId) // TypeScript type: string The properties of the |
Hi, I'm opening a new issue as you asked it in #880
I took a deep dive into the Typescript type system and was able to come up with a type (lot of ideas came from the type-challanges) for the Config.get function:
As you might notice, the solution heavily relies on the default.json, if we want to read a value which is missing from that file, typescript will throw an error. In my opinion this is more of a positive outcome than a negative, as it forces to define a default value for every key that the application will try to read, also making the defaultValue parameter obsolete (defaults should be defined in default.json).
Another limitation is in a json file we can only have string, boolean, number, array and object types, the type the function returns with can only be these. So if we want to have a more restricted type (e.g. Stripe API has an Interval type which is a subset of string ("weekly" | "daily" | "manual" | "monthly") and we want to use the value straight from the config) we either need to cast the type, be able to specify the return type of the function similar to the current solution, or have some utility to rewrite the type of the config (which I included in the solution).
But in my opinion with these limitations we would get a type-safe, easy-to-use Config functionality.
I'm really interested in your opinion (and I don't have a clear idea, how to include this in the framework)
Some screenshots to show how it works:
intellisense can show you all the available keys
typescript error if key doesn't exists, value automatically typed correctly
works with deep keys with dot notation, correctly returns with complex object types
I tried to add as many comments as possible, as it is not an easy read :). (it also uses some of the newest features of typescript, I used version 4.7.3)
The text was updated successfully, but these errors were encountered: