Определяем тип значения в 1С: функции, ссылки, коллекции и запросы

· 9 мин

Содержание

Тип значения в коде 1С проверяют одной строкой: Если ТипЗнч(Значение) = Тип("Строка") Тогда. Функция ТипЗнч() возвращает тип переданного значения, а Тип() — эталонный тип для сравнения по его имени. Этого хватает в девяти случаях из десяти.

Дальше начинаются нюансы. Ссылку на справочник мало отличить от числа — надо понять, какой именно это справочник. Составной реквизит хранит сразу несколько типов. В языке запросов проверка типа устроена иначе, чем в модуле. Разберём всё по порядку: от базовой проверки до рефлексии и ускорения проверок в цикле.

Быстрый способ узнать тип значения

Две встроенные функции закрывают базовую задачу:

  • 🔹 ТипЗнч(Значение) — возвращает объект Тип, которым является переданное значение.
  • 🔹 Тип("ИмяТипа") — возвращает объект Тип по строковому имени.

Сравниваем их через =:

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

Имена примитивных типов пишутся кириллицей, с заглавной буквы: Число, Строка, Дата, Булево. Отдельно стоят два служебных значения:

  • Неопределено — пустое значение переменной или незаполненного реквизита. Проверка: ТипЗнч(Знач) = Тип("Неопределено").
  • NULL — приходит из запроса, когда в соединении нет данных. Проверка: ТипЗнч(Знач) = Тип("NULL").

Неопределено и NULL — разные вещи. Значение из левого соединения запроса будет NULL, а не Неопределено. Если перепутать, условие просто не сработает.

Когда нужно только понять, заполнено ли значение (не пустое, не ноль, не пустая дата), тип проверять не обязательно — есть функция ЗначениеЗаполнено(Значение). Она сама знает правила «пустоты» для каждого типа.

Если по значению надо ветвить логику на несколько примитивов сразу, удобно собрать результат в строку-метку:

Функция ИмяПримитива(Значение)
    ТекТип = ТипЗнч(Значение);
    Если      ТекТип = Тип("Число")  Тогда Возврат "Число";
    ИначеЕсли ТекТип = Тип("Строка") Тогда Возврат "Строка";
    ИначеЕсли ТекТип = Тип("Дата")   Тогда Возврат "Дата";
    ИначеЕсли ТекТип = Тип("Булево") Тогда Возврат "Булево";
    Иначе                                  Возврат "Прочее";
    КонецЕсли;
КонецФункции

Это заготовка для универсальных обработчиков — выгрузки в файл, записи в лог, сериализации, — где заранее неизвестно, что придёт на вход.

Как отличить один справочник от другого

Ссылочные типы — это ссылки на элементы справочников, документы, перечисления, планы видов характеристик и прочие объекты метаданных. У каждого из них своё полное имя типа:

Если ТипЗнч(Ссылка) = Тип("СправочникСсылка.Контрагенты") Тогда
    // это ссылка на контрагента
ИначеЕсли ТипЗнч(Ссылка) = Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда
    // это ссылка на документ реализации
КонецЕсли;

Шаблон имени: СправочникСсылка.<Имя>, ДокументСсылка.<Имя>, ПеречислениеСсылка.<Имя>, ПланВидовХарактеристикСсылка.<Имя>. Имя берётся из конфигуратора или дерева метаданных 1С:EDT ровно как оно там написано.

Есть второй путь — через метаданные. Он удобнее, когда имя справочника заранее неизвестно:

ОбъектМетаданных = Ссылка.Метаданные();
Сообщить(ОбъектМетаданных.Имя);         // короткое имя, напр. "Контрагенты"
Сообщить(ОбъектМетаданных.ПолноеИмя()); // "Справочник.Контрагенты"

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

Если Ссылка.Метаданные() = Метаданные.Справочники.Контрагенты Тогда
    // точно контрагент
КонецЕсли;

Оба подхода рабочие. Для горячего кода в цикле надёжнее сравнение через Тип(...): вызов Метаданные() обращается к метаданным и обходится дороже. Про цену проверок — отдельный раздел ниже.

Пустую ссылку определяют не сравнением с Неопределено, а методом .Пустая():

Если Не Ссылка.Пустая() Тогда
    // ссылка указывает на существующий объект
КонецЕсли;

Проверка коллекций и составных реквизитов

Коллекции тоже имеют свой тип, и проверяются той же парой функций:

КоллекцияПроверка типа
МассивТипЗнч(Знач) = Тип("Массив")
СтруктураТипЗнч(Знач) = Тип("Структура")
СоответствиеТипЗнч(Знач) = Тип("Соответствие")
Таблица значенийТипЗнч(Знач) = Тип("ТаблицаЗначений")
Список значенийТипЗнч(Знач) = Тип("СписокЗначений")

