microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.39k stars 12.4k forks source link

{ [Symbol.toPrimitive]: () => Symbol }: support as index/property type #43428

Open mfulton26 opened 3 years ago

mfulton26 commented 3 years ago

Suggestion

πŸ” Search Terms

βœ… Viability Checklist

My suggestion meets these guidelines:

⭐ Suggestion

  1. Instead of receiving A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type. I want to be able to use types with [Symbol.toPrimitive] methods as properties in an interface.
  2. Instead of receiving A computed property name must be of type 'string', 'number', 'symbol', or 'any'. I want to be able to use a value with a [Symbol.toPrimitive] method as a property name.
  3. Instead of receiving Type 'SomeType' cannot be used as an index type. I want to be able to use a value with a [Symbol.toPrimitive] method as an index type.

πŸ“ƒ Motivating Example

class UniquePerson {
  #symbol = Symbol();
  constructor(readonly name: string) {}
  [Symbol.toPrimitive]() {
    return this.#symbol;
  }
}
const sherlock = new UniquePerson("Sherlock Holmes");
interface PersonToAddress {
  [sherlock]: string;
  // ^ A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.
}
const personToAddress: PersonToAddress = {
  [sherlock]: "221B Baker Street",
  // ^ A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
};
personToAddress[sherlock]; //=> 221B Baker Street
               // ^ Type 'UniquePerson' cannot be used as an index type.

πŸ’» Use Cases

I am working on a library that heavily uses Symbols to add extension function to other types while avoiding name conflicts (as Symbols are unique). Instead of passing around Symbols directly I want to pass around richer objects with more information but, when passed in as an index type, I want the [Symbol.toPrimitive] functions on my richer objects to be called and resolve to the appropriate Symbol. Currently I am exploring either not supporting TypeScript initially (which is not ideal and could inhibit adoption) or ditch the idea of passing around richer objects and use Symbols directly (but this greatly reduces the usability and usefulness of the library).

kohtala commented 2 years ago

I was hoping to write jsdoc for a case that I believe falls under this same problem since it relates to the use of same ToPropertyKey operation. The difference being I'd like to index an object using a class that implements toString(). This is valid JavaScript

class Str {
    toString() { return "str" }
}

class Prim {
    [Symbol.toPrimitive]() { return "prim" }
}

const o = {}
o[new Str()] = 1
o[new Prim()] = 2
console.log(o)
// { str: 1, prim: 2 }

But trying to use such a type to index an object gives the "cannot be used as an index type".

Rather than carry additional information in the class, I have other methods to convert the type for proper presentation on JSON.stringify, database, messaging interface etc. such that I'd not need to manually build datastructure with different types for each use.