aiven715
aiven715

Reputation: 89

Accessing nested object in a union type

How to implement a function which takes an object(part of a union) with path and returns value of the config object for given path?

I'm trying the following:

type Foo = {
  config: {
    a: string;
  };
};

type Bar = {
  config: {
    b: string;
  };
};

type Baz = Foo | Bar;

const prop = <T extends Baz>(obj: T, path: keyof T["config"]) => {
  // I'm getting an error at this point:
  // "Type 'keyof T["config"] cannot be used to index type '{ a: string; } | { b: string }'
  return obj.config[path];
};

However, if I implement the similar function and trying to access fields at the root level(instead of the config object), it works smoothly:

type Foo = {
  a: string
}

type Bar = {
  b: string
}

type Baz = Foo | Bar

const prop = <T extends Baz>(obj: T, path: keyof T) => {
  return obj[path]
}

I'm assuming it has something with applying keyof on the object field of a Union type, but can not quite understand why.

Upvotes: 0

Views: 168

Answers (2)

aleksxor
aleksxor

Reputation: 8370

You may rewrite your function as:

const prop = (obj: Baz, path: keyof Baz["config"]) => {
  return obj.config[path];
};

or:

const prop = <T, K extends keyof T>(obj: { config: T }, path: K) => {
    return obj['config'][path];
};

playground link

As for the failure to index type with keyof T["config"], it looks like that's a design limitation.

Upvotes: 1

Chris Gilardi
Chris Gilardi

Reputation: 1540

It's not exactly perfect, but this seems to accomplish what you are looking for in my testing:

const prop = <T extends Baz, K extends keyof T['config']>(obj: T, path: K): T['config'][K] => {
    return (obj['config'] as T['config'])[path];
};

I tested by running this:

const p = prop({ config: { b: 'hi' } }, 'b');
console.log(p);

Which output hi.

Upvotes: 1

Related Questions