Skip to content

Instantly share code, notes, and snippets.

@airtonix
Created October 16, 2024 02:01
Show Gist options
  • Save airtonix/c261b5dcea47747420cdc2ac2ece4e46 to your computer and use it in GitHub Desktop.
Save airtonix/c261b5dcea47747420cdc2ac2ece4e46 to your computer and use it in GitHub Desktop.
Typed Config
import { typedConfig } from './core-typed-config';
describe('coreTypedConfig', () => {
it('should work', () => {
type Schema = {
BAR_BAZ: boolean;
BAR_COUNT: number;
REALLY_KABOOM: number;
}
const config = typedConfig<Schema>({
BAR_BAZ: true,
BAR_COUNT: 42,
REALLY_KABOOM: 100,
});
expect(config.bar.baz).toBe(true);
expect(config.bar.count).toBe(42);
expect(config.really.kaboom).toBe(100);
})
});
import { set } from 'lodash-es';
type DeepRecord<K extends string, V> = K extends `${infer K0}_${infer KR}`
? Record<K0, DeepRecord<KR, V>>
: Record<K, V>;
type ExpandRecursively<T> = T extends object
? { [K in keyof T]: ExpandRecursively<T[K]> }
: T;
type Transform<T extends object> =
{
[K in string & keyof T]: (x: DeepRecord<Lowercase<K>, T[K]>) => void;
} extends Record<string, (x: infer I) => void>
? ExpandRecursively<I>
: never;
export function typedConfig<T extends object>(flatConfig: T): Transform<T> {
// split the keys by the first underscore and store the rest of the key
const entries = Object.entries(flatConfig);
const output = entries.reduce(
(output, [key, value]) => {
const path = key.replace(/_/g, '.').toLowerCase().split('.');
return set(output, path, value);
},
{} as Record<string, unknown>,
);
return output as Transform<T>;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment