Проверка типа данных во встроенном языке 1С:Предприятия 8.3

· 10 мин

Содержание

Тип значения переменной в 1С определяют функцией ТипЗнч(). Её результат сравнивают с Тип("ИмяТипа"): Если ТипЗнч(Значение) = Тип("Число") Тогда. Для ссылок имя типа пишут с суффиксом «Ссылка»: Тип("СправочникСсылка.Номенклатура"), Тип("ДокументСсылка.РеализацияТоваровУслуг").

Всё остальное — частные случаи этого приёма. Как проверить сразу несколько типов, как определить тип в запросе, как отличить Неопределено от Null и пустой ссылки, что делать с ячейками табличного документа и вложенными структурами — разобрано ниже. Примеры относятся к платформе 8.3 и подходят для любой типовой конфигурации: это возможности встроенного языка, а не конкретного прикладного решения.

Функция ТипЗнч: как узнать тип значения переменной

ТипЗнч(Значение) возвращает объект типа Тип. Это не строка, а отдельное значение, которое можно сравнивать, хранить в переменной и передавать в функции.

Количество = 12.5;
Если ТипЗнч(Количество) = Тип("Число") Тогда
    Сообщить("Это число");
КонецЕсли;

Контрагент = Справочники.Контрагенты.НайтиПоКоду("000000001");
Если ТипЗнч(Контрагент) = Тип("СправочникСсылка.Контрагенты") Тогда
    Сообщить("Это ссылка на элемент справочника «Контрагенты»");
КонецЕсли;

Как пишется имя типа

Что проверяемВыражение
Число, строка, дата, булевоТип("Число"), Тип("Строка"), Тип("Дата"), Тип("Булево")
Пустые значенияТип("Неопределено"), Тип("Null")
Ссылка справочникаТип("СправочникСсылка.Номенклатура")
Ссылка документаТип("ДокументСсылка.ПоступлениеТоваровУслуг")
Значение перечисленияТип("ПеречислениеСсылка.ВидыДоговоров")
Объект справочника (не ссылка)Тип("СправочникОбъект.Номенклатура")
Универсальные коллекцииТип("Массив"), Тип("Структура"), Тип("Соответствие"), Тип("ТаблицаЗначений")
ИдентификаторТип("УникальныйИдентификатор")

Тип("Справочник.Номенклатура") без слова «Ссылка» вызовет ошибку Тип не определен. Платформа ищет тип по точному имени, а такого типа нет.

Как посмотреть тип в отладчике

Если непонятно, что лежит в переменной, поставьте точку останова и откройте окно вычисления выражения (Shift+F9). Введите имя переменной — в колонке «Тип» будет показан фактический тип значения. Для реквизитов составного типа это самый быстрый способ понять, что пришло из базы.

Почему не стоит сравнивать строки

Частый соблазн — написать Строка(ТипЗнч(Значение)) = "Число". Для примитивов это иногда срабатывает, но строковое представление типа зависит от языка интерфейса и синонима объекта в конфигураторе. Переименовали синоним справочника — проверка тихо перестала работать.

Если нужно стабильное текстовое имя типа (например, для лога или обмена), используйте XMLТипЗнч:

ИмяТипа = XMLТипЗнч(Контрагент).ИмяТипа; // "CatalogRef.Контрагенты"

Оно не зависит от синонимов и языка, поэтому подходит для сохранения в файлы и регистры.

Проверка принадлежности к группе типов

Сравнение через = хорошо, когда тип один. Когда допустимых типов несколько или нужно ответить на вопрос «это вообще ссылка на справочник?», удобнее другие инструменты.

ОписаниеТипов и метод СодержитТип

ОписаниеТипов хранит набор типов. Его метод СодержитТип() отвечает, входит ли тип в набор:

ДопустимыеТипы = Новый ОписаниеТипов("Число, Строка");
Если ДопустимыеТипы.СодержитТип(ТипЗнч(Значение)) Тогда
    // значение — число или строка
КонецЕсли;

Так же проверяют, подходит ли значение для реквизита составного типа. У каждого реквизита в метаданных есть свойство Тип с готовым описанием типов:

ТипРеквизита = Метаданные.Документы.РеализацияТоваровУслуг.Реквизиты.Контрагент.Тип;
Если Не ТипРеквизита.СодержитТип(ТипЗнч(НовоеЗначение)) Тогда
    ВызватьИсключение "Значение не подходит для реквизита «Контрагент»";
КонецЕсли;

Список всех типов из описания возвращает метод Типы() — массив значений Тип.

Любая ссылка справочника или документа

Чтобы не перечислять все справочники вручную, используйте менеджеры коллекций:

Если Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
    // ссылка на элемент любого справочника
ИначеЕсли Документы.ТипВсеСсылки().СодержитТип(ТипЗнч(Значение)) Тогда
    // ссылка на любой документ
КонецЕсли;

В конфигурациях на Библиотеке стандартных подсистем есть готовая функция ОбщегоНазначения.ЭтоСсылка(Тип). Она отвечает, является ли переданный тип ссылочным, и избавляет от длинных цепочек условий.

Через метаданные: что это за объект

Когда нужно не только понять «ссылка или нет», но и узнать, на какой объект она указывает, используйте Метаданные.НайтиПоТипу:

ОбъектМетаданных = Метаданные.НайтиПоТипу(ТипЗнч(Значение));
Если ОбъектМетаданных <> Неопределено Тогда
    Сообщить(ОбъектМетаданных.ПолноеИмя()); // "Справочник.Контрагенты"
КонецЕсли;

Для ссылки есть короткий путь — Значение.Метаданные(). Но он упадёт с ошибкой, если в переменной не ссылка, поэтому сначала проверьте тип.

Объект Метаданные недоступен на тонком клиенте. Проверку через метаданные выполняйте на сервере, а на клиенте ограничьтесь ТипЗнч и ОписаниеТипов.

Проверка типа в запросе

В языке запросов свои конструкции. Имя типа в них пишется без «Ссылка»:

ВЫБРАТЬ
    Движения.Регистратор,
    ВЫРАЗИТЬ(Движения.Регистратор КАК Документ.ПоступлениеТоваровУслуг).Номер КАК НомерПоступления
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Движения
ГДЕ
    Движения.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг

Варианты условия:

  • 🔹 Поле ССЫЛКА Документ.ПоступлениеТоваровУслуг — короткая проверка ссылочного поля;
  • 🔹 ТИПЗНАЧЕНИЯ(Поле) = ТИП(Документ.ПоступлениеТоваровУслуг) — то же через сравнение типов, работает и с примитивами: ТИП(ЧИСЛО);
  • 🔹 ВЫРАЗИТЬ(Поле КАК Документ.ПоступлениеТоваровУслуг) — приведение составного поля к одному типу, чтобы обратиться к его реквизитам без лишних соединений со всеми возможными таблицами.

Фильтровать по типу лучше в запросе, а не перебором результата в цикле: в выборку не попадут лишние строки.

Неопределено, Null и пустая ссылка: три разных «ничего»

Эти значения выглядят одинаково пустыми, но ведут себя по-разному. Перепутать их — верный путь к ошибке Значение не является значением объектного типа.

ЗначениеОткуда берётсяКак проверить
НеопределеноПеременная без значения, необязательный параметр, реквизит составного типа без значения, ключ не найден в соответствииЗначение = Неопределено
NullПоле запроса из левого соединения, где пары не нашлосьЗначение = Null или ТипЗнч(Значение) = Тип("Null")
Пустая ссылкаНезаполненный реквизит ссылочного типаЗначение.Пустая() или Значение = Справочники.Контрагенты.ПустаяСсылка()

Универсальная проверка заполненности

Если неважно, какое именно «ничего» пришло, используйте ЗначениеЗаполнено(). Функция возвращает Ложь для Неопределено, Null, пустой ссылки, нуля, пустой строки и пустой даты:

Если Не ЗначениеЗаполнено(Объект.Контрагент) Тогда
    ВызватьИсключение "Не заполнен контрагент";
КонецЕсли;

Но если ноль — допустимое значение (например, количество в возвратной строке), ЗначениеЗаполнено его отбракует. Тогда проверяйте тип явно: ТипЗнч(Значение) = Тип("Число").

Безопасный порядок проверок перед обращением к реквизиту

Метод Пустая() есть только у ссылок. Вызов на Неопределено даст исключение, поэтому порядок проверок важен:

Если ТипЗнч(Значение) = Тип("СправочникСсылка.Контрагенты")
    И Не Значение.Пустая() Тогда
    ИНН = Значение.ИНН;
КонецЕсли;

Условия во встроенном языке вычисляются слева направо, и при ложной первой части вторая не выполняется. Поэтому проверка типа идёт первой.

Null — убирать в запросе

Null удобнее не ловить в коде, а заменить прямо в запросе:

ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток

Тогда в коде будет число, и арифметика не сломается на Null.

Типы в табличном документе и таблице значений

Табличные данные — частый источник путаницы с типами. Особенно при загрузке из Excel.

