or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

array-operations.mdcollection-types.mderror-types.mdindex.mditerator-helpers.mdmath-utilities.mdobject-methods.mdpromise-utilities.mdreflection-api.mdstring-processing.mdsymbol-management.mdtypedarray-operations.mdweb-apis.md
tile.json

symbol-management.mddocs/

Symbol Management

Symbol primitives and well-known symbols for creating unique identifiers, implementing protocols, and customizing built-in JavaScript behavior.

Capabilities

Symbol Constructor and Registry

Core Symbol functionality for creating unique identifiers and managing global symbol registry.

/**
 * Symbol constructor and static methods
 */
interface SymbolConstructor {
  /**
   * Creates a new unique Symbol
   * @param description - Optional string description for debugging
   */
  (description?: string | number): symbol;
  
  /**
   * Creates or retrieves a symbol from the global symbol registry
   * @param key - String key to identify the symbol
   */
  for(key: string): symbol;
  
  /**
   * Retrieves the key for a symbol in the global registry
   * @param symbol - Symbol to get the key for
   */
  keyFor(symbol: symbol): string | undefined;
}

Usage Examples:

import Symbol from 'core-js-pure/stable/symbol';

// Creating unique symbols
const sym1 = Symbol('description');
const sym2 = Symbol('description');
console.log(sym1 === sym2); // false - each symbol is unique

// Global symbol registry
const globalSym1 = Symbol.for('shared-key');
const globalSym2 = Symbol.for('shared-key');
console.log(globalSym1 === globalSym2); // true - same symbol from registry

console.log(Symbol.keyFor(globalSym1)); // 'shared-key'
console.log(Symbol.keyFor(sym1)); // undefined - not in global registry

// Using symbols as object keys
const obj = {};
const privateKey = Symbol('private');
obj[privateKey] = 'secret value';
obj.public = 'public value';

console.log(Object.keys(obj)); // ['public'] - symbol keys not enumerated
console.log(obj[privateKey]); // 'secret value'

Well-Known Symbols

Standard symbols that customize built-in JavaScript behavior and implement various protocols.

interface SymbolConstructor {
  /**
   * Symbol for async disposal in using declarations
   */
  readonly asyncDispose: unique symbol;
  
  /**
   * Symbol for async iteration protocol
   */
  readonly asyncIterator: unique symbol;
  
  /**
   * Symbol for disposal in using declarations
   */
  readonly dispose: unique symbol;
  
  /**
   * Symbol to customize instanceof behavior
   */
  readonly hasInstance: unique symbol;
  
  /**
   * Symbol to control Array.prototype.concat spreading
   */
  readonly isConcatSpreadable: unique symbol;
  
  /**
   * Symbol for iteration protocol
   */
  readonly iterator: unique symbol;
  
  /**
   * Symbol to customize String.prototype.match behavior
   */
  readonly match: unique symbol;
  
  /**
   * Symbol to customize String.prototype.matchAll behavior
   */
  readonly matchAll: unique symbol;
  
  /**
   * Symbol to customize String.prototype.replace behavior
   */
  readonly replace: unique symbol;
  
  /**
   * Symbol to customize String.prototype.search behavior
   */
  readonly search: unique symbol;
  
  /**
   * Symbol to specify constructor function for derived objects
   */
  readonly species: unique symbol;
  
  /**
   * Symbol to customize String.prototype.split behavior
   */
  readonly split: unique symbol;
  
  /**
   * Symbol to customize primitive conversion behavior
   */
  readonly toPrimitive: unique symbol;
  
  /**
   * Symbol to customize Object.prototype.toString behavior
   */
  readonly toStringTag: unique symbol;
  
  /**
   * Symbol to specify properties excluded from with statement
   */
  readonly unscopables: unique symbol;
}

Usage Examples:

import Symbol from 'core-js-pure/stable/symbol';

// Custom iterator implementation
class CountDown {
  constructor(start) {
    this.start = start;
  }
  
  [Symbol.iterator]() {
    let current = this.start;
    return {
      next() {
        if (current > 0) {
          return { value: current--, done: false };
        } else {
          return { done: true };
        }
      }
    };
  }
}

