Properly type access to object nested keys with dot-notation

Viewed 210

OK, So I have this interface here:


interface IConfig {
    git: {
        cloneMethod: 'ssh'|'https'
    },
    terminal: {
        level: 'log'|'etc',
        beforeEach: {
            consoleClear: boolean
        },
        consoleMode: 'extended' | 'simple'
    }
}

I want to create an accessor function, just like Rambda's path, but PROPERLY typed, showing all possible options in the autocomplete, and returning the expected value.

Usage example of my function:

access('terminal.beforeEach.console.Clear') // 'log'|'etc'
access('git.cloneMethod') // 'ssh'|'https'

I think this is somewhat possible with Typescript 4.1 so I tried some approaches and the one that got me the closest to what I want was:

type Join<A extends string, B> = B extends string ? `${A}.${B}` : A;

export interface IAccesor {
    <A extends keyof IConfig>(key: A): IConfig[A];
    <A extends keyof IConfig, B extends keyof IConfig[A]>(key: Join<A, B>): IConfig[A][B];
    <A extends keyof IConfig, B extends keyof IConfig[A], C extends keyof IConfig[A][B]>(key: Join<Join<A, B>, C>): IConfig[A][B][C];
}

But there were some errors...


const accessor: IAccesor = {} as any;

accessor('git') // IConfig['git'];
accessor('git.cloneMethod') // 'https'|'ssh'
accessor('terminal') // IConfig['terminal'];
accessor('terminal.beforeEach.consoleClear') // Error
accessor('terminal.lev') // Error (expected)
accessor('terminal.level') // "log" | "etc"

Im kind of lost on what to do now...

0 Answers
Related