I was trying to type the parameters of a function so that the compiler can perform checks on both correctly. To demonstrate, say I have the type Item and a function f that needs to be typed:
type Item = {
Book: { price: number };
Box: { weight: number };
}
const f = (param1, param2): void => { ... }
What I want to achieve is:
// correct: both name and related attributes are right
f('Book', { price: 20 })
f('Box', { weight: 10 })
// error: 'Chair' is not a 'Book' or 'Box'
f('Chair', { price: 20 })
// error: 'Book' should be provided with a price
f('Book', { weight: 20 })
f('Book', { foo: 20 })
I tried to type f as follows:
const f = (param1: keyof Item, param2: Item[keyof Item]): void => { ... }
Which mostly worked, except that
f('Box', { price: 200 })
did not raise a compiler error.
How should I go about linking the two parameters as described?
CodePudding user response:
So the way you want to go around this is by having param2 change based on param1. Right now what you are saying is param1 is some key of Item, and param2 is some value of item that matches any key, NOT specifically the key param1.
Instead what you want is param2 to be the typeof Item[param2]. I would do this with generics.
type Item = {
Book: {price: number};
Box: {weight: number;}
};
const f = <K extends keyof Item, E extends Item[K]>(param1: K, param2: E): void => {}
f('Box', {price: 5})
This throws the following error:
Argument of type '{ price: number; }' is not assignable to parameter of type '{ weight: number; }'.
Object literal may only specify known properties, and 'price' does not exist in type '{ weight: number; }'.
But this does not:
f('Box', {weight: 5})