const countdown = new CountDown(3);
console.log([...countdown]); // [3, 2, 1]

// Custom string tag for better debugging
class MyClass {
  [Symbol.toStringTag] = 'MyClass';
}

const instance = new MyClass();
console.log(Object.prototype.toString.call(instance)); // '[object MyClass]'

// Custom primitive conversion
class Temperature {
  constructor(celsius) {
    this.celsius = celsius;
  }
  
  [Symbol.toPrimitive](hint) {
    switch (hint) {
      case 'number':
        return this.celsius;
      case 'string':
        return `${this.celsius}°C`;
      default:
        return this.celsius;
    }
  }
}

const temp = new Temperature(25);
console.log(+temp); // 25 (number conversion)
console.log(`${temp}`); // '25°C' (string conversion)
console.log(temp + 10); // 35 (default conversion to number)

Custom Protocol Implementation

Using symbols to implement custom protocols and behaviors.

/**
 * Examples of implementing custom protocols with symbols
 */

// Custom matcher protocol
interface Matcher {
  [Symbol.match](string: string): RegExpMatchArray | null;
  [Symbol.replace](string: string, replaceValue: string): string;
  [Symbol.search](string: string): number;
  [Symbol.split](string: string, limit?: number): string[];
}

// Custom species constructor
interface CustomArray<T> extends Array<T> {
  static [Symbol.species]: ArrayConstructor;
}

// Custom iteration protocol
interface CustomIterable<T> {
  [Symbol.iterator](): Iterator<T>;
}

// Custom async iteration protocol
interface CustomAsyncIterable<T> {
  [Symbol.asyncIterator](): AsyncIterator<T>;
}

Usage Examples:

import Symbol from 'core-js-pure/stable/symbol';

// Custom matcher that works with string methods
class WordMatcher {
  constructor(word) {
    this.word = word;
  }
  
  [Symbol.match](str) {
    const index = str.indexOf(this.word);
    return index !== -1 ? [this.word] : null;
  }
  
  [Symbol.replace](str, replacement) {
    return str.replace(this.word, replacement);
  }
  
  [Symbol.search](str) {
    return str.indexOf(this.word);
  }
  
  [Symbol.split](str) {
    return str.split(this.word);
  }
}

const matcher = new WordMatcher('hello');
const text = 'Say hello to the world';

console.log(text.match(matcher)); // ['hello']
console.log(text.replace(matcher, 'hi')); // 'Say hi to the world'
console.log(text.search(matcher)); // 4
console.log(text.split(matcher)); // ['Say ', ' to the world']

// Custom async iterable
class AsyncRange {
  constructor(start, end, delay = 100) {
    this.start = start;
    this.end = end;
    this.delay = delay;
  }
  
  [Symbol.asyncIterator]() {
    let current = this.start;
    const end = this.end;
    const delay = this.delay;
    
    return {
      async next() {
        if (current <= end) {
          await new Promise(resolve => setTimeout(resolve, delay));
          return { value: current++, done: false };
        }
        return { done: true };
      }
    };
  }
}

// Usage with for-await-of
const asyncRange = new AsyncRange(1, 3, 500);
for await (const num of asyncRange) {
  console.log(num); // Prints 1, 2, 3 with 500ms delay between each
}

instanceof Customization

Using Symbol.hasInstance to customize instanceof behavior.

/**
 * Custom instanceof behavior using Symbol.hasInstance
 */
interface Constructor {
  [Symbol.hasInstance](instance: any): boolean;
}

Usage Examples:

import Symbol from 'core-js-pure/stable/symbol';

// Custom instanceof behavior
class Collection {
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance) || 
           instance instanceof Set || 
           instance instanceof Map;
  }
}

console.log([] instanceof Collection); // true
console.log(new Set() instanceof Collection); // true
console.log(new Map() instanceof Collection); // true
console.log({} instanceof Collection); // false

// Type checking utility
class TypeChecker {
  static [Symbol.hasInstance](instance) {
    return typeof instance === 'string' || 
           typeof instance === 'number' || 
           typeof instance === 'boolean';
  }
}

