Enhanced string key indexer type safety in TypeScript

Discover and explore this online TypeScript playground where code magic happens:

export enum KeyCode {
  Alt = 'meta',
  Command = 'command',
  // etc.
}

export type KeyStroke = KeyCode | string;

export interface Combination {
  combination: KeyStroke[];
}

export interface Sequence {
  sequence: KeyStroke[];
}

export type ShortcutItem = KeyStroke | KeyStroke[] | Combination | Sequence;

export interface Shortcut {
  [key: string]: ShortcutItem;
}

export type ShortcutMap =
  | {
      [key: string]: Shortcut;
    }
  | Shortcut;

export const buildShortcuts = (map: Shortcut) => {
  return []
}

function getShortcuts(shortcutMap: ShortcutMap, mapKey?: keyof typeof shortcutMap){
  const map = mapKey ? shortcutMap[mapKey] : shortcutMap;
  return buildShortcuts(map);
}

export const shortcutMapWithoutKey: ShortcutMap = {
  MOVE_LEFT: [KeyCode.Alt, 'a'],
  MOVE_RIGHT: [KeyCode.Command, 'd'],
};

export const shortcutMapWithKey: ShortcutMap = {
  one: {
    MOVE_UP: [KeyCode.Alt, 'b'],
    MOVE_DOWN: [KeyCode.Command, 'e'],
  },
  two: {
    MOVE_HERE: [KeyCode.Alt, 'c'],
    MOVE_THERE: [KeyCode.Command, 'f'],
  }
};

const a = getShortcuts(shortcutMapWithoutKey);
const b = getShortcuts(shortcutMapWithKey, "one");

The current implementation of the ShortcutMap type may need further refinement to enhance union type narrowing.

Do you think there's a way to achieve better type safety by narrowing down the union?

An error is reported on this line:

  return buildShortcuts(map);

Argument of type 'string | Combination | string[] | Sequence | Shortcut | { [key: string]: Shortcut; }' cannot be assigned to parameter of type 'Shortcut'. The type 'string' is not compatible with type 'Shortcut'.

Answer №1

It seems that part of the confusion stems from how you have defined your ShortcutMap.

export type ShortcutMap =
  | {
      [key: string]: Shortcut;
    }
  | Shortcut;

In my opinion, a map should simply be:

{
      [key: string]: Shortcut;
}

To avoid this confusion, consider making your types more explicit and narrow by eliminating the union type.

You can experiment with different approaches using this TypeScript playground.

I hope this clarifies things for you.

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

Having trouble retrieving the JSON data from the getNutrition() service method using a post request to the Nutritionix API. Just started exploring APIs and using Angular

When attempting to contact the service, this.food is recognized as a string import { Component, OnInit } from '@angular/core'; import { ClientService } from '../../services/client.service'; import { Client } from '../../models/Cli ...

Typescript for managing the Shopify admin API

Is there anyone who can confirm whether Shopify offers typescript definitions for their admin API? I'm specifically interested in finding types for Orders, Products, and Variants. I initially assumed that this package would have them, but it seems l ...

Angular 8 ngFor not displaying expected information from object

