Skip to content

isSymbol

检查值是否为Symbol类型。

基础用法

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

语法

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

参数

  • value (any): 要检查的值

返回值

返回一个布尔值,如果值是Symbol则返回 true,否则返回 false。同时作为TypeScript类型守卫。

示例

基本类型检查

typescript
import { isSymbol } from 'radash'

// Symbol类型
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

// 非Symbol类型
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

内置Symbol检查

typescript
import { isSymbol } from 'radash'

// 内置Symbol
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

类型守卫使用

typescript
import { isSymbol } from 'radash'

function processValue(value: unknown) {
  if (isSymbol(value)) {
    // TypeScript 知道 value 是 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

数组过滤

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

对象属性检查

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注册表

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)]

元数据管理

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)]

私有属性模拟

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

迭代器实现

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
}

类型检查工具

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

配置管理

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)]

事件系统

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)]

缓存系统

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)]

性能测试

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() // 运行性能测试

边界值测试

typescript
import { isSymbol } from 'radash'

// 测试边界值
const boundaryTests = [
  // Symbol类型
  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,
  
  // 非Symbol类型
  '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)}`)
})

注意事项

  1. Symbol检测: 准确检测真正的Symbol类型
  2. 类型守卫: 作为TypeScript类型守卫使用
  3. 性能: 检查速度很快,适合高频使用
  4. 边界值: 正确处理所有边界情况
  5. 内置Symbol: 正确处理所有内置Symbol

与其他方法的区别

  • isSymbol(): 检查是否为Symbol类型
  • typeof value === 'symbol': 原生JavaScript检查
  • value instanceof Symbol: 不适用于Symbol(Symbol是原始类型)
  • Object.prototype.toString.call(value) === '[object Symbol]': 复杂但准确

实际应用场景

  1. 元数据管理: 使用Symbol作为元数据键
  2. 私有属性: 模拟私有类成员
  3. 迭代器: 实现自定义迭代器
  4. 配置管理: 使用Symbol作为配置键
  5. 事件系统: 使用Symbol作为事件标识符
  6. 缓存系统: 使用Symbol作为缓存键

Released under the MIT License.