Skip to content

isSymbol

Checks if a value is a Symbol type.

Basic Usage

typescript
import { isSymbol } from 'radash'

console.log(isSymbol(Symbol('test')))           // true
console.log(isSymbol(Symbol()))                 // true
console.log(isSymbol(Symbol.iterator))          // true
console.log(isSymbol('symbol'))                 // false
console.log(isSymbol(123))                      // false
console.log(isSymbol({}))                       // false

Syntax

typescript
function isSymbol(value: any): value is symbol

Parameters

  • value (any): The value to check

Return Value

Returns a boolean value, true if the value is a Symbol, otherwise false. Also acts as a TypeScript type guard.

Examples

Basic Type Checking

typescript
import { isSymbol } from 'radash'

// Symbol types
console.log(isSymbol(Symbol('test')))           // true
console.log(isSymbol(Symbol()))                 // true
console.log(isSymbol(Symbol.iterator))          // true
console.log(isSymbol(Symbol.asyncIterator))     // true
console.log(isSymbol(Symbol.toStringTag))       // true
console.log(isSymbol(Symbol.toPrimitive))       // true

// Non-Symbol types
console.log(isSymbol('symbol'))                 // false
console.log(isSymbol(123))                      // false
console.log(isSymbol(true))                     // false
console.log(isSymbol(null))                     // false
console.log(isSymbol(undefined))                // false
console.log(isSymbol({}))                       // false
console.log(isSymbol([]))                       // false
console.log(isSymbol(() => {}))                 // false

Built-in Symbol Checking

typescript
import { isSymbol } from 'radash'

// Built-in Symbols
console.log(isSymbol(Symbol.iterator))          // true
console.log(isSymbol(Symbol.asyncIterator))     // true
console.log(isSymbol(Symbol.toStringTag))       // true
console.log(isSymbol(Symbol.toPrimitive))       // true
console.log(isSymbol(Symbol.unscopables))       // true
console.log(isSymbol(Symbol.species))           // true
console.log(isSymbol(Symbol.split))             // true
console.log(isSymbol(Symbol.match))             // true
console.log(isSymbol(Symbol.replace))           // true
console.log(isSymbol(Symbol.search))            // true
console.log(isSymbol(Symbol.hasInstance))       // true
console.log(isSymbol(Symbol.isConcatSpreadable)) // true

Type Guard Usage

typescript
import { isSymbol } from 'radash'

function processValue(value: unknown) {
  if (isSymbol(value)) {
    // TypeScript knows value is a Symbol
    console.log('Processing Symbol:', value.toString())
    return value.toString()
  }
  
  console.log('Processing non-Symbol:', typeof value)
  return String(value)
}

const symbol = Symbol('test')
const string = 'hello'

console.log(processValue(symbol))  // Processing Symbol: Symbol(test) Symbol(test)
console.log(processValue(string))  // Processing non-Symbol: string hello

Array Filtering

typescript
import { isSymbol } from 'radash'

const mixedArray = [
  Symbol('test1'),
  'hello',
  123,
  Symbol('test2'),
  Symbol.iterator,
  {},
  [],
  () => {},
  Symbol('test3')
]

const symbols = mixedArray.filter(isSymbol)
console.log(symbols.length) // 4

const nonSymbols = mixedArray.filter(item => !isSymbol(item))
console.log(nonSymbols.length) // 5

Object Property Checking

typescript
import { isSymbol } from 'radash'

const data = {
  [Symbol('id')]: 1,
  name: 'Alice',
  [Symbol('secret')]: 'hidden',
  age: 25,
  [Symbol.iterator]: function* () { yield 1; yield 2; }
}

const symbolProperties = Object.getOwnPropertySymbols(data)
  .filter(symbol => isSymbol(symbol))
  .map(symbol => ({ key: symbol.toString(), value: data[symbol] }))

console.log(symbolProperties)
// [
//   { key: 'Symbol(id)', value: 1 },
//   { key: 'Symbol(secret)', value: 'hidden' },
//   { key: 'Symbol(Symbol.iterator)', value: [GeneratorFunction] }
// ]

