user1543574
user1543574

Reputation: 863

How to preserve type when plucking from object, but account for undefined keys?

Consider the below code, the code const foundKey = classDict[key] should have the type object | undefined, because it's possible to enter in a key that's not part of the dict object (e.g. dict['bar']).

However, I can't seem to figure out how to accomplish this. Does anybody how to accomplish this?

Function

function classMatcher<
  K extends keyof T,
  T extends baseClassDict_T
>(keys: K[], classDict: T): T[K] | undefined {
  for (let key of keys) {
    const foundKey = classDict[key]; // <---- Type = object, should equal object | undefined
    if (foundKey !== undefined) return foundKey;
  }
  return undefined;
}

type baseClassDict_T = {
  [key: string]: {
    component: (prop: any) => JSX.Element;
  };
};

Usage

const keys = ['foo', 'bar'];
const dict = {
    foo: {
        component: //misc...
    }
}
classMatcher(keys, dict)

Upvotes: 0

Views: 133

Answers (1)

yqlim
yqlim

Reputation: 7080

I think you've missed the point here. If everything is typed properly, there would be no possibility that it will evaluate to undefined. Let me explain.

From this type:

type baseClassDict_T = {
  [key: string]: {
    component: (props: any) => JSX.Element;
  }
}

All key of an object of this type will always be a string.

Next, the important part:

function classMatcher<K extends keyof T, T extends baseClassDict_T>(keys: K[], classDict: T)

The generic K extends keyof T basically says that K will always be a string, and this string will always be one of the properties from T.

What this means is that the argument keys can never accept any strings that is not a property of argument classDIct. Meaning this code:

classMatcher(['a', 'b', 'c'], {
  a: { component: <></> },
  c: { component: <></> }
});

Will always have a typing error and never get past type checking because the key "b" is not present in classDict object.

Therefore, the line:

const foundKey = classDict[key];

Will never evaluate to undefined.

See this playground for the explanation in action.

Upvotes: 1

Related Questions