简体   繁体   中英

Typescript 'tree' object definition

I'm not quite sure of the 'name' for the type of object that I'm creating. I'm calling it a tree because it looks similar to a nested tree without the relationships. Essentially I want an object with nested definitions like so

{
    test1: OptionsInterface,
    test2: {
        test3: OptionsInterface,
        test4: {
            test5: OptionsInterface,
        },
    },
}

So the first level can be either OptionsInterface or {[s: string]: OptionsInterface} Is there a way to use this on every 'level' of the object?

I've tried defining the above like so:

export default class ApiClient {
    constructor(options: {[s: string]: OptionsInterface | {[s: string]: OptionsInterface}}) {}

But this is only going to be 2 deep right? Is there a way I can define my example object without having to manually add every depth?

use case

I want to be able to call my class like so

api = new ApiClient(routeSchema);
await api.call('test2.test4.test5', params);

in call:

async call(config: string, variables: object = {}): Promise<Response> {
  const options = get(this.configuration, config);

  if (options === undefined) {
    throw new ConfigNotDefinedExpection(config);
  }

  return await this.callWithOptions(options, variables);
}

Where callWithOptions expects an OptionsInterface

Sure, you can do that.

type NestableOptionsInterface = OptionsInterface | { [k: string]: NestableOptionsInterface }

That says a NestableOptionsInterface is either an OptionsInterface or a dictionary whose keys are anything you want and whose values are NestedOptionsInterface . So it's a recursive defintion. Let's test it:

class Foo {
  constructor(options: NestableOptionsInterface) { }
}

declare const optionsInterface: OptionsInterface;

new Foo(optionsInterface); // okay
new Foo({ a: optionsInterface, b: { c: optionsInterface } }); // okay
new Foo({ a: { b: { c: { d: { e: optionsInterface } } } } }); // okay
new Foo("whoops"); // error
new Foo({ a: optionsInterface, b: { c: "whoops" } }); // error

Looks good.

If you want to maintain the type of the actual constructor argument, you can use generics like this:

class Foo<O extends NestableOptionsInterface> {
  constructor(options: O) { }
}

declare const optionsInterface: OptionsInterface;

new Foo(optionsInterface); // Foo<OptionsInterface>
new Foo({ a: optionsInterface, b: { c: optionsInterface } }); // Foo<{ a: OptionsInterface, b:{c: OptionsInterface}}>
new Foo({ a: { b: { c: { d: { e: optionsInterface } } } } }); // Foo<{ a:{b:{c:{d:{e: OptionsInterface}}}}}>

Hope that helps. Good luck!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM