Type

Runtime type guards — строки, числа, обычные объекты, массивы, DOM-узлы, Blob, FormData, формы JSON-RPC сообщений.

Обзор

Type — это синглтон (new TypeManager()), экспортируемый из @bitrix24/b24jssdk. Каждый метод (кроме getTag и clone) — это type guard TypeScript, поэтому вы можете использовать его в цепочках if, и компилятор сузит тип значения:

import { Type } from '@bitrix24/b24jssdk'

function process(value: unknown) {
  if (Type.isStringFilled(value)) {
    // value: string
    return value.trim()
  }
  if (Type.isPlainObject(value)) {
    // value: Record<string | number, any>
  }
}

Проверки сгруппированы по виду значения, которое они проверяют:

  • Строки — проверки на непустую строку.
  • Числа / булевы — число, целое, дробное, булево.
  • Объекты / функции — обычные объекты, функции, Map / Set / WeakMap / WeakSet, RegExp, прототипы, null / undefined.
  • Массивы — массивы, array-like значения, типизированные массивы, ArrayBuffer.
  • DOM — узлы, узлы-элементы, текстовые узлы.
  • ФайлыBlob, File, FormData.
  • JSON-RPC — формы сообщений request / response.
  • ПрочееgetTag (сырой тег [[Class]]) и clone (глубокое клонирование).

Сигнатура методов

Строки

МетодПредикат
isString(value)typeof value === 'string' или value instanceof String.
isStringFilled(value)isString(value) && value !== ''.

Числа / булевы

МетодПредикат
isNumber(value)typeof value === 'number' и не NaN.
isInteger(value)Number.isInteger(value).
isFloat(value)isNumber && !isInteger.
isBoolean(value)value === true || value === false.

Объекты / функции

МетодПредикат
isFunction(value)typeof value === 'function' (а также экземпляры Function).
isObject(value)typeof === 'object' || 'function' (и не null).
isObjectLike<T>(value)только typeof === 'object' (также истинно для объектов, не равных null, включая массивы).
isPlainObject(value)Истина для объектов, чей constructorObject (ложь для массивов, экземпляров классов, DOM-узлов).
isPrototype(value)Истина для объектов prototype.
isMap, isSet, isWeakMap, isWeakSet, isRegExpПроверки по тегу (Object.prototype.toString.call(value)).
isDate(value)value instanceof Date.
isNil(value)value === null || value === undefined.
isNull / isUndefinedОчевидно из названия.

Массивы

МетодПредикат
isArray(value)Array.isArray.
isArrayFilled(value)isArray && value.length > 0.
isArrayLike(value)Имеет числовой length и не является функцией — также соответствует строкам.
isArrayBuffer(value)Проверка по тегу.
isTypedArray(value)Соответствует Int8Array / Uint8Array / Uint8ClampedArray / Int16Array / Uint16Array / Int32Array / Uint32Array / Float32Array / Float64Array (проверка по тегу).

DOM

МетодПредикат
isDomNode(value)Имеет nodeType и не является обычным объектом.
isElementNode(value)nodeType === Node.ELEMENT_NODE.
isTextNode(value)nodeType === Node.TEXT_NODE.

Файлы / Blob / FormData

МетодПредикат
isBlob(value)Имеет size: number, type: string, slice: Function.
isFile(value)isBlob && name: string && (lastModified || lastModifiedDate).
isFormData(value)instanceof FormData (когда глобал доступен) или проверка по тегу.

JSON-RPC

МетодПредикат
isJsonRpcRequest(value)Имеет jsonrpc (непустой) и method (непустой). Тип возврата — boolean, а не type guard.
isJsonRpcResponse(value)Имеет jsonrpc, id и либо result, либо error. Тип возврата — boolean, а не type guard.

Прочее

getTag(value: any): string                          // Object.prototype.toString.call(value)
clone(obj: any, bCopyObj?: boolean): any             // глубокое клонирование; bCopyObj по умолчанию true