Для структуры чаще нужна не проверка типа целиком, а наличие нужного ключа. Для этого есть метод Свойство():

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

Метод возвращает Истина, если ключ найден, и заодно кладёт его значение во второй параметр.

Составной реквизит — это поле, которое по описанию метаданных может хранить несколько типов (например, «Контрагент» или «Физическое лицо»). Описание типов такого поля читают через ОписаниеТипов:

ОписаниеТипов = Метаданные.Документы.ПоступлениеНаРасчетныйСчет
    .Реквизиты.Контрагент.Тип;

Если ОписаниеТипов.СодержитТип(Тип("СправочникСсылка.Контрагенты")) Тогда
    // среди допустимых типов есть контрагент
КонецЕсли;

Для Каждого ТипЭлемента Из ОписаниеТипов.Типы() Цикл
    Сообщить(Строка(ТипЭлемента));
КонецЦикла;

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

У колонки таблицы значений допустимые типы лежат в свойстве ТипЗначения — это тоже ОписаниеТипов:

ОписаниеКолонки = ТаблицаЗначений.Колонки.Количество.ТипЗначения;

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

В запросе своя логика — проверять тип значения в коде после выборки неэффективно, лучше сразу отфильтровать в запросе. Для составных полей работает оператор ССЫЛКА:

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

Оператор ССЫЛКА возвращает Истина, когда значение поля относится к указанному типу. Так отбирают, например, только те движения, которые сделаны нужным видом документа.

Функция ТИПЗНАЧЕНИЯ() в запросе возвращает тип поля — её удобно выводить или сравнивать в условии выбора:

ВЫБРАТЬ
    ВЫБОР
        КОГДА Движения.Регистратор ССЫЛКА Документ.РеализацияТоваровУслуг
            ТОГДА "Реализация"
        КОГДА Движения.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг
            ТОГДА "Поступление"
        ИНАЧЕ "Прочее"
    КОНЕЦ КАК ВидДокумента
ИЗ
    РегистрНакопления.ТоварыНаСкладах КАК Движения

Когда составное поле нужно использовать как ссылку конкретного типа (обратиться к его реквизитам), применяют приведение ВЫРАЗИТЬ:

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

Без ВЫРАЗИТЬ обратиться к реквизиту составного поля нельзя — запрос либо не увидит реквизит, либо вернёт данные через медленное неявное соединение по всем возможным таблицам.

Отдельно стоит значение NULL в результате запроса. Если нужно отфильтровать строки, где поле не заполнено, в условии пишут ГДЕ Движения.Регистратор ЕСТЬ НЕ NULL, а не сравнивают с пустой ссылкой. В самом коде после выборки такое поле проверяют как ТипЗнч(Знач) = Тип("NULL") — тот же NULL, что и в разделе про базовые функции.

Динамическая типизация и рефлексия

Иногда тип заранее неизвестен — он приходит в параметре, собирается из строки или берётся из настроек. Встроенный язык 1С позволяет работать с типами как с данными.

Создать объект по типу, который лежит в переменной:

ТипКоллекции = Тип("Массив");
НоваяКоллекция = Новый(ТипКоллекции);

Найти объект метаданных по типу ссылки — обратная операция к Метаданные():

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

Если по типу ничего не найдено (например, передали Тип("Число")), метод вернёт Неопределено — это условие стоит обрабатывать.

Обратиться к менеджеру справочника по имени, не зная его на этапе написания кода:

ИмяСправочника = "Контрагенты";
ПустаяСсылка = Справочники[ИмяСправочника].ПустаяСсылка();

Из этих кирпичиков собирается универсальная функция, которая определяет род ссылки — справочник, документ или перечисление — не завязываясь на конкретные имена:

Функция РодОбъекта(Ссылка)
    Если Не ЗначениеЗаполнено(Ссылка) Тогда
        Возврат "Пусто";
    КонецЕсли;
    ПолноеИмя = Ссылка.Метаданные().ПолноеИмя();
    Возврат СтрРазделить(ПолноеИмя, ".")[0]; // "Справочник", "Документ", ...
КонецФункции

ПолноеИмя() возвращает строку вида Справочник.Контрагенты, а СтрРазделить берёт из неё первый сегмент. Такие приёмы нужны в универсальных обработках: обмене данными, групповых операциях, выгрузках. Пользуйтесь ими аккуратно — читаемость и скорость у динамического кода ниже, чем у прямого.

Ещё один инструмент рефлексии — конструктор ОписаниеТипов. Он собирает набор допустимых типов программно, например чтобы ограничить тип реквизита формы или колонки динамической таблицы:

ДопустимыеТипы = Новый ОписаниеТипов("СправочникСсылка.Контрагенты, СправочникСсылка.ФизическиеЛица");
Если ДопустимыеТипы.СодержитТип(ТипЗнч(ВведённаяСсылка)) Тогда
    // пользователь выбрал разрешённый тип
