What is the best way to utilize the data structure `(K | V)[][]` for representing a `ReadonlyArray<[K, V]>` within a Map?

One interesting approach is to utilize a (K | V)[][] as ReadonlyArray<[K, V]> within a Map constructor. Suppose that V represents an interface IItem, while K is a basic type of number.

interface IItem {
    key: number;
    val: string;
}

const items = [[1, {key: 1, val: "a"}]] 
const ze_map = new Map<number, IItem>(items);
console.log(JSON.stringify([...ze_map]));

By default, typescript will interpret the type of items as : (number | IItem)[][]. This will result in an error:

Argument of type '(number | { key: number; val: string; })[][]' is not assignable to parameter of type 'ReadonlyArray<[{}, {}]>'.
Type '(number | { key: number; val: string; })[]' is missing the following properties from type '[{}, {}]': 0, 1

Fortunately, we can explicitly define the type as Array<[number, IItem]> to satisfy the requirements of Map.

const items2 : Array<[number, IItem]> = [[1, {key: 1, val: "a"}]] 
const ze_map2 = new Map<number, IItem>(items2);
console.log(JSON.stringify([...ze_map]));

This solution works perfectly. Now, let's address a common issue - what if we cannot enforce the type?

const arr = [
    { key: 3, val: 'bar' },
    { key: 5, val: 'world' }
];
const result: Array<[number, IItem]> = arr.map(i => ([i.key, i]));

const ze_map3 = new Map(result);
console.log(JSON.stringify([...ze_map]));

In this scenario, the code fails because

(number | { key: number; val: string; })[][]
does not align with Array<[number, IItem]>. So, how do we effectively use (K | V)[][] as ReadonlyArray<[K, V]> in a Map?

You can experiment with all the code snippets online here

I studied both How to define Map with correlation between a key type and a value type, while they both are unions and Typescript Map<enum, set<enum>> "No overload matches this call", but I don't get why? without finding a definitive solution.

I also referred to the MDN entry on Map, which suggests that it should function correctly. Testing in pure JS confirms this:

var arr = [
    { key: 3, val: 'bar' },
    { key: 5, val: 'world' }
];

var result = arr.map(i => [i.key, i]);
const ze_map = new Map(result);

console.log(JSON.stringify([...ze_map])); 

Give it a try here!

Answer №1

The issue lies within the body of the .map callback function. When a function returns an array, Typescript interprets the return type as a regular array type instead of a tuple type.

Avoid Using as

While using the as assertion may work, it can be risky because it instructs Typescript to treat the type as [number, IItem], even if it isn't truly that type. Instead, we should inform Typescript that we expect the type to be [number, IItem] and ask it to confirm. In this scenario, using as is unnecessary since the type is indeed [number, IItem]; we just need Typescript to recognize it as such.

Specify a Generic Type

The most straightforward solution is to specify the generic type parameter on the .map function. By setting the generic variable, we define the return type of the callback function as [number, IItem].

const result = arr.map<[number, IItem]>(i => [i.key, i]); // result's type is [number, IItem][]

const ze_map = new Map(result); // no errors :)

Extract the Callback Function

Another approach is to separate your map callback i => [i.key, i] into its own function so you can explicitly annotate the return type.

const toTuple = (i: IItem): [number, IItem] => [i.key, i];

const result = arr.map(toTuple); // result has type [number, IItem][]

const ze_map = new Map(result); // no errors :)

Typescript Playground Link

Answer №2

It seems there might be a more efficient approach to achieving this task, by utilizing type coercion within the map function:

const arr = [
    { key: 3, val: 'bar' },
    { key: 5, val: 'world' }
];
const result = arr.map(i => ([i.key, i] as [number, IItem])); // employing a clever trick here
const ze_map3 = new Map(result);
console.log(JSON.stringify([...ze_map3]));

The use of the as keyword serves as a Type Assertion instructing tsc to view the object as a different type (in this case, [K, V]) than what the compiler initially deduces it to be ((K | V)[]).

An interesting aspect is that the as [K, V] operation remains transparent in the resulting javascript output:

"use strict";
const arr = [
    { key: 3, val: 'bar' },
    { key: 5, val: 'world' }
];
const result = arr.map(i => [i.key, i]);
const ze_map3 = new Map(result);
console.log(JSON.stringify([...ze_map3]));

Give it a try Online

source

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

Rule in Eslint for Typescript that enforces explicit typing and prohibits the use of implicit "any

Starting a fresh typescript project with eslint, I'm facing an issue in setting up eslint rules for the tsc command to run smoothly without errors. Specifically, I'm encountering difficulty with the "noImplicitAny" rule defined in tsconfig.json, ...

Record compressed JavaScript bugs in live code

