I'm not a typescript expert and I am stuck trying to figure out the following error:
Argument of type '({ keys, fire }: { keys?: any; fire: any; }) => void' is not assignable to parameter of type '(value: unknown, value2: unknown, set: Set) => void'. Types of parameters '__0' and 'value' are incompatible. Type 'unknown' is not assignable to type '{ keys?: any; fire: any; }'.
The block of code is:
listeners.forEach(({ keys, fire }: {keys?: any; fire: any}) => {
//some code
});
Listeners is a set like:
const listeners = new Set();
and I am pushing an object into it like so:
const create = (keys) => {
//keys can be undefined
const listener = {
keys,
fire: useState()[1],
};
listeners.add(listener)
}
If I remove the fire or the keys from the forEach I still get the same error and I can't figure out why this is happening.
CodePudding user response:
The TypeScript typings for Set
define it as Set<T>
, an interface with a generic type parameter corresponding to the elements of the set. If you invoke the Set()
constructor without providing any type information, the TypeScript compiler will try to infer this type parameter.
For example, if you construct a Set
and initialize it with an array, the compiler will use the array elements to infer the type parameter:
const setOfStuff = new Set([{ a: 1, b: 2 }, { a: 3, b: 4 }]);
/* const setOfStuff: Set<{
a: number;
b: number;
}> */
But if you invoke the constructor with no arguments, the compiler has nothing from which to infer the type of the set elements, and so it falls back to a default of the unknown
type:
const oops = new Set();
// const oops: Set<unknown>
The unknown
type can accept any value whatsoever, so it's very easy to supply a value of type unknown
. But on the flip side, since an unknown
value can be absolutely anything, it's very hard to consume such a value. The compiler complains that you are treating the elements of your set as if they were of type { keys?: any; fire: any; }
when all it knows is that they are of type unknown
.
There are times when unknown
is a reasonable type to use, but in your case, you probably only intend for your set to hold values of type { keys?: any; fire: any; }
and nothing else. If so, then you can explicitly specify the generic type parameter when constructing your set, and thus strongly type it:
const listeners = new Set<{ keys?: any; fire: any; }>();
/* const listeners: Set<{
keys?: any;
fire: any;
}> */
After this, you can call the forEach()
method and the compiler will know that each element is of type { keys?: any; fire: any; }
, as desired:
listeners.forEach(({ keys, fire }) => {
//some code
}); // no error