КонецЕсли;

Контроль типов в 1С:EDT

Встроенный язык 1С динамически типизирован: тип значения известен только в момент выполнения, и ТипЗнч() работает именно там. Но среду разработки 1С:EDT стоит упомянуть отдельно — она выводит типы ещё на этапе написания кода.

Что даёт EDT:

  • 📌 Вывод типа при наведении. Наведите курсор на переменную — EDT покажет, какой тип он для неё определил по присваиваниям и контексту.
  • 📌 Проверки модуля (validations). EDT подчёркивает обращение к несуществующему свойству или методу, когда тип переменной ему известен.
  • 📌 Аннотации типа в комментариях. Для параметров и переменных, тип которых EDT вывести не может, его указывают в комментарии перед объявлением — тогда появляются подсказки и проверки.

Конфигуратор так не умеет: его синтакс-помощник подсказывает по справочнику языка, но тип конкретной переменной не отслеживает. Поэтому разработку с контролем типов удобнее вести в EDT, а ТипЗнч() в коде остаётся единственным способом узнать тип в рантайме в обеих средах.

Как ускорить проверки типа в цикле

На тысячах итераций способ проверки заметно влияет на время. Несколько правил:

  1. Выносите Тип(...) за пределы цикла. Каждый вызов Тип("Строка") разбирает строку и ищет тип. В цикле это повторяется зря:
ТипСтрока = Тип("Строка");     // один раз до цикла
Для Каждого Элемент Из Коллекция Цикл
    Если ТипЗнч(Элемент) = ТипСтрока Тогда
        // ...
    КонецЦикла;
  1. Не ловите тип через Попытка. Конструкция «попробовать привести — поймать исключение» работает в разы медленнее прямой проверки ТипЗнч() и мешает отладке.
  2. Фильтруйте тип в запросе, а не в коде. Если данные всё равно берутся запросом, оператор ССЫЛКА отсеет лишнее на уровне СУБД — это дешевле, чем выгрузить всё и перебирать в цикле.
  3. Не приводите тип к строке для сравнения. Строка(ТипЗнч(Знач)) = "Строка" зависит от представления типа и медленнее прямого сравнения объектов Тип.
Проверьте себя
Что вернёт `ТипЗнч(Неопределено)`?
Как правильно отличить ссылку на контрагента от ссылки на документ?
Что делает оператор `ССЫЛКА` в запросе?

Ошибки, из-за которых проверка типа не срабатывает

  • ❌ Опечатка в имени типа. Тип("Строка ") с лишним пробелом или Тип("String") вызывает ошибку «Указан несуществующий тип». Имя чувствительно к регистру и языку.
  • ❌ Забыли ТипЗнч. Сравнение Если Значение = Тип("Строка") всегда ложно: слева значение, справа — объект типа. Нужно ТипЗнч(Значение) слева.
  • ❌ Путают NULL и Неопределено. Данные из запроса дают NULL, незаполненная переменная — Неопределено. Проверка на одно не ловит другое.
  • ❌ Пустую ссылку сравнивают с Неопределено. Пустая ссылка справочника не равна Неопределено — у неё свой тип. Используйте .Пустая() или ЗначениеЗаполнено().
  • ❌ Сравнение типа через строку. Строка(ТипЗнч(Знач)) = "Число" ломается, потому что строковое представление типа не предназначено для сравнения. Сравнивайте объекты Тип напрямую.

Частые вопросы

Чем `ТипЗнч()` отличается от `Тип()`?
ТипЗнч(Значение) возвращает тип того значения, что вы передали. Тип("ИмяТипа") возвращает эталонный тип по строковому имени. Их сравнивают между собой: ТипЗнч(Знач) = Тип("Строка").
Как проверить, что в переменной ссылка на любой справочник, не уточняя какой?
Прямого «любой справочник» в одном сравнении нет. Используйте метаданные: Метаданные.НайтиПоТипу(ТипЗнч(Ссылка)) вернёт объект метаданных, а по его ПолноеИмя() можно понять, что это справочник.
Как узнать все типы составного реквизита?
Возьмите ОписаниеТипов из метаданных реквизита и переберите Типы(). Метод СодержитТип(Тип("...")) ответит, разрешён ли конкретный тип.
Почему условие с типом из запроса не срабатывает?
Скорее всего, в поле пришёл NULL, а вы сравниваете с Неопределено. Проверяйте ТипЗнч(Знач) = Тип("NULL") либо отбирайте тип прямо в запросе оператором ССЫЛКА.
Можно ли проверить тип значения в языке запросов?
Да. Оператор ССЫЛКА проверяет принадлежность поля к типу, функция ТИПЗНАЧЕНИЯ() возвращает тип, а ВЫРАЗИТЬ(... КАК ...) приводит составное поле к нужному типу для обращения к реквизитам.