What could be causing the interface to malfunction when defining a type that includes an enum as a property key?

When dealing with an enum and wanting to use its values as keys in objects, the type declaration looks like this:

enum Bar {
    A, B
}

let dictionary: BarDictType = {
    [Bar.A]: "foo",
    [Bar.B]: "bar"
}

type BarDictType = {
    [key in Bar]: string
}

The above code works without any issues. But when attempting the same approach using an interface:

interface BarDictInterface {
    [key in Bar]: string
}

Error messages pop up:

TS1169: An interface's computed property name must refer to a literal type or unique symbol type.
TS2464: Computed property names for interfaces are restricted to 'string', 'number', 'symbol', or 'any' types.
TS2304: 'Key' cannot be found.

Why is there a disparity between using interface versus type in this scenario? What limitations, if any, exist for interfaces that led to this design choice?

Answer №1

Using mapped type keys (in the format [P in K]: T where K is a key-like type) as keys of an interface is not allowed in TypeScript. This can lead to confusing error messages due to misinterpretation by the compiler as using computed property declaration ([k]: U where k is a key-like value, not a type). An open issue has been raised at microsoft/TypeScript#18299 addressing these error messages.

The reason why mapped types cannot be used here is not explicitly mentioned in canonical documentation. Interfaces can have properties with statically-known string or numeric literal types, call signatures, construct signatures, method signatures, and index signatures, all of which are also permitted in class instance types. However, mapped types are not part of this list.


If you wish to create an interface from your mapped type, you can achieve this by extending the mapped type as a new interface. You can extend a named type with statically known keys, like so:

interface FooMapInterface extends FooMapType { } // okay

By doing this, you can observe that it functions correctly:

declare const fooMapInterface: FooMapInterface;
fooMapInterface[Foo.X].toUpperCase(); // okay

While you cannot directly write it, you can still attain this behavior through extension.

Link to code on the 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

Typescript's ability to have Enums with dynamic keys

Suppose I define: enum Sort { nameAsc = 'nameAsc', nameDesc = 'nameDesc' } Is it possible to do the following? const key = 'name' + 'Desc'; Sort[key] Appreciate any help in advance ...

Adjusting the audio length in React/Typescript: A simple guide

I'm currently developing a web app with React and TypeScript. One of the components I created is called SoundEffect, which plays an mp3 file based on the type of sound passed as a prop. interface ISoundEffectProps { soundType: string, // durat ...

Issue detected: Props that are of type Object/Array must utilize a factory function in order to provide the default value

I recently started using Vue-Cli3.0 and came across this interesting module for Vue.js called https://github.com/holiber/sl-vue-tree It's a customizable draggable tree component for Vue.js, but I encountered an issue where it couldn't copy funct ...

Issue with TypeScript when calling the jQuery getJSON() method

Currently, I am working with TypeScript version 1.7.5 and the latest jQuery type definition available. However, when I attempt to make a call to $.getJSON(), it results in an error message stating "error TS2346: Supplied parameters do not match any signatu ...

Download pictures from swift into typescript with the help of share extensions

Currently, I am working on setting up Share Extensions in my ionic3 application. To begin with, I followed these steps: Firstly, I built the app and then launched it in Xcode. After that, I added a Share Extension by navigating to File -> New -> Ta ...

Routing through routers and closing modals

Within my Angular project, I am using a form component across two separate components. What I want is for the onSubmit function to close a modal if the user selects 'Save' in one component, and to navigate using the router link if it's in th ...

Mastering the Art of Mocking Asynchronous Methods in Node.js Using Jest

I have the following files: |_ utils.js |_methods.js I am currently conducting unit testing for rest.js methods, where the content of the file is as follows: methods.js import Express from 'express' import { add_rec } from './utils' ...

Using Angular 2+ to make HTTP POST requests and interact with the GDrive API. Implementing resumable file uploads

I am currently facing a challenge with uploading files to Google Drive using Angular 2. I have managed to upload files successfully, but they end up being labeled as "Untitled" without any title. Below is the code snippet I am using for the upload process ...

Typescript: Removing signatures with a filter

I am encountering a TypeScript error stating that .filter has no signatures. I'm unsure of how to resolve this issue. interface IDevice { deviceId: string; deviceName?: string; } const joinRoom = ({ userId, deviceId, deviceName }: IRoomParams ...

Angular: proper dependency injection may not occur when appending .js or .ts at the end of an import statement

When it comes to import statements, the format is usually as follows: import {HelpService} from '../../help.service' It's worth noting that if I utilize autowiring to inject HelpService into the constructor, an already existing instance of ...

Tips for crafting a TypeScript function signature for a bijection-generating function

One of my functions involves taking a map and generating the bijection of that map: export function createBijection(map) { const bijection = {}; Object.keys(map).forEach(key => { bijection[key] = map[key]; bijection[map[key]] = key; }); ...

Having trouble consuming data from a service in Angular 6?

I'm in the process of creating a basic cache service in Angular; a service that includes a simple setter/getter function for different components to access data from. Unfortunately, when attempting to subscribe to this service to retrieve the data, t ...

Tips on efficiently utilizing stored information in Ionic and Angular applications

I am facing an issue where I can only access my variable inside the this.storage.get function. How can I retrieve this stored data? Here is the content of tab2.page.html: <ion-toolbar> <ion-title> Stats </ion-title> &l ...

AngluarFire 2 authState function displaying null after refreshing the page

Currently, I am working on integrating Firebase with AngularFire 2 and facing an issue. The problem arises when I try to refresh the page, as the auth instance returns null. Below is the code snippet for my AuthService: Everything functions correctly, b ...

Discovering the versatility of Typescript objects

I want to define a type that follows this rule: If the property container is present, then expect the property a. If the property item is present, then expect the property b. Both container and item cannot exist at the same time. The code I would expect ...

The 'RegExpExecArray | null' type is required to include a method '[Symbol.iterator]()' which returns an iterator to iterate through its values

As someone who is new to TypeScript, I have a good understanding of the basics but recently came across a typecast error that I am struggling to solve. const [full, id]: string | null = /.*media\/[^\/]+\/(.*)/.exec(item.uri) When it comes t ...

Accessing nested objects within an array using lodash in typescript

After analyzing the structure of my data, I found it to be in this format: {property: ["a","b"], value : "somevalue" , comparison : "somecomparison"} I am looking for a way to transform it into a nested object like so: { "properties": { "a": { ...

Update the router URL without switching pages, yet still record it in the browser history

One of the features on my search page allows users to perform searches and view results. Initially, I faced a challenge in updating the router URL without navigating, but I managed to overcome this by utilizing the "Location" feature. In my ngOnInit meth ...

Rollup faces challenges when trying to bundle source code alongside Bazel and Typescript

I am attempting to create a bundle using rollup, typescript, and bazel environment. I am having trouble importing relative paths. Typescript builds correctly but rollup is unable to bundle the source. WORKSPACE # WORKSPACE workspace( name = "WORK ...

Struggling to update TypeScript and encountering the error message "Unable to establish the authenticity of host 'github.com (192.30.253.113)'"

While attempting to update my version of TypeScript using npm, I ran into an issue when trying to execute the following command: localhost:Pastebin davea$ npm install typescript/2.8.4 --save-dev The authenticity of host 'github.com (192.30.253.113)&a ...