console.log('hello' instanceof TypeChecker); // true
console.log(42 instanceof TypeChecker); // true
console.log(true instanceof TypeChecker); // true
console.log({} instanceof TypeChecker); // false

Array Concat Spreadability

Controlling how objects behave when used with Array.prototype.concat.

/**
 * Control concat spreading behavior
 */
interface ConcatSpreadable {
  [Symbol.isConcatSpreadable]: boolean;
}

Usage Examples:

import Symbol from 'core-js-pure/stable/symbol';

// Array-like object that spreads in concat
const arrayLike = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
  [Symbol.isConcatSpreadable]: true
};

console.log([1, 2].concat(arrayLike)); // [1, 2, 'a', 'b', 'c']

// Array that doesn't spread
const nonSpreadingArray = ['x', 'y'];
nonSpreadingArray[Symbol.isConcatSpreadable] = false;

console.log([1, 2].concat(nonSpreadingArray)); // [1, 2, ['x', 'y']]

// Custom collection class
class CustomCollection extends Array {
  constructor(...items) {
    super(...items);
    // Control whether instances spread in concat
    this[Symbol.isConcatSpreadable] = false;
  }
}

const collection = new CustomCollection('a', 'b');
console.log([1, 2].concat(collection)); // [1, 2, CustomCollection(['a', 'b'])]

Common Patterns

Private Object Properties

Using symbols to create private-like properties that won't conflict with string keys.

import Symbol from 'core-js-pure/stable/symbol';

// Private properties using symbols
const _private = Symbol('private');
const _secret = Symbol('secret');

class SecureClass {
  constructor(publicData, privateData) {
    this.public = publicData;
    this[_private] = privateData;
    this[_secret] = 'top secret';
  }
  
  getPrivate() {
    return this[_private];
  }
  
  static getSecret(instance) {
    return instance[_secret];
  }
}

const obj = new SecureClass('public info', 'private info');
console.log(obj.public); // 'public info'
console.log(obj.getPrivate()); // 'private info'
console.log(Object.keys(obj)); // ['public'] - symbols not enumerated

Protocol Implementation Framework

Creating a framework for implementing custom protocols.

import Symbol from 'core-js-pure/stable/symbol';

// Protocol registry
const protocols = new Map();

const defineProtocol = (name, symbol) => {
  protocols.set(name, symbol);
  return symbol;
};

const implementsProtocol = (obj, protocolName) => {
  const symbol = protocols.get(protocolName);
  return symbol && typeof obj[symbol] === 'function';
};

// Define custom protocols
const Serializable = defineProtocol('Serializable', Symbol('Serializable'));
const Comparable = defineProtocol('Comparable', Symbol('Comparable'));
const Cloneable = defineProtocol('Cloneable', Symbol('Cloneable'));

// Implement protocols
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  [Serializable]() {
    return JSON.stringify({ name: this.name, age: this.age });
  }
  
  [Comparable](other) {
    return this.age - other.age;
  }
  
  [Cloneable]() {
    return new Person(this.name, this.age);
  }
}

const person = new Person('Alice', 30);
console.log(implementsProtocol(person, 'Serializable')); // true
console.log(person[Serializable]()); // '{"name":"Alice","age":30}'

Cross-Realm Communication

Using global symbols for cross-realm (iframe, worker) communication.

import Symbol from 'core-js-pure/stable/symbol';

// Global symbols work across different JavaScript realms
const SHARED_PROTOCOL = Symbol.for('app.shared.protocol');
const MESSAGE_TYPE = Symbol.for('app.message.type');

// In main window
const createMessage = (type, data) => ({
  [MESSAGE_TYPE]: type,
  data,
  timestamp: Date.now()
});

// In worker or iframe (different realm)
const handleMessage = (message) => {
  const type = message[MESSAGE_TYPE];
  switch (type) {
    case 'user-action':
      return processUserAction(message.data);
    case 'system-event':
      return processSystemEvent(message.data);
  }
};

// Protocol works across realms because symbols are global
const userMessage = createMessage('user-action', { action: 'click' });
handleMessage(userMessage); // Works even in different realm