Symbol Registry

typescript
import { isSymbol } from 'radash'

class SymbolRegistry {
  private symbols = new Map<string, symbol>()
  
  getSymbol(key: string): symbol {
    if (!this.symbols.has(key)) {
      this.symbols.set(key, Symbol(key))
    }
    return this.symbols.get(key)!
  }
  
  getAllSymbols(): symbol[] {
    return Array.from(this.symbols.values()).filter(isSymbol)
  }
  
  hasSymbol(key: string): boolean {
    const symbol = this.symbols.get(key)
    return symbol !== undefined && isSymbol(symbol)
  }
}

const registry = new SymbolRegistry()

const idSymbol = registry.getSymbol('id')
const nameSymbol = registry.getSymbol('name')
const ageSymbol = registry.getSymbol('age')

console.log(registry.hasSymbol('id'))      // true
console.log(registry.getAllSymbols())      // [Symbol(id), Symbol(name), Symbol(age)]

Metadata Management

typescript
import { isSymbol } from 'radash'

class MetadataManager {
  private metadata = new WeakMap<object, Map<symbol, any>>()
  
  setMetadata<T>(target: object, key: symbol, value: T): void {
    if (!isSymbol(key)) {
      throw new Error('Key must be a Symbol')
    }
    
    if (!this.metadata.has(target)) {
      this.metadata.set(target, new Map())
    }
    
    this.metadata.get(target)!.set(key, value)
  }
  
  getMetadata<T>(target: object, key: symbol): T | undefined {
    if (!isSymbol(key)) {
      return undefined
    }
    
    const targetMetadata = this.metadata.get(target)
    return targetMetadata?.get(key)
  }
  
  getSymbolKeys(target: object): symbol[] {
    const targetMetadata = this.metadata.get(target)
    if (!targetMetadata) return []
    
    return Array.from(targetMetadata.keys()).filter(isSymbol)
  }
}

const metadata = new MetadataManager()
const user = { name: 'Alice', age: 25 }

const idSymbol = Symbol('id')
const secretSymbol = Symbol('secret')

metadata.setMetadata(user, idSymbol, 12345)
metadata.setMetadata(user, secretSymbol, 'top secret')

console.log(metadata.getMetadata(user, idSymbol))      // 12345
console.log(metadata.getMetadata(user, secretSymbol))  // top secret
console.log(metadata.getSymbolKeys(user))              // [Symbol(id), Symbol(secret)]

Private Property Simulation

typescript
import { isSymbol } from 'radash'

class PrivateClass {
  private [Symbol('privateField')] = 'private value'
  private [Symbol('privateMethod')] = () => 'private method'
  
  publicField = 'public value'
  
  getPrivateField(): string {
    const symbols = Object.getOwnPropertySymbols(this).filter(isSymbol)
    const privateFieldSymbol = symbols.find(s => s.toString().includes('privateField'))
    return privateFieldSymbol ? this[privateFieldSymbol] : 'not found'
  }
  
  callPrivateMethod(): string {
    const symbols = Object.getOwnPropertySymbols(this).filter(isSymbol)
    const privateMethodSymbol = symbols.find(s => s.toString().includes('privateMethod'))
    return privateMethodSymbol ? this[privateMethodSymbol]() : 'not found'
  }
}

const instance = new PrivateClass()

console.log(instance.getPrivateField())    // private value
console.log(instance.callPrivateMethod())  // private method
console.log(instance.publicField)          // public value

Iterator Implementation

typescript
import { isSymbol } from 'radash'

class CustomCollection {
  private items: any[] = []
  
  [Symbol.iterator]() {
    let index = 0
    return {
      next: () => {
        if (index < this.items.length) {
          return { value: this.items[index++], done: false }
        }
        return { value: undefined, done: true }
      }
    }
  }
  
  add(item: any) {
    this.items.push(item)
  }
  
  getIteratorSymbol(): symbol | null {
    const symbols = Object.getOwnPropertySymbols(this).filter(isSymbol)
    return symbols.find(s => s === Symbol.iterator) || null
  }
}

