Leveraging symbols as object key type in TypeScript

I am attempting to create an object with a symbol as the key type, following MDN's guidance:

A symbol value may be used as an identifier for object properties [...]

However, when trying to use it as the key property type:

type obj = {
    [key: symbol | string]: string
}

I encounter the following error:

TS1023: An index signature parameter type must be either 'string' or 'number'.

even though it is possible to use it as an index type. I am working with the latest version of TypeScript (v3.7.2), and I have come across related questions:

  • Typescript: destructuring an object with symbols as keys (He's using an actual instance of a Symbol, I want the type symbol)
  • TypeScript: An index signature parameter must be a 'string' or 'number' when trying to use string | number
  • ES6: destructuring an object with symbols as keys (That can't be a solution - it seems kinda wrong to use an actual instance as type since every Symbol instance is unique...)

I have also consulted the typescript symbol docs, but they only demonstrate how it is used as a value, not as a type.

Example:

const obj = {} as {
    [key: number | symbol]: string // Won't work
};

const sym = Symbol('My symbol');
obj[sym] = 'Hi';

Issue on Microsoft/TypeScript

Open feature request

Answer №1

TypeScript 4.4 introduces the ability to use symbols in index signatures:

type SymbolIndex = {
    [key: symbol | string]: string // valid syntax
}

const sym = Symbol("descr");
const t1: SymbolIndex = {
    "foo": "bar",
    [Symbol.iterator]: "qux",
    sym: "sym"
};

// all values accessed are of type string
t1.foo 
t1.sym 
t1[Symbol.iterator]
t1["oh"]

Playground

In previous versions, defining SymbolIndex would result in an error:

An index signature parameter type must be either 'string' or 'number'.(1023)

Different Approach

If you simply need an object type with symbols without an index signature, it can be achieved using current TypeScript features:

const sym = Symbol() // declared as constant 
type O = {
    foo: string
    [Symbol.iterator]: string
    [sym]: number
}

let o: O = { [sym] : 3, [Symbol.iterator]: "bar", foo: "qux"}

let { [sym]: symVal } = o

Playground

Answer №2

Currently, TypeScript does not support using symbols as keys directly. However, if you need to work with APIs that specifically require or prefer symbol keys, there is a workaround available:

// To prevent passing regular maps to custom functions
type SymbolMapTag = { readonly symbol: unique symbol }

type SymbolMap = SymbolMapTag & {
    [Key in string | number | symbol]: string;
}

function set_symbol<T extends SymbolMap, TSym extends symbol>
(target: T, sym: TSym, value: T[TSym]) {
    target[sym] = value;
}

function get_symbol<T extends SymbolMap, TSym extends symbol>
(target: T, sym: TSym): T[TSym] {
    return target[sym];
}

const symbol_map = {} as SymbolMap;

const sym = Symbol('My symbol');
set_symbol(symbol_map, sym, "hi");
get_symbol(symbol_map, sym); // returns 'hi'


type NonSymbolMap = {
    [Key in string | number]: string;
}

const non_symbol_map = {} as NonSymbolMap;
set_symbol(non_symbol_map, sym, "hi"); // error
get_symbol(non_symbol_map, sym); // error

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

I'm having trouble asynchronously adding a row to a table using the @angular/material:table schematic

Having trouble asynchronously adding rows using the @angular/material:table schematic. Despite calling this.table.renderRows(), the new rows are not displayed correctly. The "works" part is added to the table, reflecting in the paginator, but the asynchron ...

JQuery .click Event doesn't center elements even with transform-origin adjustment

In the JSfiddle provided below, you can see that after a click event occurs, two span (block) elements rotate 45deg to form an "X". However, both elements are slightly shifted left, creating an off-center "X" relative to the parent's true center-origi ...

Res.redirect() function does not redirect the browser URL as expected when triggered by a request made through a frontend fetch() call

Encountering a new issue that is challenging me. In the backend, there is an API route that redirects the browser URL to /signin using res.redirect('/signin'). Upon this redirection, React Router triggers the rendering of a 'log back in&apos ...

Using regex in javascript to strip HTML tags

I'm trying to clean up my document by removing all HTML tags except for <a>, <img>, and <iframe> using the following code: var regex = "<(?!a )(?!img )(?!iframe )([\s\S]*?)>"; var temp; while (source.match(regex)) { ...

Having trouble with my findIndex function in Node.js while working with a mongo DB database

I am having difficulty finding the index of a specific product in a MongoDB database. const cart = await this.model.findOne({ user: { $eq: user } }); if (cart) { const itemFound = cart.products.findIndex( (item) => item._id === ...

JSON.stringify not behaving as anticipated

I am working on the code below; var data = []; data['id'] = 105; data['authenticated'] = true; console.log(data); var jsonData = JSON.stringify(data); console.log(jsonData); The initial console.log is displaying; [id: 105, authenti ...

Is there a way to determine the negative horizontal shift of text within an HTML input element when it exceeds the horizontal boundaries?

On my website, I have a standard HTML input field for text input. To track the horizontal position of a specific word continuously, I have implemented a method using an invisible <span> element to display the content of the input field and then utili ...

What could be causing the issue of rows being undefined?

Need help creating a user registration feature with Passport(Local-Signup)? Check out the code snippet below: // config/passport.js // requiring necessary modules var LocalStrategy = require('passport-local').Strategy; // loading the user mode ...

Connecting Angular directive to a controller

While diving into some Angular JS tutorials, I decided to apply what I learned in the Ionic framework. Unfortunately, I hit a roadblock when attempting to create a reusable HTML control because the model isn't syncing with the view as expected. Here&a ...

The Angular framework may have trouble detecting changes made from global window functions

While working, I came across a very peculiar behavior. Here is the link to a similar issue: stackblitz In the index.html file, I triggered a click event. function createClause(event) { Office.context.document.getSelectedDataAsync( Office.Coerci ...

Executing the JavaScript function on a batch of 6 IDs at once (should return every 6 calls?)

I'm curious if there's a function available that can group called data into sets of 6. Here's the expanded version of the code var currentResults; function init() { getProducts(); } function getProducts() { $.ajax({ url:" ...

Encountering an issue with the autocomplete feature in the jQuery library where it is stating "this.source is not a function."

Here's the code snippet I'm working with: $.ajax({ type: "GET", url: "https://url.com", dataType: "json", success: function (data) { $("#search").autocomplete({ source: data, select: function (even ...

Can a `react` app with `mysql` be uploaded to `github`?

I recently created a basic online store with the help of react, node, and mysql. I am considering uploading it to github, but I'm uncertain if I can do so while my database is currently stored on localhost. Any advice? ...

Testing vue-router's useRoute() function in Jest tests on Vue 3

Struggling with creating unit tests using Jest for Vue 3 components that utilize useRoute()? Take a look at the code snippet below: <template> <div :class="{ 'grey-background': !isHomeView }" /> </template> &l ...

The video rendered with fluent-ffmpeg appears to have an elongated image

I am working on combining an mp3 audio file with a jpg image file to create a new mp4 video. Although I have a functional fluent-ffmpeg command that accomplishes this task, there is an issue where the image gets stretched in the final output video, especia ...

Improper headings can prevent Chrome from continuously playing HTML5 audio

Recently, I encountered a peculiar and unlikely issue. I created a custom python server using SimpleHTTPServer, where I had to set my own headers. This server was used to serve .wav files, but I faced an unusual problem. While the files would play in an ...

Determining the exact number of immediate descendants within a ul element, while disregarding any script elements

Here is the HTML structure I am working with, which contains a script tag: <ul id="list"> <li class="item1"><a href="#">Item 1</a></li> <li class="item2"><a href="#">Item 2</a></li> <li ...

Is it possible to execute MongoDB queries from an AS3 client?

Can native Javascript functions for the mongo shell be run on server side from an AS3 client AIR app? I have experience running Javascript methods embedded/loaded in HTML where SWF is also embedded, and I'm curious if it's possible to make a ser ...

Incorporating a YouTube or Vimeo video while maintaining the proper aspect ratio

On my video page, I embed Vimeo videos dynamically with only the video ID. This causes issues with the aspect ratio as black bars appear on the sides due to the lack of width and height settings. The dynamic video ID is implemented like this: <iframe ...

Automatically navigate to the bottom of the page by applying the overflow auto feature

I've been working on a chat application using Vue.js. I have set the overflow property to auto for my div element. My goal is to automatically scroll to the bottom of the div so that users won't have to manually click the scrollbar to view the la ...