In summary: the best way to resolve this issue is to switch the order of your two call signatures like so:
function doStuff(arg: Map<any, any>): Map<any, any>;
function doStuff(arg: Record<any, any>): Record<any, any>;
function doStuff(arg: any) {
return arg;
}
The type Record<any, any>
(using the Record<K, V>
utility type) is essentially the same as {[x: string]: any}
:
type RecordAnyAny = Record<any, any>;
// type RecordAnyAny = {[x: string]: any}
This type represents an object-like structure with a string
index signature that can have any keys and values.
let r: RecordAnyAny
r = {}; // acceptable
r = {a: 0, b: "one", c: true}; // acceptable
r = new Date(); // acceptable;
r = new Map(); // also acceptable
Even a Map
object can be assigned to Record<any, any>
.
In contrast, the Map<K, V>
type is defined as an interface with various methods:
interface Map<K, V> {
clear(): void;
delete(key: K): boolean;
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
get(key: K): V | undefined;
has(key: K): boolean;
set(key: K, value: V): this;
readonly size: number;
}
Map<any, any>
equates to this interface with any
substituted for both K
and V
.
It's important to note that Map<any, any>
is more specific than
Record<any, any></code. Therefore, swapping the order of your call signatures is crucial. This approach is also recommended in <a href="https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#ordering" rel="nofollow noreferrer">the TypeScript Handbook</a>:</p>
<pre><code>// call signatures
function doStuff(arg: Map<any, any>): Map<any, any>;
function doStuff(arg: Record<any, any>): Record<any, any>;
// implementation
function doStuff(arg: any) {
return arg;
}
m = doStuff(new Map<any, any>()); // works
r = doStuff({a: 0, b: "one", c: true}); // also works
By changing the order, the compiler selects the correct overload based on the argument provided, ensuring the code functions as intended.