Search code examples
javascripttypescriptlodash

Typesafe nested property lookup


I am trying to create a type safe version of the get lodash function (see here). My idea is to create a function that is able to return the correct type if the nested property is available otherwise undefined.

interface Nested {
  value: 'some' | 'type';
}
interface SomeComplexType {
  foo: string;
  bar: number;
  nested: Nested;
}

const testObject: SomeComplexType = {
  foo: 'foo value',
  bar: 1234,
  nested: {
    value: 'type'
  }
};

// The type of someValue should be: 'some' | 'type'
const someValue = lookup(testObject, 'nested.value');
console.log('Value found:', someValue);

Right now I have the following:

function get<T, K extends keyof T>(object: T, key: K): T[K] | undefined {
  return object[key];
}

function lookup<T, K extends keyof T>(object: T, path: string) {
  const parts = path.split('.');
  const property = parts.shift() as K; // TODO autoinfer is possible?
  const value = get(object, property);

  if (!parts.length || value === undefined) {
    return value;
  }

  const newPath = parts.join('.');
  return lookup(value, newPath);
}

But I am stuck with the lookup return type. Typescript in strict mode says:

src/lookup.ts:14:10 - error TS7023: 'lookup' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.

Any ideas?


Solution

  • Answering my own question after the introduction of template literal types.

    Now with template literal types you can obtain exactly what I wanted.

    See example in playground.

    See here for the implementation (from this tweet): https://github.com/ghoullier/awesome-template-literal-types#dot-notation-string-type-safe

    So now it works (I renamed the function from get to lookup) Working solution