Ячейки табличного документа

Когда файл Excel читают в ТабличныйДокумент, содержимое ячеек обычно доступно через свойство Текст — это всегда строка. Число «1 250,50» придёт как текст с пробелом и запятой, и сравнение с числом даст Ложь.

Строку перед сравнением нужно привести к числу. Надёжный вариант с обработкой мусора:

Функция ЧислоИзТекста(Знач Текст)
    Текст = СокрЛП(Текст);
    Текст = СтрЗаменить(Текст, " ", "");
    Текст = СтрЗаменить(Текст, Символы.НПП, "");
    Текст = СтрЗаменить(Текст, ",", ".");
    Если Текст = "" Тогда
        Возврат Неопределено;
    КонецЕсли;
    Попытка
        Возврат Число(Текст);
    Исключение
        Возврат Неопределено;
    КонецПопытки;
КонецФункции

Для НомерСтроки = 2 По ТабДок.ВысотаТаблицы Цикл
    ТекстСуммы = ТабДок.Область(НомерСтроки, 3, НомерСтроки, 3).Текст;
    Сумма = ЧислоИзТекста(ТекстСуммы);
    Если Сумма = Неопределено Тогда
        Сообщить("Строка " + Формат(НомерСтроки, "ЧГ=0") + ": в колонке суммы не число");
        Продолжить;
    КонецЕсли;
    // дальше работаем с числом
КонецЦикла;

Функция возвращает Неопределено вместо исключения. Вызывающий код сам решает, пропустить строку или прервать загрузку.

У области ячеек есть и типизированный режим: свойства СодержитЗначение, ТипЗначения (описание типов) и Значение. Его используют, когда ячейка заполняется программно и должна хранить ссылку или число, а не текст.

Колонки таблицы значений

Колонка ТаблицаЗначений может иметь тип, а может не иметь. Тип задают при добавлении:

Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Номенклатура", Новый ОписаниеТипов("СправочникСсылка.Номенклатура"));
Таблица.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число"));
Таблица.Колонки.Добавить("Комментарий"); // без типа

Разница на практике:

  • ✅ в типизированную колонку значение приводится к её типу при записи: строка «abc» в колонке «Сумма» станет нулём;
  • ⚠️ в колонку без типа ложится что угодно, и в соседних строках могут оказаться число и строка;
  • ⚠️ если таблицу передают в запрос параметром и помещают во временную таблицу, колонки должны быть типизированы, иначе запрос завершится ошибкой.

Тип колонки читается через Таблица.Колонки.Сумма.ТипЗначения. Это тоже ОписаниеТипов, к нему применимы СодержитТип() и Типы().

Тот же механизм приведения доступен отдельно — метод ПривестиЗначение() у описания типов. Он возвращает значение нужного типа или значение по умолчанию этого типа, если преобразовать не удалось.

Сложные значения: структура, соответствие, массив

Значения внутри Структура, Соответствие и Массив могут быть любого типа, в том числе вложенными коллекциями. Так обычно выглядит результат разбора JSON или ответ HTTP-сервиса.

Безопасное чтение ключей

У структуры обращение к несуществующему ключу через точку вызовет ошибку. Проверяйте ключ методом Свойство():

Если Параметры.Свойство("Организация", Организация)
    И ТипЗнч(Организация) = Тип("СправочникСсылка.Организации") Тогда
    // ключ есть, и в нём ссылка нужного типа
КонецЕсли;

У соответствия Получить() возвращает Неопределено, если ключа нет. Но Неопределено может лежать и как настоящее значение. Если различие «ключа нет» и «ключ есть, но пустой» важно, не кладите в соответствие Неопределено как значение — храните пустоту явно, например пустой строкой или нулём.

JSON: структура или соответствие

ПрочитатьJSON(Чтение) по умолчанию создаёт Структура. Если вторым параметром передать Истина, объекты JSON станут Соответствие. Это важно: ключи вида "1-й" или "order id" нельзя сделать ключом структуры, и чтение без второго параметра завершится ошибкой.

Рекурсивный обход с выводом типов

Когда структура данных неизвестна, помогает функция, которая обходит её целиком и печатает тип каждого элемента:

