4

I have a very simple case, but my experience with Typescript typings is limited and I cannot seem to solve this seemingly simple case.

I have a type map for example like this:

interface KeyValueMap {
  key: 'value';
  foo: 'bar';
}

Now I would like to type the first and second argument of a function to the key and value of the above map

const test = <K extends keyof KeyValueMap>(key: K, value: KeyValueMap[K]) => {
  switch (key) {
    case 'key':
      return value; // currently getting "KeyValueMap[K]" expecting "value"
    case 'foo':
      return value; // currently getting "KeyValueMap[K]" expecting "bar"
  }
};

I tried to search for a similar case, but it seems my Google is broken... So no offense taken when marked as duplicate if such example exists already on Stackoverflow.

UPDATE

After the comment from @ShivamSingla under my question I realize I might have not been clear enough in my question. I am not interested in the return values of the function, but would already like the type to be recognized in the actual function logic (within the switch-case). I will change the example to make it more clear:

interface KeyObjectMap {
  foo: {
    key1: 'value1';
  };
  bar: {
    key2: 'value2';
  };
}

const test = <K extends keyof KeyObjectMap>(key: K, value: KeyObjectMap[K]) => {
  switch (key) {
    case 'foo':
      return value.key1; // property 'key1' does not exist on 'KeyObjectMap[K]'
    case 'bar':
      return value.key2; // property 'key2' does not exist on 'KeyObjectMap[K]'
  }
};

Here you can find a Playground with this example

2
  • add a default case too. Currently, it is showing not all the code path return the value. Playground Commented Oct 15, 2020 at 19:54
  • Why dont you make the "key1" and "key2" as just "key" so that typescript can get that parameter? Commented Oct 16, 2020 at 18:07

1 Answer 1

3

Since key and value are not associated, value can be re-assigned, before switch case. That's why the error. See this answer for better understanding.

interface KeyObjectMap {
  foo: {
    key1: 'value1';
  };
  bar: {
    key2: 'value2';
  };
}

type K1 = keyof KeyObjectMap

const test = <K extends K1 = K1>(key: K, value: KeyObjectMap[K]) => {
  // error here, 'key2' is missing
  value = {
    key1: 'value1',
  }

  // value is actually intersection, subtype `KeyObjectMap` actually
  value = {
    key1: 'value1',
    key2: 'value2',
  }

  // since `key` and `value` are not associated, value can be re-assigned
  // before switch case. That's why the error

  switch (key) {
    case 'foo':
      return value.key1; // property 'key1' does not exist on 'KeyObjectMap[K]'
    case 'bar':
      return value.key2; // property 'key2' does not exist on 'KeyObjectMap[K]'
  }
};

Playground

Possible solution

interface KeyObjectMap {
  foo: {
    key1: 'value1';
  };
  bar: {
    key2: 'value2';
  };
}

const test = <O extends Partial<KeyObjectMap>>(obj: O) => {
  if (obj.foo) {
    return obj.foo.key1
  }

  if (obj.bar) {
    return obj.bar.key2
  }

  return undefined
};

// calling
const c = test({foo: {key1: 'value1'}})

Playground

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

Comments

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.