10000 Support symbol-based indexing and well-known symbols · Issue #980 · microsoft/TypeScript · GitHub
[go: up one dir, main page]

Skip to content
Support symbol-based indexing and well-known symbols #980
Closed
@sophiajt

Description

@sophiajt

Background

ES6 adds supports for symbols, a kind of key that can be used to access object properties with a unique value. Symbols do not collide with each other or other names, making them useful as a special property name.

In this proposal, we recommend adding support for symbols to TypeScript by typing the well-known symbols. This proposal also treats all user-defined types as a Symbol type, rather than tracking each new symbol as its own type (as this drastically increases the complexity of the type system).

Proposal

ES6 supports a handful of well-known symbols, which are used to unlock additional functionality.

(Taken from the ES6 draft specification)

Name Purpose
Symbol.hasInstance A method that determines if a constructor object recognizes an object as one of the constructor’s instances. Called by the semantics of the instanceof operator.
Symbol.isConcatSpreadable A Boolean value that if true indicates that an object should be flatten to its array elements by Array.prototype.concat.
Symbol.isRegExp A Boolean value that if true indicates that an object may be used as a regular expression.
Symbol.iterator A method that returns the default iterator for an object. Called by the semantics of the for-of statement.
Symbol.toPrimitive A method that converts an object to a corresponding primitive value. Called by the ToPrimitive abstract operation.
Symbol.toStringTag A String value that is used in the creation of the default string description of an object. Called by the built-in method Object.prototype.toString.
Symbol.unscopables An Object whose own property names are property names that are excluded from the with environment bindings of the associated objects.

A Symbol interface should be added to lib.d.ts for each of the above, so that they can be used as unique keys. Additionally, we need to add a new indexer that works with Symbols to compliment the numeric and string indexers that objects already support.

The Symbol interface also contains a call signature, allowing users to create Symbols. As mentioned above, these would not be unique types, but instead would be a common symbol type.

@JsonFreeman writes:
There are three kinds of ES Symbols:

  1. Built in Symbols (Symbol.iterator)
  2. Registered Symbols (Symbol.for("foo"))
  3. Unregistered Symbols (Symbol("foo"))

The following proposal is to support built-in symbols only. Below it I have a suggestion to also support registered Symbols.
-- A keyword called "symbol" for the Symbol type
-- Symbol indexer works just like string and number indexers, but has no relation to either of them (Right now a property of type "any" is assumed to correspond to the number indexer, not sure what to do about "any" here).
-- Apparent properties of symbol are the properties of Symbol
-- symbol is assignable and subtype to Symbol
-- typeof support in type guards (typeof x === "symbol")
-- Coercive operators will give type check errors if their operands are symbols
-- Known symbol properties are of the form Symbol., where is any identifier name.
-- Allow known-Symbol computed properties in interfaces, object type literals, and class property declarations (including ambients). These are places where dynamic computed properties are not allowed.
-- An index expression with a known symbol (the expression Symbol. of type symbol) will get the corresponding property from the target type.
-- If a Symbol-keyed class property is initialized, it will be moved to the constructor. We are assuming that it is okay to access Symbol.iterator in the constructor instead of in the class function closure.
-- Symbol-keyed properties will be type checked like this:

  1. The key has to be of type symbol
  2. The Symbol identifier has to resolve to the global variable Symbol (although I think we should make it a const).
  3. The name must be a property of the global Symbol object's type (note that Symbol.prototype is of type Symbol, not symbol).
    -- The property is treated just like any other property with respect to assignability, inference, etc.
    -- Symbol properties do get emitted into .d.ts.

As a possible extension, we could support registered Symbols. The identities of these Symbols are statically knowable if they are referenced as Symbol.for("name literal"). This creates a Symbol with the specified key, and all further calls with the same key will get the same Symbol.

This would work exactly like the built in Symbols. We would allow it in interfaces and ambient contexts too. The one question is whether the naive emit would be acceptable for class properties:

class C {
    [Symbol.for("foo")]: string; // Call does not get emitted
    [Symbol.for("bar")] = ""; // Call gets emitted in the constructor
}

Suggestions are also welcome for unregistered Symbols, although I think they are much lower priority.

Note that the main limitation of these proposals is that they do not handle aliased Symbols. They also do not traffic types for Symbols, so you cannot cast an unknown Symbol to a known Symbol.

Metadata

Metadata

Assignees

No one assigned

    Labels

    FixedA PR has been merged for this issueSpecIssues related to the TypeScript language specification

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0