const collection = new CustomCollection()
collection.add('a')
collection.add('b')
collection.add('c')

console.log(collection.getIteratorSymbol()) // Symbol(Symbol.iterator)

for (const item of collection) {
  console.log(item) // a, b, c
}

Type Checking Utility

typescript
import { isSymbol } from 'radash'

class TypeChecker {
  static getSymbolProperties(obj: object): symbol[] {
    return Object.getOwnPropertySymbols(obj).filter(isSymbol)
  }
  
  static hasSymbolProperty(obj: object, symbol: symbol): boolean {
    return isSymbol(symbol) && Object.getOwnPropertySymbols(obj).includes(symbol)
  }
  
  static getSymbolValue<T>(obj: object, symbol: symbol): T | undefined {
    if (!isSymbol(symbol) || !this.hasSymbolProperty(obj, symbol)) {
      return undefined
    }
    return obj[symbol]
  }
}

const testObj = {
  [Symbol('id')]: 123,
  [Symbol('name')]: 'test',
  regularProp: 'value'
}

console.log(TypeChecker.getSymbolProperties(testObj))           // [Symbol(id), Symbol(name)]
console.log(TypeChecker.hasSymbolProperty(testObj, Symbol('id'))) // true
console.log(TypeChecker.getSymbolValue(testObj, Symbol('id')))   // 123

Configuration Management

typescript
import { isSymbol } from 'radash'

class ConfigManager {
  private config = new Map<symbol, any>()
  
  setConfig(key: symbol, value: any): void {
    if (!isSymbol(key)) {
      throw new Error('Config key must be a Symbol')
    }
    this.config.set(key, value)
  }
  
  getConfig<T>(key: symbol): T | undefined {
    if (!isSymbol(key)) {
      return undefined
    }
    return this.config.get(key)
  }
  
  getAllSymbolKeys(): symbol[] {
    return Array.from(this.config.keys()).filter(isSymbol)
  }
  
  hasConfig(key: symbol): boolean {
    return isSymbol(key) && this.config.has(key)
  }
}

const config = new ConfigManager()

const apiKeySymbol = Symbol('apiKey')
const timeoutSymbol = Symbol('timeout')
const debugSymbol = Symbol('debug')

config.setConfig(apiKeySymbol, 'secret-key-123')
config.setConfig(timeoutSymbol, 5000)
config.setConfig(debugSymbol, true)

console.log(config.getConfig(apiKeySymbol))      // secret-key-123
console.log(config.getConfig(timeoutSymbol))     // 5000
console.log(config.hasConfig(debugSymbol))       // true
console.log(config.getAllSymbolKeys())           // [Symbol(apiKey), Symbol(timeout), Symbol(debug)]

Event System

typescript
import { isSymbol } from 'radash'

class EventEmitter {
  private events = new Map<symbol, Function[]>()
  
  on(event: symbol, handler: Function): void {
    if (!isSymbol(event)) {
      throw new Error('Event must be a Symbol')
    }
    
    if (!this.events.has(event)) {
      this.events.set(event, [])
    }
    
    this.events.get(event)!.push(handler)
  }
  
  emit(event: symbol, ...args: any[]): void {
    if (!isSymbol(event) || !this.events.has(event)) {
      return
    }
    
    this.events.get(event)!.forEach(handler => handler(...args))
  }
  
  getSymbolEvents(): symbol[] {
    return Array.from(this.events.keys()).filter(isSymbol)
  }
}

const emitter = new EventEmitter()

const userCreatedSymbol = Symbol('userCreated')
const userDeletedSymbol = Symbol('userDeleted')

emitter.on(userCreatedSymbol, (user: any) => {
  console.log('User created:', user)
})

emitter.on(userDeletedSymbol, (userId: number) => {
  console.log('User deleted:', userId)
})

emitter.emit(userCreatedSymbol, { id: 1, name: 'Alice' })
emitter.emit(userDeletedSymbol, 1)