Currently, I am managing a back-end application with a React JS frontend on our company's intranet. I prefer not to utilize third-party services such as Sentry or Rollbar to monitor minified code in production since I am reluctant to share our source- ...

Extract information from a lengthy text (vcard)

While scanning data from a vcard QR-code, the string I receive always follows this format: BEGIN:VCARD VERSION:2.1 N:Lastname;Firstname FN:Firstname Lastname ORG:Lol Group TITLE:Project Engineer TEL;WORK:+32 (0)11 12 13 14 ADR;WORK:Industrielaan 1;2250 Ol ...

Is it possible to use Ajax to prompt a pop-up window for basic authentication when logging in?

While attempting to access the reed.co.uk REST web API in order to retrieve all related jobs, I am encountering an issue. Despite passing my username and password, a popup window keeps appearing when I call the URL. The alert message displayed reads: i ...

Utilizing the js-yaml library to parse a YAML document

Currently, I'm utilizing js-yaml to analyze and extract the data from a yaml file in node js. The yaml file consists of key-value pairs, with some keys having values formatted like this: key : {{ val1 }} {{ val2 }} However, the parsing process enco ...

Execute the PHP file using a script tag

Is there a way to include PHP code within <script>? For example, like this: `<script src="example.php">. I understand that the script tag is typically used for JavaScript, but I want PHP to generate JS that can be inserted using the s ...

typescript: textual depiction of a generic type T

I have a requirement to develop a method that is capable of handling generic data types, and I need to incorporate the type information into the method Our API requires passing the entity name as a parameter: http://server/api/fetch/Entity/123 It's ...

Accessing information from database using Javascript

I am facing a dilemma with my HTML and PHP pages. The HTML page contains a table, while the PHP page executes a query to a MySQL table. I am struggling to display the query results on my HTML page. The HTML page features the following code snippet: <t ...

The presentation of CSS and JavaScript

I'm having trouble pinpointing the error within my .css file. Currently, my JavaScript content is displaying on the far left of the page instead of centered as I desire. Specifically, I want the address and contact information to be centered below the ...

What is the best practice for importing React components?

In my experience with React, I've noticed two different ways of importing and extending components. The first way that I typically use is shown below. import React from 'react'; class SomeClass extends React.Component { //Some code } ...

Utilize the push method to form a new array

var teamMembers = [ ['John D. Adams', '1959-1967', 'Ohio'], ['Dawson Mathis', '1971-1981', 'Georgia'], ]; To generate this dynamically, I am implementing the code below: var data = ne ...

Guide on how to show or hide divs using keyword filters

My webpage features various cards with different keywords: <div class="item"> <div class="hashtags"> <span><a href="#">Technology</a></span> <span><a href="#">Innovation</a></spa ...

Run a command to engage with the user and then display a new page

Currently, I'm exploring the documentation in search of a solution for the following scenario: Imagine the user is on a form page and decides to go back. I want to prompt a verification message like "Are you sure you want to discard your changes?". D ...

Top method for displaying and concealing GUI elements upon a click event

I would like a dat.GUI() instance to appear when a mesh is clicked, and disappear when it is clicked again. Furthermore, if it is clicked once more, I want it to reappear. Despite trying various approaches, I have been unable to achieve the desired behavio ...

Array insertion is not supported by Node MSSQL

I am trying to insert multiple rows at once using an array of data. Here is how my array is structured: [ [ '1234' ], [ '5678' ], [ '9123' ]... ] And here is the query code I am using: const sql = require('mssql&a ...

"Utilizing jQuery and Bootstrap 4 in TypeScript, attempting to close modal window using jQuery is not functioning

Trying to make use of jquery to close a bootstrap modal within an angular project using typescript code. The following is the code: function call in html: (click)="populaterfpfromsaved(i, createSaved, createProp)" createSaved and createProp are local ...

Using $npm_package_ notation to retrieve information about the version of a private package

When using Npm, you can easily access package information from the package.json file by using a helpful prefix. For example, you can extract the react version with $npm_package_dependencies_react to find the current version of react listed in the file. Ho ...

How can a variable be quickly incremented in Javascript until it equals the value of another variable?

Currently, I have a code that incrementally increases the value of variable 'x' by 0.01 and performs calculations until it reaches the value of another variable 'y'. However, it is important to note that while 'x' is increasin ...

The noUnusedLocal rule in the Typescript tsconfig is not being followed as expected

I am currently working on a project that utilizes typescript 3.6.3. Within my main directory, I have a tsconfig.json file with the setting noUnusedLocals: true: { "compilerOptions": { "noUnusedLocals": true, "noUnusedParameters": true, }, ...

The anchor tag seems to be malfunctioning within the div container

<style type="text/css> .xyz { position: absolute; top: 120px; left: 160px; z-index: -1; } #container { position: relative; overflow: visible; opacity: .3; } </style> <body> <div> <video i ...