Skip to content

isObject

檢查值是否為對象類型。

基礎用法

typescript
import { isObject } from 'radash'

console.log(isObject({}))                    // true
console.log(isObject([]))                    // true
console.log(isObject(new Date()))           // true
console.log(isObject(null))                 // false
console.log(isObject(undefined))            // false
console.log(isObject('hello'))              // false
console.log(isObject(123))                  // false

語法

typescript
function isObject(value: any): value is object

參數

  • value (any): 要檢查的值

返回值

返回一個布爾值,如果值是對象則返回 true,否則返回 false。同時作為TypeScript類型守衛。

示例

基本類型檢查

typescript
import { isObject } from 'radash'

// 對象類型
console.log(isObject({}))                    // true
console.log(isObject({ name: 'Alice' }))    // true
console.log(isObject([]))                    // true
console.log(isObject([1, 2, 3]))            // true
console.log(isObject(new Date()))           // true
console.log(isObject(new RegExp('test')))   // true
console.log(isObject(new Map()))            // true
console.log(isObject(new Set()))            // true

// 非對象類型
console.log(isObject(null))                 // false
console.log(isObject(undefined))            // false
console.log(isObject('hello'))              // false
console.log(isObject(123))                  // false
console.log(isObject(true))                 // false
console.log(isObject(false))                // false
console.log(isObject(() => {}))             // false

特殊對象檢查

typescript
import { isObject } from 'radash'

// 內置對象
console.log(isObject(new Array()))          // true
console.log(isObject(new Object()))         // true
console.log(isObject(new String('hello'))) // true
console.log(isObject(new Number(123)))     // true
console.log(isObject(new Boolean(true)))   // true

// 函數對象
console.log(isObject(function() {}))        // false
console.log(isObject(() => {}))             // false
console.log(isObject(async () => {}))       // false

// 原始值包裝對象
console.log(isObject(new String('hello'))) // true
console.log(isObject(new Number(123)))     // true
console.log(isObject(new Boolean(true)))   // true

類型守衛使用

typescript
import { isObject } from 'radash'

function processValue(value: unknown) {
  if (isObject(value)) {
    // TypeScript 知道 value 是對象
    console.log('Processing object:', Object.keys(value))
    return Object.keys(value).length
  }
  
  console.log('Not an object')
  return 0
}

console.log(processValue({ a: 1, b: 2 }))  // Processing object: ['a', 'b'] 2
console.log(processValue([1, 2, 3]))       // Processing object: ['0', '1', '2'] 3
console.log(processValue('hello'))          // Not an object 0

數組過濾

typescript
import { isObject } from 'radash'

const mixedArray = [
  { name: 'Alice' },
  [1, 2, 3],
  'hello',
  123,
  new Date(),
  null,
  undefined,
  () => {}
]

const objects = mixedArray.filter(isObject)
console.log(objects.length) // 3

const nonObjects = mixedArray.filter(item => !isObject(item))
console.log(nonObjects.length) // 5

對象屬性檢查

typescript
import { isObject } from 'radash'

const data = {
  user: { name: 'Alice', age: 25 },
  settings: { theme: 'dark', notifications: true },
  items: [1, 2, 3],
  count: 5,
  name: 'test',
  isActive: true
}

const objectProperties = Object.entries(data)
  .filter(([key, value]) => isObject(value))
  .map(([key, value]) => ({ key, type: Array.isArray(value) ? 'array' : 'object' }))

console.log(objectProperties)
// [
//   { key: 'user', type: 'object' },
//   { key: 'settings', type: 'object' },
//   { key: 'items', type: 'array' }
// ]

深度對象檢查

typescript
import { isObject } from 'radash'

function getObjectPaths(obj: any, path = ''): string[] {
  const paths: string[] = []
  
  if (!isObject(obj)) {
    return paths
  }
  
  Object.keys(obj).forEach(key => {
    const currentPath = path ? `${path}.${key}` : key
    paths.push(currentPath)
    
    if (isObject(obj[key])) {
      paths.push(...getObjectPaths(obj[key], currentPath))
    }
  })
  
  return paths
}

const nestedObject = {
  user: {
    profile: {
      name: 'Alice',
      settings: {
        theme: 'dark'
      }
    }
  },
  data: [1, 2, 3]
}

console.log(getObjectPaths(nestedObject))
// ['user', 'user.profile', 'user.profile.name', 'user.profile.settings', 'user.profile.settings.theme', 'data']

API響應處理

typescript
import { isObject } from 'radash'

interface ApiResponse {
  data: unknown
  meta: unknown
  errors: unknown
}