console.log(emitter.getSymbolEvents()) // [Symbol(userCreated), Symbol(userDeleted)]

Cache System

typescript
import { isSymbol } from 'radash'

class SymbolCache {
  private cache = new Map<symbol, any>()
  
  set(key: symbol, value: any): void {
    if (!isSymbol(key)) {
      throw new Error('Cache key must be a Symbol')
    }
    this.cache.set(key, value)
  }
  
  get<T>(key: symbol): T | undefined {
    if (!isSymbol(key)) {
      return undefined
    }
    return this.cache.get(key)
  }
  
  has(key: symbol): boolean {
    return isSymbol(key) && this.cache.has(key)
  }
  
  delete(key: symbol): boolean {
    if (!isSymbol(key)) {
      return false
    }
    return this.cache.delete(key)
  }
  
  clear(): void {
    this.cache.clear()
  }
  
  getSymbolKeys(): symbol[] {
    return Array.from(this.cache.keys()).filter(isSymbol)
  }
}

const cache = new SymbolCache()

const dataSymbol = Symbol('data')
const configSymbol = Symbol('config')

cache.set(dataSymbol, { items: [1, 2, 3] })
cache.set(configSymbol, { theme: 'dark' })

console.log(cache.get(dataSymbol))      // { items: [1, 2, 3] }
console.log(cache.has(configSymbol))    // true
console.log(cache.getSymbolKeys())      // [Symbol(data), Symbol(config)]

Performance Testing

typescript
import { isSymbol } from 'radash'

function benchmarkIsSymbol() {
  const testValues = [
    Symbol('test1'),
    Symbol('test2'),
    Symbol.iterator,
    Symbol.toStringTag,
    'symbol',
    123,
    true,
    null,
    undefined,
    {},
    [],
    () => {}
  ]
  
  const iterations = 1000000
  const start = performance.now()
  
  for (let i = 0; i < iterations; i++) {
    testValues.forEach(value => {
      isSymbol(value)
    })
  }
  
  const end = performance.now()
  console.log(`Benchmark completed in ${end - start}ms`)
}

// benchmarkIsSymbol() // Run performance test

Boundary Value Testing

typescript
import { isSymbol } from 'radash'

// Test boundary values
const boundaryTests = [
  // Symbol types
  Symbol(),
  Symbol(''),
  Symbol('test'),
  Symbol.iterator,
  Symbol.asyncIterator,
  Symbol.toStringTag,
  Symbol.toPrimitive,
  Symbol.unscopables,
  Symbol.species,
  Symbol.split,
  Symbol.match,
  Symbol.replace,
  Symbol.search,
  Symbol.hasInstance,
  Symbol.isConcatSpreadable,
  
  // Non-Symbol types
  'Symbol()',
  'symbol',
  'Symbol.iterator',
  123,
  true,
  false,
  null,
  undefined,
  {},
  [],
  () => {},
  new String('Symbol'),
  new Object()
]

boundaryTests.forEach(value => {
  console.log(`${typeof value} ${String(value).slice(0, 20)}: ${isSymbol(value)}`)
})

Notes

  1. Symbol Detection: Accurately detects true Symbol types
  2. Type Guard: Acts as a TypeScript type guard
  3. Performance: Fast checking speed, suitable for high-frequency use
  4. Boundary Values: Correctly handles all boundary cases
  5. Built-in Symbols: Correctly handles all built-in Symbols

Differences from Other Methods

  • isSymbol(): Checks if value is a Symbol type
  • typeof value === 'symbol': Native JavaScript check
  • value instanceof Symbol: Not applicable to Symbol (Symbol is a primitive type)
  • Object.prototype.toString.call(value) === '[object Symbol]': Complex but accurate

Real-world Application Scenarios

  1. Metadata Management: Using Symbol as metadata keys
  2. Private Properties: Simulating private class members
  3. Iterators: Implementing custom iterators
  4. Configuration Management: Using Symbol as configuration keys
  5. Event Systems: Using Symbol as event identifiers
  6. Cache Systems: Using Symbol as cache keys

Released under the MIT License.