0

I'm trying to write a function that will accept 2 parameters i.e key and subkey and return one of the properties of that nested object. I have successfully written the type for function but cannot access the property for the same.

This is what my code looks like:

type ObjectDefinationType<T> = {
  myFunction: () => void;
  data?: T[];
  someData: string;
};

type KeySet1 = "key1" | "key2";
type KeySet2 = "key3" | "key4";

type DataType1 = {
  id: string;
  num: number;
};

type DataType2 = {
  age: number;
  address: string;
};

type KeySet1Record = Record<KeySet1, ObjectDefinationType<DataType1>>;
type KeySet2Record = Record<KeySet2, ObjectDefinationType<DataType2>>;

type SetStoreType = {
  set1: KeySet1Record;
  set2: KeySet2Record;
};

const initialData = {
  myFunction: () => {},
  someData: "someData"
};
const set1: KeySet1Record = {
  key1: { ...initialData },
  key2: { ...initialData }
};
const set2: KeySet2Record = {
  key3: { ...initialData },
  key4: { ...initialData }
};

const setStore: SetStoreType = {
  set1,
  set2
};

const myFunc = <T extends keyof SetStoreType, K extends keyof SetStoreType[T]>(
  key: T,
  subKey: K
) => {
  // This should also work
  console.log("I want to access data", setStore[key][subKey].someData);

  // This will work but I dont want this:
  // setStore[key][subKey] = {
  //   ...setStore[key][subKey],
  //   myFunction: () => {
  //     // This is some function
  //   }
  // };

  // This should work:
  setStore[key][subKey].myFunction = () => {
    // This is some function
  };
};

I am not sure why I am not able to access

myFunction property from an object

I have created a code sandbox for the same Code sandbox

1
  • I don't see the problem here, I am able to set both the properties and call the function and log the other property in the code sandbox, check this sandbox Commented Jun 28, 2022 at 16:37

2 Answers 2

1

I'm not smart enough with Typescript to figure out why what you want to do does not seem to be working, but you could work around this behavior by using a helper function employing type-fest's Get utility type and Lodash's get function like so: https://codesandbox.io/s/compassionate-sammet-w4hr53?file=/src/App.tsx

Not sure if that is usable for whatever it is that you need, but it's the best I could come up with.

Sign up to request clarification or add additional context in comments.

5 Comments

This has to do with TS inference engine trying to be efficient. When it tries to parse setStore[key][subKey] it will parse all possible combinations and union them, such as setStore[set1][key1], but will also do incorrect ones, such as setStore[set1][key4]. This is because it hasn't narrowed type T, it is still "set1" | "set2", thus K can be key1 through key4. setStore[set1][key4] is unknown and anything (except any) unioned with unknown also becomes unknown. Type-fest's Get type forces the compiler to go step by step, narrowing the object correctly.
But when we are calling the methods, it gives options correctly, so if I write set1 we actually get key3 and key4 as second option and does not allow key1 and key2. So that TS is actually able to narrow it down. So why it is not allowing to get any values from it while implementation
Hi @jamesk , Your solution solves the problem but then we have an overhead of including few lib. Is there any alternative to this which reduces use of any external lib
Hi @KunalJain . I recommended Lodash because I couldn’t seem to solve the problem without it. I know Lodash is a huge library, but they also offer their functions as standalone modules which significantly cuts down on bloat. The only solution I personally could find to your issue was the one I recommended, where I used typefest and Lodash. I’m certain you could reimplement the necessary types/functions from these packages if you really need to be uber-minimalist when it comes to dependencies in your project, maybe try that? Sorry. I don’t have a better answer for you.
@CodyDuong ‘s comment may be the key to a dependency-free/minimal dependencies solution in your case tho. I’m not as familiar with typescript compiler internals as they seemed to be so I don’t know what advice to give based off their comment, but they seem to be confident in their analysis. Maybe try and wrap your head around how Typefest’s Get type has it’s described effect on “making the compiler go step by step”, and refactor your code / rewrite your functions to try and emulate or take advantage of that behaviour without using the library itself? Just spitballing here. Best of luck
0

Instead of using generic types you can use the actual types.

type KeysOfUnion<T> = T extends T ? keyof T : never;
const myFunc = (key: keyof SetStoreType, subKey: KeysOfUnion<SetStoreType[keyof SetStoreType]>) => {
    setStore[key][subKey].myFunction = () => {
        // This is some function
    };
    console.log("I want to access data", setStore[key][subKey].someData);
};

I got the small trick to get key of union from here.

1 Comment

Generic type is required, as it will not understand which part of main object sub key is associated with.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.