function processApiResponse(response: ApiResponse) {
  const processed = {
    data: isObject(response.data) ? response.data : {},
    meta: isObject(response.meta) ? response.meta : {},
    errors: isObject(response.errors) ? response.errors : {}
  }
  
  return processed
}

const response1: ApiResponse = {
  data: { id: 1, name: 'Alice' },
  meta: { total: 1, page: 1 },
  errors: null
}

const response2: ApiResponse = {
  data: 'not an object',
  meta: 123,
  errors: 'error string'
}

console.log(processApiResponse(response1))
// { data: { id: 1, name: 'Alice' }, meta: { total: 1, page: 1 }, errors: {} }

console.log(processApiResponse(response2))
// { data: {}, meta: {}, errors: {} }

配置驗證

typescript
import { isObject } from 'radash'

interface Config {
  server: unknown
  database: unknown
  api: unknown
}

function validateConfig(config: Config) {
  const errors: string[] = []
  
  if (!isObject(config.server)) {
    errors.push('server must be an object')
  }
  
  if (!isObject(config.database)) {
    errors.push('database must be an object')
  }
  
  if (!isObject(config.api)) {
    errors.push('api must be an object')
  }
  
  return errors
}

const validConfig: Config = {
  server: { port: 3000, host: 'localhost' },
  database: { url: 'postgresql://localhost:5432/mydb' },
  api: { version: 'v1', timeout: 5000 }
}

const invalidConfig: Config = {
  server: 'localhost:3000',
  database: null,
  api: undefined
}

console.log(validateConfig(validConfig))   // []
console.log(validateConfig(invalidConfig)) // ['server must be an object', 'database must be an object', 'api must be an object']

表單數據處理

typescript
import { isObject } from 'radash'

interface FormData {
  personalInfo: unknown
  address: unknown
  preferences: unknown
}

function validateFormData(data: FormData) {
  const errors: string[] = []
  
  if (!isObject(data.personalInfo)) {
    errors.push('personalInfo must be an object')
  }
  
  if (!isObject(data.address)) {
    errors.push('address must be an object')
  }
  
  if (!isObject(data.preferences)) {
    errors.push('preferences must be an object')
  }
  
  return errors
}

const validFormData: FormData = {
  personalInfo: { name: 'Alice', age: 25 },
  address: { street: '123 Main St', city: 'Beijing' },
  preferences: { theme: 'dark', notifications: true }
}

const invalidFormData: FormData = {
  personalInfo: 'Alice',
  address: null,
  preferences: undefined
}

console.log(validateFormData(validFormData))   // []
console.log(validateFormData(invalidFormData)) // ['personalInfo must be an object', 'address must be an object', 'preferences must be an object']

數據庫記錄處理

typescript
import { isObject } from 'radash'

interface DatabaseRecord {
  id: number
  data: unknown
  metadata: unknown
  settings: unknown
}

function sanitizeDatabaseRecord(record: DatabaseRecord) {
  return {
    id: record.id,
    data: isObject(record.data) ? record.data : {},
    metadata: isObject(record.metadata) ? record.metadata : {},
    settings: isObject(record.settings) ? record.settings : {}
  }
}

const dbRecord: DatabaseRecord = {
  id: 1,
  data: { name: 'Product A', price: 29.99 },
  metadata: { created: new Date(), updated: new Date() },
  settings: null
}

console.log(sanitizeDatabaseRecord(dbRecord))
// { id: 1, data: { name: 'Product A', price: 29.99 }, metadata: { created: Date, updated: Date }, settings: {} }

事件對象處理

typescript
import { isObject } from 'radash'

interface EventData {
  target: unknown
  data: unknown
  context: unknown
}

function processEvent(event: EventData) {
  const processed = {
    target: isObject(event.target) ? event.target : {},
    data: isObject(event.data) ? event.data : {},
    context: isObject(event.context) ? event.context : {}
  }
  
  return processed
}

const event: EventData = {
  target: { id: 'button1', type: 'button' },
  data: { x: 100, y: 200 },
  context: null
}

console.log(processEvent(event))
// { target: { id: 'button1', type: 'button' }, data: { x: 100, y: 200 }, context: {} }

嵌套對象驗證

typescript
import { isObject } from 'radash'

function validateNestedObject(obj: unknown, maxDepth = 5, currentDepth = 0): boolean {
  if (currentDepth > maxDepth) {
    return false
  }
  
  if (!isObject(obj)) {
    return true
  }
  
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      if (!validateNestedObject(obj[key], maxDepth, currentDepth + 1)) {
        return false
      }
    }
  }
  
  return true
}

const validNestedObject = {
  level1: {
    level2: {
      level3: {
        value: 'deep'
      }
    }
  }
}

