2

I have a function that receives a string as a parameter. This string should match an object's path, so the function can return a correct value from the requested key.

How to make sure the string path is included in the original object? Also, would it be possible to get an autocomplete while writing the parameter?

For exemple:

const obj = {
  "hello": "hello",
  "company": {
    "employee": {
      "tech": "tech team",
      "sales": "sales team"
    }
  },
}

function getString(path: string){
  let result = obj
  path.split(".").forEach((k) => {
    if (!result[k]) return;
    return (result = result[k]);
  });
  return result
}

getString("hello")
// typescript remains silent because the path exists.

getString("howdy")
// typescript returns an error because the path doesn't exist.

getString("company.em") 
// autocomplete suggests: company.employee.tech & company.employee.sales

Of course, this function is useless, it's just a simple exemple to keep things focused on the issue.

DoneDeal0
  • 5,273
  • 13
  • 55
  • 114
  • 1
    Please see [the answer](https://stackoverflow.com/a/58436959/2887218) to the other question for detailed information. It looks like you want `Leaves`, which results in a union of all the dotted paths to the leaf nodes of the object tree. If I translate that to your example, it yields [this code](https://tsplay.dev/wEDxOW). Good luck! – jcalz Aug 18 '21 at 18:51
  • Hello Jcalz, thanks for your answer! Based on this thread, the autocomplete now works, but displays all the possible paths of an object. Would it be possible to make it work progressively, like TS would do with a classic object? For exemple: instead of suggesting: `hello, company, company.employee.tech, company.employee.sales`, could the autocomplete only show `hello, company`, then `company.employee`, and finally `company.employee.tech, company.employee.sales`? Here is a codesandbox of the current version: https://codesandbox.io/s/aged-darkness-s6kmx?file=/src/App.tsx – DoneDeal0 Aug 19 '21 at 09:19
  • 1
    I don't think this is possible, sorry. IntelliSense will suggest all valid inputs including full paths (and `"company"` is not valid btw). I spent a bit of effort coming up with an `AutoComplete` type that takes a partially typed string `T`, and all valid strings `U`, and returns the set of partial completions from there up to the next dot. But the compiler cannot be convinced to have `getString(path)` work where `path` is inferred as `T` and then you are prompted with `AutoComplete`. So we're stuck, sorry. [See attempt](//tsplay.dev/mqvxJW) – jcalz Aug 19 '21 at 13:17
  • No problem, thanks for your help! – DoneDeal0 Aug 27 '21 at 09:52

1 Answers1

0

This function should do the job:

const obj = {
  "hello": "hello",
  "company": {
    "employee": {
      "tech": "tech team",
      "sales": "sales team"
    }
  },
}

function getString(path) {
  // get keys
  const keys = path.indexOf(".") + 1 ? path.split(".") : [path];
  // remove lastkey and save it in a variable
  const lastKey = keys.pop();
  // now get obj in accordance to the 'keys' array
  const lastObj = keys.reduce((acc, key, i) => obj[key], obj);
  if (lastObj && typeof lastObj === "object") {
    if (Object.keys(lastObj).includes(lastKey)) {
      // remains silent
      return "";
    } else {
      // if there is a possible path push it in the array 'possible'
      const possible = [];
      if (Object.keys(lastObj).some(key => !key.indexOf(lastKey) && possible.push(key))) {
        // now make possible paths
        const result = [];
        const route = (subObj, keyIndex = 0, _path = keys.join(".")) => {
          const _keys = Object.keys(subObj);
          if (typeof subObj === 'object' && !Array.isArray(subObj) && _keys.length > 0) {
            while (keyIndex < _keys.length) {
              route(subObj[_keys[keyIndex]], 0, _path + "." + _keys[keyIndex]);
              keyIndex++;
            }
          } else result.push(_path);
        };
        route(lastObj);
        // return possible paths
        return "autocomplete suggests: " + result.join(" & ");
      }
    }
  }
  // error no path found
  return "Error couldn´t find path: " + path;
}

console.log(getString("hello"));
// typescript remains silent because the path exists.

console.log(getString("howdy"));
// typescript returns an error because the path doesn't exist.

console.log(getString("company.em"));
AlexSp3
  • 2,201
  • 2
  • 7
  • 24
  • Hi, thanks for your answer. Your autocomplete function is interesting, but I'm actually looking for a typescript autocompletion while I'm writing the function parameter. This function only return some suggestions once it has ran. – DoneDeal0 Aug 19 '21 at 07:11