How do I insert items into an ES6 Map using a specific object key/value type in Typescript?

I'm looking to utilize Maps instead of object maps to define keys and values. However, it appears that Typescript doesn't fully support index types for ES6 Maps. Are there any alternatives or workarounds available?

Furthermore, I want to enforce type safety for the values in the Map, ensuring that each entry corresponds to the correct key.

Below is some pseudocode outlining what I hope to achieve:

type Keys = 'key1' | 'key2';

type Values = {
  'key1': string;
  'key2': number;
}

/** Should trigger a missing entry error */
const myMap = new Map<K in Keys, Values[K]>([
  ['key1', 'error missing key'],
]);

/** Should trigger a wrong value type error for 'key2' */
const myMap = new Map<K in Keys, Values[K]>([
  ['key1', 'okay'],
  ['key2', 'error: this value should be number'],
]);

/** Should pass */
const myMap = new Map<K in Keys, Values[K]>([
  ['key1', 'all good'],
  ['key2', 42],
]);

Edit: additional code relating to my specific scenario

enum Types = {
  ADD = 'ADD',
  REMOVE = 'REMOVE',
};

/** Seeking type-safety and autocompletion for the payload parameter */
const handleAdd = (state, payload) => ({...state, payload});

/** Want to guarantee that all types in Types are covered */
export const reducers = new Map([
  [Types.ADD, handleAdd],
  [Types.REMOVE, handleRemove]
]);

Answer №1

My approach is to create a new interface called ObjectMap, tailored to the object type O for defining key/value relationships. This way, the Map constructor doubles as an ObjectMap constructor. The goal is to simplify the use of K extends keyof O and O[K] to represent types as seen in standard Map<K, V>.

Creating the constructor involves meticulous type juggling to ensure type safety. First, let the compiler infer the constructor's return type:

const myMapInferredType = new ObjectMap([
  ['key1', 'v'], 
  ['key2', 1],  
]);

const myMap: ObjectMap<Values> = myMapInferredType;

If the inferred type doesn't align with ObjectMap<Values>, errors will be flagged when assigning it to myMap.

By using myMap as an ObjectMap<Values> akin to a Map instance with get() and set() functions, you can maintain type safety. However, note that this approach may be overly complex for situations where a plain object would suffice.

View in Playground

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

There seems to be a contradiction in my code - I am returning a Promise but TypeScript is throwing an error saying that the

I currently have a function that retrieves a bot's inventory on the Frontend fetchBotInventory() { this.socket.emit('fetch bot inv'); this.socket.on('bot inv', (botInventory) => { return new Promise((resolve, re ...

Issues with Retrieving Nested Arrays

I am encountering an issue with an object within an array and I am looking to specifically display that as an array. data1 const data1 = [ { "id": "01", "info": "fefef", "sub": "hieei&qu ...

Encountered an issue when utilizing the useRef hook in Next.js version 13

I am currently exploring nextjs13 and typescript. I encountered an issue when attempting to use the useRef hook. Below is the code snippet in question: import { useEffect, useRef } from "react" export const useDraw = () => { const canvas ...

The JsonFormatter is throwing an error because it is trying to access the property 'on' of an undefined variable

I have encountered an error while attempting to generate an HTML report using cucumber-html-reporter The error message is: Unhandled rejection TypeError: Cannot read property 'on' of undefined at new JsonFormatter (C:\path-to-project\ ...

Error: Class cannot be loaded by React Webpack loader

I'm encountering an issue with my two typescript packages - a React application and an infrastructure package. The React app has a dependency on the infrastructure package (currently linked via npm). After adding a new class to the infrastructure pack ...

The function `find()` will not provide any data, it will only output `undefined`

I am trying to extract the `s_id` field from this JSON data: { "StatusCode": 0, "StatusMessage": "OK", "StatusDescription": [ { "s_id": "11E8C70C8A5D78888E6EFA163EBBBC1D", "s_serial": " ...

Leveraging Renderer in Angular 4

Understanding the importance of using a renderer instead of directly manipulating the DOM in Angular2 projects, I have gone through multiple uninstallations, cache clearings, and re-installations of Node, Typescript, and Angular-CLI. Despite these efforts, ...

Encountering issue in Angular 14: Unable to assign type 'Date | undefined' to type 'string | number | Date' parameter

I have been working on an Angular 14 project where I am implementing a Search Filter Pipe. Below is the code snippet I am using: import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'transferFilter' }) export class Trans ...

Adjust the input width dynamically in Angular

Looking to dynamically adjust the width of an input field and ensure that the suffix "meters (m)" sticks close to the entered number. Additionally, I want to pass a specific value to the input using `value="something"`, which then should expand the input w ...

Encountered an issue while attempting to assign a value to a property that is declared as [key in keyof T]

I am currently working on implementing a function that selects specific properties of an object. Here is the current function: const project = function<T>(object: T, projection: Projection<T>): Partial<T> { throw new Error("not imple ...

Querying with Node SQLite fails to return a value

So, here's my little dilemma: I have 3 methods that need to access a database file (SQLite3). export function F_SetupDatabase(_logger: any): void export function Q_RunQuery(query: string, db: "session" | "global"): any export func ...

Changing the default headless browser to chrome in Cypress: A step-by-step guide

Currently I am utilizing the Cypress framework with TypeScript for my testing. When I execute the command "npx cypress run," all of my tests are running in headless mode using Electron as the default browser. However, I am interested in running them in C ...

tslint: no use of namespace and module is permitted

I have recently acquired a legacy .ts file that I would like to update. Two warnings appear: 'namespace' and 'module' are disallowed and The internal 'module' syntax is deprecated, use the 'namespace' keyword ins ...

What could be causing the presence of a "strike" in my typescript code?

While transitioning my code from JavaScript to TypeScript for the first time, I noticed that some code has been struck out. Can someone explain why this is happening and what it signifies? How should I address this issue? Here's a screenshot as an exa ...

How to ensure Angular mat-button-toggle elements are perfectly aligned within their respective groups

https://i.stack.imgur.com/Wjtn5.png Hello there, I'm trying to make the numbers in the first group match the style of the second group (noche, mañana...). I've set both the group and individual element width to 100%, but the numbers beyond 22 ...

Containerizing Next.js with TypeScript

Attempting to create a Docker Image of my Nextjs frontend (React) application for production, but encountering issues with TypeScript integration. Here is the Dockerfile: FROM node:14-alpine3.14 as deps RUN apk add --no-cache tini ENTRYPOINT ["/sbin ...

The Observable<ArrayBuffer> type does not have a property map

I encountered an issue while upgrading from Angular v4 to Angular v6. I was in the process of replacing Http and HttpModule with HttpClient and HttpClientModule. As a result, I imported HttpClient from @angular/common/http in a service to fetch results fro ...

Merge generic nested objects A and B deeply, ensuring that in case of duplicate properties, A's will take precedence over B's

Two mysterious (generic) nested objects with a similar structure are in play: const A = { one: { two: { three: { func1: () => null, }, }, }, } const B = { one: { two: { three: { func2: () => null, ...

Implementing an All-Routes-Except-One CanActivate guard in Angular2

In order to group routes within a module, I am aware that it can be done like this: canActivate: [AuthGuard], children: [ { path: '', children: [ { path: 'crises', component: ManageCrisesComponent }, ...

Exploring Angular: Looping through an Array of Objects

How can I extract and display values from a JSON object in a loop without using the keyValue pipe? Specifically, I am trying to access the "student2" data and display the name associated with it. Any suggestions on how to achieve this? Thank you for any h ...