Issue
Once I add an overloaded version of a method in an interface, I get errors. However, if I place it before the existing version, I don't. What is the reason? Where can I get more info on this? Is this a matter of the order? Or something else?
interface Item {}
interface MyCache {
// set(key: string, item: Item): void;
// ^ uncomment the line and it works
set(record: Record<string, Item[]>): void;
// set(key: string, item: Item): void;
// ^ uncomment the line and you get the errors
}
interface State {
cache: Record<string, Item[]>;
}
function useCache<C>({
state,
cache,
}: {
state: { cache: C };
cache: { set: (cacheValue: C) => void };
}) {
const { cache: cacheValue } = state;
cache.set(cacheValue);
}
function doSomething(state: State, cache: MyCache) {
useCache({ state, cache });
// the errors ^ are ^ here
}
Solution
TypeScript does indeed care about the order of call signatures in an overloaded function. The obvious dependency is that when you call an overloaded function, the type checker will resolve the call to the first matching call signature, which could result in different behavior if multiple call signatures match:
declare function f(x: string | number): string;
declare function f(x: Date | number): Date;
const x = f(123);
// ^? const x: string;
vs
declare function f(x: Date | number): Date;
declare function f(x: string | number): string;
const x = f(123);
// ^? const x: Date;
But there are other dependencies. Generally speaking the compiler is only able to perform overload resolution when you call the function directly. Otherwise, when you infer from overloaded function types, it just picks one of the call signatures. It can't pick "the best" one, since this would require a much more complex inference algorithm. As mentioned in this comment on Microsoft/TypeScript#30369:
In general we can't do overload resolution at the same time as inference and will usually be working with either the first or the last overload, depending on the situation
In your code example, the call to useCache({ state, cache })
triggers type argument inference for the C
generic type parameter. And apparently, in this situation, the first overload is selected. That results in C
being string
, and thus an error. If you change the order of the overloads, this problem goes away.
So that's what's going on.
Note that you could keep the call signatures as-is and still call useCache()
successfully, as long as you don't require the compiler to infer C
for you. You could just explicitly specify it yourself, like this:
useCache<Record<string, Item[]>>({ state, cache }); // okay
That works because the compiler is able to verify that cache
has a call signature of the proper type. It just can't infer it for you.
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.