Ключевые понятия

  • isJsonRpcRequest / isJsonRpcResponse — это обычные предикаты boolean, а не type guards. Каждый другой метод этого класса сужает через value is X; эти два намеренно (или нет) возвращают boolean, поэтому использование их в if не сужает тип value.
  • isObjectLike<T> сужает к переданному вызывающим кодом дженерику, а не к object. Он лишь проверяет typeof value === 'object' && value !== null; переданный вами T не проверяется в runtime — массивы, Map, Date и DOM-узлы все проходят. isPlainObject — более строгая проверка, когда вам конкретно нужен объект в стиле {}.
  • isArrayLike также соответствует строкам. У строк есть числовой length, и они не функции, поэтому Type.isArrayLike('abc')true. Используйте isArray, когда нужен настоящий массив.
  • isDomNode требует реального DOM-глобала. isElementNode / isTextNode разыменовывают глобальную константу Node, поэтому работают только в браузерном (или JSDOM) окружении — их вызов в чистом Node.js без DOM-шима выбрасывает ReferenceError.
  • clone ничего не мутирует в источнике, но клонирует только распознаваемые им формы обычных данных. Массивы и обычные объекты копируются ключ за ключом (рекурсивно, если bCopyObj не false), Date пересобирается через new Date(obj), а DOM-узлы проходят через Node.cloneNode(bCopyObj). Всё остальное с typeof obj === 'object' попадает в общую ветку и пересобирается через new obj.constructor(), поэтому кастомные классы с обязательными аргументами конструктора могут выбросить исключение.

Обработка ошибок

Методы-проверки никогда не выбрасывают исключений для проверяемого значения — не совпавшее, null или undefined значение просто даёт false. Исключения зависят от окружения: isElementNode / isTextNode разыменовывают глобальную константу Node и выбрасывают ReferenceError вне DOM-окружения, а clone может выбросить, когда откатывается к new obj.constructor() для класса, требующего аргументов конструктора. Оборачивайте такие вызовы при работе на сервере или при клонировании произвольных экземпляров классов.

Примеры

Сужение через проверки строк и объектов:

import { Type } from '@bitrix24/b24jssdk'

function describe(value: unknown): string {
  if (Type.isStringFilled(value)) {
    return `string: ${value.trim()}`
  }
  if (Type.isPlainObject(value)) {
    return `object with ${Object.keys(value).length} keys`
  }
  if (Type.isArrayFilled(value)) {
    return `array with ${value.length} items`
  }
  return 'unknown'
}

describe('  hi  ') // 'string: hi'
describe({ a: 1 }) // 'object with 1 keys'
describe([1, 2, 3]) // 'array with 3 items'

Проверки по тегу и getTag:

import { Type } from '@bitrix24/b24jssdk'

Type.getTag([1, 2, 3])   // '[object Array]'
Type.getTag(new Map())   // '[object Map]'
Type.getTag(/abc/)       // '[object RegExp]'

Type.isMap(new Map())    // true
Type.isSet(new Set())    // true
Type.isRegExp(/abc/)     // true

Формы JSON-RPC сообщений:

import { Type } from '@bitrix24/b24jssdk'

Type.isJsonRpcRequest({ jsonrpc: '2.0', method: 'ping', id: 1 }) // true
Type.isJsonRpcRequest({ jsonrpc: '2.0' })                         // false, нет `method`

Type.isJsonRpcResponse({ jsonrpc: '2.0', id: 1, result: { ok: true } }) // true
Type.isJsonRpcResponse({ jsonrpc: '2.0', id: 1 })                       // false, нет `result`/`error`

Глубокое клонирование через clone:

import { Type } from '@bitrix24/b24jssdk'

const original = { a: 1, nested: { b: 2 } }

const deep = Type.clone(original)
deep.nested.b = 99
console.log(original.nested.b) // 2 — не тронуто, глубокое клонирование

const shallow = Type.clone(original, false)
shallow.nested.b = 42
console.log(original.nested.b) // 42 — та же вложенная ссылка, поверхностное клонирование

Альтернативы и рекомендации

  • Предпочитайте методы-type-guards (value is X) ручным проверкам typeof / instanceof. Они читаются так же, но ещё и сужают TypeScript-тип значения внутри ветки if, чего две возвращающие boolean проверки JSON-RPC не делают.
  • Используйте isPlainObject вместо isObject, когда нужен объект-запись в стиле {}. isObject также принимает функции, а isObjectLike принимает массивы, Map, Date и DOM-узлы — isPlainObject единственный из трёх исключает всё это.
  • isTypedArray покрывает девять конкретных представлений типизированных массивов, но не DataView. Он соответствует Int8ArrayFloat64Array, но возвращает false для DataView; используйте ArrayBuffer.isView(value), когда хотите включить и DataView.
  • Приведение и форматирование строк/чисел живут в Text. Type только проверяет формы; используйте Text.toNumber / Text.toInteger / Text.toBoolean, когда нужно преобразовать значение, а не просто проверить его.
  • clone — это универсальное глубокое клонирование, а не замена structured-clone. Для значений, которыми вы управляете (обычные данные из REST API), его достаточно; для payload'ов Map / Set / ArrayBuffer или значений, требующих точной верности прототипа, предпочитайте structuredClone (где доступен) или специализированную библиотеку клонирования.