Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using symbol as object-key type in TypeScript

I'm trying to define an object with a symbol as key-type since MDN says:

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

But using it as type for the key-property:

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

results in the following error:

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

even it can be used as index-type. I'm using the latest typescript version (v3.7.2), related questions I've found:

  • 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've also took a look at the typescript symbol docs but they only show how it's used as value, not as 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

like image 373
Simon Avatar asked Nov 30 '19 16:11

Simon


People also ask

Can a symbol be an object key?

Symbols can be used as object keys. Only strings and symbols can be used as object keys.

What is the use of symbol in TypeScript?

In Typescript symbol is a primitive data type. A primitive data type is not an object, it possesses no properties or methods and they cannot be altered. The symbol type is similar to other types such as number, string, boolean, etc. Symbol values are created using the Symbol constructor.

Is there an object type in TypeScript?

In TypeScript, object is the type of all non-primitive values (primitive values are undefined , null , booleans, numbers, bigints, strings). With this type, we can't access any properties of a value.


2 Answers

Unfortunately this is not possible at the moment in TypeScript. If you have to interoperate with some APIs that expect this or really want to use symbols as keys, you can do this awkward version:

// Ensure we can not pass regular map to our 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); // string   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 
like image 135
Dmitriy Avatar answered Oct 04 '22 17:10

Dmitriy


TypeScript 4.4 allows symbols in index signatures:

type SymbolIndex = {     [key: symbol | string]: string // works }  const sym = Symbol("descr"); const t1: SymbolIndex = {     "foo": "bar",     [Symbol.iterator]: "qux",     sym: "sym" };  // all result in string t1.foo  t1.sym  t1[Symbol.iterator] t1["oh"] 

Playground

With older versions, SymbolIndex will trigger an error:

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

Alternative

If you just want an object type with symbols and no index signature, you can already do that today:

const sym = Symbol() // note const (no let)  type O = {     foo: string     [Symbol.iterator]: string     [sym]: number }  let o: O = { [sym] : 3, [Symbol.iterator]: "bar", foo: "qux"}  let { [sym]: symVal } = o 

Playground

like image 44
ford04 Avatar answered Oct 04 '22 18:10

ford04