Make function param accepts only keys from variable object

Viewed 271

I have an object with defined type for value:

type Type = { [key: string]: ValueType }

const variable: Type = {
    key1: valueType,
    key2: valueType,
    key3: valueType,
}

And I have a function func, which I want to be accepting only string with values from variable's keys:

func('key1')     // OK
func('key2')     // OK
func('key3')     // OK
func('keyother') // Error
func(3)          // Error

And this is what I have done when making type for func:

type FuncType = (param: keyof typeof variable) => any
const func: FuncType = ...

But I can only achieve one:

  • typing for variable's value

or

  • typing for func's param accept only variable's key

Not both.

  • If I'm typing for variable's value const variable: Type = {, param has string type and I can pass any string to func call, which is wrong
  • If I'm not typing for variable's value const variable: Type = {, func now typing param correctly but it makes variable accept anything as value.

Another way I can think about is predefined Type with list of keys ([key1, key2, ...]). But I don't want to maintain two list of the same thing. How can I achieve both of them without doing this way.

Typescript playground for this problem, which has some comments to describe problem more clearfully.

2 Answers

You have to use helper function that does nothing just validates type.

const makeType = <T extends Type = Type>(obj: T) => obj

const variable = makeType({
    key1: 0,
    key2: 0,
    key3: 0,
})

Playground

I would consider BorisTB's solution quite good, since it's intuitive and straightforward, and to the best of my understanding fulfills the requirements you've set in your question.

When you say the solution didn't work, I presume you mean "While both problems are detected if they are individually occurring (invalid key value in variable, invalid keyname in func), if they occur together you don't get errors for both at the same time".
If that's the problem, here is a slightly different solution:

Playground

The way it basically works is to do an additional check on variable after it is declared with it's const type.

const variable = { 
    key1: 0,
    key2: 0,
    key3: 'wrong type value',
} as const;
variable as Type;

This additional check compiles to just variable on it's own, so it's not too disruptive either. You do get the same level of detail in the error as you would expect with just typing variable as Type, just with the added benefit of preserving the const type of variable.
If both invalid value in variable or a invalid argument to func is used, you get errors for both.

Related