I have a set of data which includes IDs, versions, and user details for Paul and Peter. See below: var data = { id: 1 version: 1 user: [ { name: 'paul' }, { name: 'peter' ...

Should mutators be encapsulated within a class contained in a JS Module for better code organization and maintainability?

In order to maximize functionality of our new product using JavaScript, we have implemented an Authentication module that manages a tokenPromise which is updated upon user logins or token refreshes. It seems imperative to allow for mutation in this process ...

TSLint flagging a parsing issue in type guard while TypeScript compiler fails to pick up on any errors

I am facing an issue with my TypeScript method that includes a type guard: export function isArray(item: any): item is Array<any> { return item.constructor === Array; } After running tslint on the file, I encountered the following error message ...

What is the best way to dynamically insert values into a JSON object?

I'm currently working with a JSON object in Angular and I need to dynamically add values that the user enters. Despite searching extensively, I haven't found a straightforward method to achieve this. I simply want to understand how to append key- ...

What materials are required in order to receive messages and information through my Contact page?

Currently, I am pondering the optimal method for gathering information from my Contact page. I have already created a form; however, I'm unsure how to send the gathered data to myself since I am relatively new to Web development. Angular is the framew ...

Transfer Typescript Project to Visual Studio Code

When I first started my project, I used the Typescript HTML Application Template project template. It worked well and set up a project for me. However, now I want to transition to using VSCode. The issue I'm facing is figuring out which switches and c ...

Updates to TypeScript 2.3.1 creating disruptions in SystemJS plunk

Check out this official Angular + TypeScript plunk using SystemJS 0.19.31, now updated to TypeScript 2.3.0. However, changing the SystemJS configuration in the same plunk to TypeScript 2.3.1 or 2.3.2 'typescript': 'npm:<a href="/cdn-cgi ...

Encountering the error message `TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts"` with `ts-node` when the type is specified as module

After configuring absolute paths in my Express project and changing the type to module for using import, I encountered an error: TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" Below is the content of my tsconfig.json { &q ...

Creating folders and writing data to text files in Angular 4/5 with TypeScript: A tutorial

Is it feasible to create a folder, text file, and write data into that file in Angular5 using Typescript for the purpose of logging errors? Your expertise on this matter would be greatly appreciated. Thank you in advance! ...

I'm encountering difficulties utilizing ternary operators in TypeScript

I am struggling with ternary operators in TypeScript and need help understanding the issue. Please review the code below: const QuizQuestionContainer = ({ qa }: QuizQuestionContainerPropsType) => { const { question, option1, option2, option ...

Tips for having tsc Resolve Absolute Paths in Module Imports with baseUrl Setting

In a typescript project, imagine the following organizational structure: | package.json | tsconfig.json | \---src | app.ts | \---foobar Foo.ts Bar.ts The tsconfig.json file is set up t ...

Trouble with 'import type' declaration causing build issues in a Next.js project

Having trouble importing the Metadata type from the next module. The code snippet below is directly from the Next.js documentation. THE ISSUE client.js:1 ./app/layout.tsx:3:12 Syntax error: Unexpected token, expected "from" 1 | import React from 'r ...

Difficulties in Networking Requests Following Event Emitter Notification in an Angular Application

Within my Angular application, a network request is sent to retrieve filtered data based on user-selected filters. The function responsible for handling the filter values and executing the request is outlined as follows: public onFilterReceived(values) { ...

Tips for customizing the list of components and attributes for each component in the Angular Form.io builder

I have successfully integrated form.io with Angular 10. After creating a demo project using form.io in the Angular CLI, I was able to develop a custom component and customize the editForm for it. import { Injector } from '@angular/core'; import ...

Establishing an efficient development environment with continuous integration for react-native using typescript and nodejs

Unfortunately, we encounter the challenge of working with different nodejs versions in our projects. I am unsure if this is similar to Java where multiple jdks can be installed (multiple nodejs installations), and each project automatically utilizes the co ...

How to simulate loadStripe behavior with Cypress stub?

I am struggling to correctly stub out Stripe from my tests CartCheckoutButton.ts import React from 'react' import { loadStripe } from '@stripe/stripe-js' import useCart from '~/state/CartContext' import styles from '. ...

Utilizing Node modules in TypeScript, Angular 2, and SystemJS: A Comprehensive Guide

In the process of developing a simple Angular 2 application, I am constructing a class named User. This class includes a function called validPassword() which utilizes the bcrypt library to validate user passwords: import { compareSync, genSaltSync, hashS ...

Tips for ensuring that the DOM is fully rendered before executing any code

Before proceeding to the next functions, it is necessary to wait for the DOM to finish rendering. The flow or lifecycle of this process is outlined below: Adding an item to the Array: this.someFormArray.push((this.createForm({ name: 'Name& ...