const invalidNestedObject = {
  level1: {
    level2: {
      level3: {
        level4: {
          level5: {
            level6: {
              value: 'too deep'
            }
          }
        }
      }
    }
  }
}

console.log(validateNestedObject(validNestedObject))     // true
console.log(validateNestedObject(invalidNestedObject))   // false

對象合並

typescript
import { isObject } from 'radash'

function deepMerge(target: any, source: any): any {
  if (!isObject(target) || !isObject(source)) {
    return source
  }
  
  const result = { ...target }
  
  for (const key in source) {
    if (source.hasOwnProperty(key)) {
      if (isObject(source[key]) && isObject(target[key])) {
        result[key] = deepMerge(target[key], source[key])
      } else {
        result[key] = source[key]
      }
    }
  }
  
  return result
}

const obj1 = {
  user: {
    name: 'Alice',
    settings: {
      theme: 'dark'
    }
  },
  data: [1, 2, 3]
}

const obj2 = {
  user: {
    age: 25,
    settings: {
      notifications: true
    }
  },
  data: [4, 5, 6]
}

console.log(deepMerge(obj1, obj2))
// {
//   user: {
//     name: 'Alice',
//     age: 25,
//     settings: {
//       theme: 'dark',
//       notifications: true
//     }
//   },
//   data: [4, 5, 6]
// }

對象比較

typescript
import { isObject } from 'radash'

function deepEqual(a: unknown, b: unknown): boolean {
  if (a === b) {
    return true
  }
  
  if (!isObject(a) || !isObject(b)) {
    return false
  }
  
  const keysA = Object.keys(a)
  const keysB = Object.keys(b)
  
  if (keysA.length !== keysB.length) {
    return false
  }
  
  for (const key of keysA) {
    if (!keysB.includes(key)) {
      return false
    }
    
    if (!deepEqual(a[key], b[key])) {
      return false
    }
  }
  
  return true
}

const obj1 = { a: 1, b: { c: 2, d: 3 } }
const obj2 = { a: 1, b: { c: 2, d: 3 } }
const obj3 = { a: 1, b: { c: 2, d: 4 } }

console.log(deepEqual(obj1, obj2)) // true
console.log(deepEqual(obj1, obj3)) // false

對象序列化

typescript
import { isObject } from 'radash'

function safeStringify(obj: unknown): string {
  if (!isObject(obj)) {
    return JSON.stringify(obj)
  }
  
  const seen = new WeakSet()
  
  return JSON.stringify(obj, (key, value) => {
    if (isObject(value)) {
      if (seen.has(value)) {
        return '[Circular Reference]'
      }
      seen.add(value)
    }
    return value
  })
}

const circularObj: any = {
  name: 'Alice',
  data: { id: 1 }
}
circularObj.self = circularObj

console.log(safeStringify(circularObj))
// {"name":"Alice","data":{"id":1},"self":"[Circular Reference]"}

對象克隆

typescript
import { isObject } from 'radash'

function deepClone(obj: unknown): unknown {
  if (!isObject(obj)) {
    return obj
  }
  
  if (Array.isArray(obj)) {
    return obj.map(item => deepClone(item))
  }
  
  const cloned: any = {}
  
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloned[key] = deepClone(obj[key])
    }
  }
  
  return cloned
}

const original = {
  name: 'Alice',
  age: 25,
  hobbies: ['reading', 'coding'],
  address: {
    street: '123 Main St',
    city: 'Beijing'
  }
}

const cloned = deepClone(original)
console.log(cloned)
// { name: 'Alice', age: 25, hobbies: ['reading', 'coding'], address: { street: '123 Main St', city: 'Beijing' } }

// 驗證是深拷貝
original.address.city = 'Shanghai'
console.log(cloned.address.city) // 'Beijing'

注意事項

  1. 對象定義: 檢查所有對象類型,包括數組、Date、RegExp等
  2. null檢查: null不被認為是對象
  3. 函數檢查: 函數不被認為是對象
  4. 類型守衛: 作為TypeScript類型守衛使用
  5. 性能: 檢查速度很快,適合高頻使用

與其他方法的區別

  • isObject(): 檢查是否為對象
  • typeof value === 'object': 原生JavaScript檢查(包括null)
  • value !== null && typeof value === 'object': 排除null的對象檢查
  • Array.isArray(): 檢查是否為數組

實際應用場景

  1. 數據驗證: 驗證API響應中的對象字段
  2. 配置處理: 驗證配置對象
  3. 表單處理: 驗證表單數據對象
  4. 數據庫操作: 驗證數據庫記錄對象
  5. 事件處理: 驗證事件對象
  6. 對象操作: 深度對象操作和比較

Released under the MIT License.