When it comes to dealing with signature overload, the behavior of Record and Map may not align

This scenario may seem straightforward, but it's causing confusion. I have a function with an overloaded signature that can accept either a Record or a Map. However, even though I am passing a Map as an argument, TypeScript is treating it as a Record. Why is this happening and how can I resolve it?


function doStuff(arg: Record<any, any>): Record<any, any>;
function doStuff(arg: Map<any, any>): Map<any, any>;
function doStuff(arg: any) {
  return arg;
}
    
let m: Map<any, any> = doStuff(new Map<any, any>());
// error ! m is returned as a Record

For more details, check out the playground

Answer №1

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.

Answer №2

Record<any, any> denotes a type, whereas Map<any, any> represents a value that is an extension of Record. When providing a Map value, the first matching signature in the function call is used, resulting in Typescript interpreting dostuff(Record<any, any>) as returning a Record<any, any>.

Due to the aforementioned reason, Record cannot be assigned to Map, leading to an error in Typescript.

To resolve this issue, it is advised to overload with either Record or Map.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is seamless integration possible between Angular2 and jQuery plugins?

When attempting to integrate jQuery plugins with Angular 2, there are three distinct scenarios: 1. ONLOAD: Initializing the plugin on page load works smoothly with the following code snippet: ngAfterViewChecked(){ ... $('#somedatatable1&apos ...

Strategies for Obtaining a Response Following a Function Execution in React JS

Hello, I am encountering an issue where I am calling a function and trying to get a response, but it is returning undefined. The response is being passed from the parent component to the child component. Here is the code for the component: import React fr ...

React JS state that points to another state value

I am trying to create a component that displays a list of products along with individual list items for each product. However, I keep encountering the following error: TypeError: Cannot read property 'products' of undefined new ProductList src/c ...

Error: The default export is not a component compatible with React in the specified page: "/"

I'm facing an issue while building my next app. Despite using export default, I keep encountering an error that others have mentioned as well. My aim is to create a wrapper for my pages in order to incorporate elements like navigation and footer. vi ...

Wondering how to leverage TypeScript, Next-redux-wrapper, and getServerSideProps in your project?

Transitioning from JavaScript to TypeScript for my codebase is proving to be quite challenging. // store.ts import { applyMiddleware, createStore, compose, Store } from "redux"; import createSagaMiddleware, { Task } from "redux-saga"; ...

Formik QR code reader button that triggers its own submission

I recently developed a custom QR code reader feature as a button within the Formik component customTextInput.tsx, but I encountered an issue where clicking on the button would trigger a submission without any value present. The following code snippet show ...

What is the most efficient way to simultaneously check multiple variables for undefined values?

Before executing my code, I need to ensure that none of the variables in a given list are undefined. In the code snippet below, there are 4 variables with uncertain values. While I can manually check variables a and b to satisfy TypeScript's requirem ...

Learn how to use sanitizer.bypassSecurityTrustStyle to apply styling to Pseudo Elements before and after in a template

Currently, I am attempting to add style to a pseudo element :after <a class="overflow">{{item?.eco}}</a> My goal is to modify the background color of a:after, and I believe this adjustment needs to be made in HTML. I've been thinking ...

Count duplicated values in an array of objects using JavaScript ES6

I am working on creating a filter for my list of products to count all producers and display them as follows: Apple (3) I have managed to eliminate duplicates from the array: ["Apple", "Apple", "Apple"] using this helpful link: Get all non-unique values ...

Azure pipeline failing to run Visual Studio 2017 task because of outdated Typescript SDK version

The Visual Studio 2017 build encounters an error during the build process src\Service\node_modules\utility-types\dist\aliases-and-guards.d.ts(10,51): Error TS2304: Build:Cannot find name 'bigint This issue is specific to the ...

TypeScript: The capability to deduce or derive values for a type from a constant object literal that is nested with non-distinct keys

I'm trying to figure out a way to utilize TS/IDE to display specific literal values from a style guide object, instead of just the inferred type. Here's an example: const guide = { colors: { black: { medium: "#000000", } ...

Obtaining a string union value dynamically during execution

Question 1: Exploring a component library, material-ui, which offers interfaces and types for customizing the css class values of each component. For the Select component, they define a type as a combination of string literals type SelectClassKey = " ...

What is the reason behind Typescript not narrowing generic union type components when they are eliminated by type guards?

Consider the following scenario where type definitions and function implementations are provided: interface WithNumber { foo: number; } interface WithString { bar: string; } type MyType = WithNumber | WithString; interface Parameter<C extends My ...

Blob is unable to open the PDF file as it is not

Currently, I am utilizing ASP.NET Core and Angular to create a web application. In one of the controller actions, I am returning a File like this: return Ok(Microsoft.AspNetCore.Mvc.File(await GetFileContents(args), "application/pdf")); Then in TypeScript ...

Flow - secure actions to ensure type safety

Currently implementing flow and looking to enhance the type safety of my reducers. I stumbled upon a helpful comment proposing a solution that seems compatible with my existing codebase: https://github.com/reduxjs/redux/issues/992#issuecomment-191152574 I ...

Develop a binary file in Angular

My Angular application requires retrieving file contents from a REST API and generating a file on the client side. Due to limitations in writing files directly on the client, I found a workaround solution using this question. The workaround involves crea ...

Adding properties to request object on-the-fly - Google Pay [Typescript]

Here is how I am constructing the payment request object: paymentRequestObject: google.payments.api.PaymentDataRequest = { apiVersion: 2, apiVersionMinor: 0, allowedPaymentMethods: [ { type: 'CARD', parameters: { allowedAuthMethod ...

The Angular HttpClient Service will exclusively provide responses that have a status code of 200

I'm facing an issue with my login component where it calls an http client service to send a request to the server for logging in. Everything works smoothly when I enter valid credentials, but if I input wrong credentials, the service doesn't seem ...

Error in TypeScript not being caught in React due to incorrect type detection

Could you assist me in grasping the workings of TypeScript? I am currently trying to learn it but am struggling with understanding certain behaviors. Example 1: The issue lies in the type errors not being detected, please refer to the commented message wi ...

Utilize your custom types within an npm package without the need to release them to @types

I recently came across this npm package (https://www.npmjs.com/package/react-web-notification) and decided to integrate it into my project. To do so, I created an index.d.ts file within the node_modules/@types/react-web-notification folder: import * as Re ...