Функция ДеревоТипов(Значение, Отступ = "") Экспорт
    Результат = "";
    ТекущийТип = ТипЗнч(Значение);
    Если ТекущийТип = Тип("Структура") Или ТекущийТип = Тип("ФиксированнаяСтруктура")
        Или ТекущийТип = Тип("Соответствие") Или ТекущийТип = Тип("ФиксированноеСоответствие") Тогда
        Для Каждого КлючИЗначение Из Значение Цикл
            Результат = Результат + Отступ + Строка(КлючИЗначение.Ключ) + ": "
                + XMLТипЗнч(КлючИЗначение.Значение).ИмяТипа + Символы.ПС;
            Результат = Результат + ДеревоТипов(КлючИЗначение.Значение, Отступ + "    ");
        КонецЦикла;
    ИначеЕсли ТекущийТип = Тип("Массив") Или ТекущийТип = Тип("ФиксированныйМассив") Тогда
        Для Индекс = 0 По Значение.ВГраница() Цикл
            Элемент = Значение[Индекс];
            Результат = Результат + Отступ + "[" + Формат(Индекс, "ЧН=0; ЧГ=0") + "]: "
                + XMLТипЗнч(Элемент).ИмяТипа + Символы.ПС;
            Результат = Результат + ДеревоТипов(Элемент, Отступ + "    ");
        КонецЦикла;
    КонецЕсли;
    Возврат Результат;
КонецФункции

Для {"id": 15, "items": [{"name": "Бумага"}]} функция вернёт примерно такое дерево: id: decimal, items: Array, [0]: Structure, name: string. Пустой массив ВГраница() вернёт -1, и цикл просто не выполнится.

Переменную под тип назовите ТекущийТип, а не Тип. Имя Тип совпадает с глобальной функцией, и такой код хуже читается.

Частые ошибки при проверке типов и как их избежать

  • ❌ Сравнение типа со строкой. ТипЗнч(Х) = "Число" всегда даёт Ложь: слева Тип, справа Строка. Ошибки нет, просто условие никогда не срабатывает.
  • ❌ Обращение к реквизиту без проверки. Значение.Наименование при Неопределено падает с Значение не является значением объектного типа (Наименование). Сначала тип, потом реквизит.
  • ❌ Путаница ссылки и объекта. После Ссылка.ПолучитьОбъект() тип уже СправочникОбъект.…, и проверка на СправочникСсылка.… не пройдёт.
  • ❌ Тип("…") внутри большого цикла. На тысячах итераций каждый вызов разбирает строку имени. Вычислите тип один раз до цикла: ТипНоменклатура = Тип("СправочникСсылка.Номенклатура").
  • ❌ Проверка через Метаданные на клиенте. В тонком клиенте объект недоступен — выносите проверку в серверную процедуру.
  • ❌ Тип по строковому представлению. Работает до первого переименования синонима или запуска на другом языке интерфейса.

Когда тип лучше задать, а не проверять

Многие проверки исчезают, если типы описаны заранее: у реквизитов формы, параметров процедур в комментарии, колонок таблиц значений. Чем уже тип на входе, тем меньше ветвлений в коде.

Для составных реквизитов полезно отдельное правило: перед записью проверяйте значение через Тип реквизита в метаданных и СодержитТип(). Ошибка сразу покажет, кто передал неподходящее значение, а не всплывёт позже в отчёте.

Проверьте себя
Что вернёт выражение ТипЗнч(Х) = "Число", если Х = 5?
Какое имя типа правильное для ссылки на справочник «Номенклатура»?
Какое значение получит поле запроса, если в левом соединении не нашлось пары?
Как в языке запросов проверить, что регистратор — документ поступления?

Вопросы о типах данных в 1С

Можно ли получить тип значения на клиенте?
Да. ТипЗнч, Тип и ОписаниеТипов работают и на клиенте, и на сервере. На клиенте недоступен только объект Метаданные.
Как определить тип реквизита, а не его текущего значения?
Через метаданные: Метаданные.Справочники.Номенклатура.Реквизиты.Артикул.Тип. Это описание типов; список типов вернёт метод Типы().
Чем ТипЗнч отличается от XMLТипЗнч?
ТипЗнч возвращает объект Тип для сравнения в коде. XMLТипЗнч возвращает имя типа для XML-сериализации, например CatalogRef.Номенклатура, — его удобно писать в логи и файлы обмена.
Как проверить, что значение — элемент перечисления?
Сравните с типом конкретного перечисления: ТипЗнч(Х) = Тип("ПеречислениеСсылка.ВидыДоговоров"). Для любого перечисления подходит Перечисления.ТипВсеСсылки().СодержитТип(ТипЗнч(Х)).
Почему проверка типа ссылки работает в одной базе и не работает в другой?
Имя типа берётся из имени объекта в конфигурации. Если справочник в другой базе называется иначе или его нет, Тип("СправочникСсылка.…") вызовет ошибку Тип не определен.