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

· 9 мин

Содержание

Тип любого значения во встроенном языке 1С возвращает функция ТипЗнч(). Она принимает значение и отдаёт объект Тип, который сравнивают с эталоном: ТипЗнч(Значение) = Тип("Строка"). Этого хватает для 90 % задач — отличить строку от числа, ссылку на справочник от документа, заполненное поле от пустого.

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

Все значения в 1С делятся на несколько групп: примитивные (Строка, Число, Дата, Булево), ссылочные (ссылки на справочники, документы, перечисления и прочие прикладные объекты), универсальные коллекции (Массив, Структура, Соответствие, ТаблицаЗначений, СписокЗначений) и служебные значения — Неопределено, NULL, Тип. Приём определения типа один и тот же для любой группы, отличается только имя, которое вы передаёте в оператор Тип().

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

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

Если ТипЗнч(Значение) = Тип("Строка") Тогда
    Сообщить("Строка длиной " + СтрДлина(Значение));
ИначеЕсли ТипЗнч(Значение) = Тип("Число") Тогда
    Сообщить("Число: " + Формат(Значение, "ЧГ=0"));
ИначеЕсли ТипЗнч(Значение) = Тип("Дата") Тогда
    Сообщить("Дата: " + Формат(Значение, "ДФ=dd.MM.yyyy"));
КонецЕсли;

Имена примитивных типов пишутся строкой: "Строка", "Число", "Дата", "Булево". Ссылочные — через точку: "СправочникСсылка.Номенклатура", "ДокументСсылка.РеализацияТоваровУслуг", "ПеречислениеСсылка.СтавкиНДС".

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

Частая ошибка новичка — сравнивать два значения вместо их типов: Если Значение = Тип("Строка") всегда ложно, потому что слева лежит строка, а справа объект Тип. Обязательно оборачивайте левую часть в ТипЗнч().

Имя типа в операторе Тип() регистронезависимо, но писать его принято как в синтакс-помощнике. Опечатка в имени ("Срока" вместо "Строка" или неверное имя справочника) не всплывёт при синтаксическом контроле — ошибка возникнет уже в момент выполнения строки с Тип(). Поэтому ссылочные имена удобно собирать один раз в переменные и проверять на тестовой базе.

Сравнение через оператор Тип и метаданные объекта

Оператор Тип("...") разбирает переданное имя и возвращает объект Тип. Разбор строки — операция не бесплатная, поэтому если один и тот же тип проверяется в цикле, вынесите его в переменную заранее:

ТипНоменклатуры = Тип("СправочникСсылка.Номенклатура");

Для Каждого Строка Из ТабличнаяЧасть Цикл
    Если ТипЗнч(Строка.Объект) = ТипНоменклатуры Тогда
        // обрабатываем позицию номенклатуры
    КонецЕсли;
КонецЦикла;

Если значение — ссылка, у него есть метод Метаданные(), который даёт объект метаданных. Через него удобно получить имя объекта без разбора строки типа:

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

Обратная задача — узнать тип ссылок по полному имени из метаданных — решается через глобальный объект Метаданные:

ОбъектМД = Метаданные.НайтиПоПолномуИмени("Справочник.Контрагенты");

Метод Метаданные() вызывается у ссылки, а не у Неопределено или NULL. Перед обращением убедитесь, что значение действительно ссылка (см. раздел про проверку ссылочности), иначе получите ошибку выполнения.

Составной тип: объект ОписаниеТипов и его методы

Когда реквизит объявлен составным (например, «Строка или Число», или «несколько справочников»), один вызов ТипЗнч() покажет только то, что лежит в поле сейчас. Чтобы узнать все допустимые типы, работают с объектом ОписаниеТипов.

Создать описание можно вручную или взять готовое из метаданных реквизита:

// вручную
Описание = Новый ОписаниеТипов("Строка, Число");

// из метаданных реквизита справочника
Описание = Метаданные.Справочники.Контрагенты.Реквизиты.ИНН.Тип;

Два метода закрывают почти все задачи:

  • 🔹 СодержитТип(Тип) — вернёт Истина, если такой тип в описании допустим.
  • 🔹 Типы() — вернёт массив объектов Тип, по которому можно пройти циклом.
Если Описание.СодержитТип(Тип("Число")) Тогда
    Сообщить("Поле допускает число");
КонецЕсли;

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

Не путайте «какой тип допустим» и «какой тип лежит сейчас». СодержитТип() отвечает на первый вопрос (по описанию реквизита), ТипЗнч() — на второй (по конкретному значению). Для составного реквизита нужны оба.

У ОписаниеТипов есть и квалификаторы — длина строки, точность числа, состав дат. Их читают через свойства КвалификаторыСтроки, КвалификаторыЧисла, КвалификаторыДаты, когда нужно, например, динамически собрать форму ввода под реквизит.

Полезный метод того же объекта — ПривестиЗначение(). Он приводит значение к типу описания и, что особенно удобно, подставляет значение по умолчанию вместо Неопределено. Для числового описания это 0, для строкового — пустая строка:

ОписаниеЧисла = Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10, 2));
Количество = ОписаниеЧисла.ПривестиЗначение(ВозможноНеопределено); // вернёт 0, а не Неопределено

Как определить тип элемента коллекции

У универсальных коллекций свой тип: ТипЗнч(Массив) = Тип("Массив"), ТипЗнч(Стр) = Тип("Структура") и так далее. А вот тип элемента внутри коллекции проверяют тем же ТипЗнч(), применяя его к элементу:

Для Каждого Элемент Из Массив Цикл
    Если ТипЗнч(Элемент) = Тип("Структура") Тогда
        // элемент — структура, читаем её свойства
    КонецЕсли;
КонецЦикла;

Для ТаблицаЗначений тип значения конкретной колонки задаётся её описанием: Таблица.Колонки.Сумма.ТипЗначения — это тот же объект ОписаниеТипов, с которым работают методы СодержитТип() и Типы().

Как проверить, что значение — ссылка на объект

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

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

Аналогичные методы есть у Документы, ПланыВидовХарактеристик, ПланыСчетов и других менеджеров. Такой способ надёжнее, чем проверять наличие метода Метаданные(), и не падает на примитивах.

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

Определение типа прямо в запросе

Тип значения проверяют не только в коде, но и в языке запросов — это полезно, когда поле регистратора или измерения имеет составной тип. Работают три конструкции.

Оператор ССЫЛКА фильтрует строки по типу ссылки:

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

Функция ТИПЗНАЧЕНИЯ() в паре с ТИП() сравнивает тип поля:

ГДЕ
    ТИПЗНАЧЕНИЯ(Движения.Регистратор) = ТИП(Документ.РеализацияТоваровУслуг)

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

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

Если тип не совпал, ВЫРАЗИТЬ вернёт NULL — это штатное поведение, его стоит ловить через ЕСТЬNULL().

Неопределено, NULL и пустые ссылки: что вернёт ТипЗнч

Эти три значения новички путают чаще всего, а ведут они себя по-разному.

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

Ключевой нюанс: пустая ссылка сохраняет свой ссылочный тип. То есть ТипЗнч(ПустаяСсылка) вернёт СправочникСсылка.Номенклатура, а не Неопределено. Проверять заполненность надо отдельно:

ПустаяНоменклатура = Справочники.Номенклатура.ПустаяСсылка();

Сообщить(Строка(ТипЗнч(ПустаяНоменклатура))); // СправочникСсылка.Номенклатура
Сообщить(ПустаяНоменклатура.Пустая());        // Истина
Сообщить(ЗначениеЗаполнено(ПустаяНоменклатура)); // Ложь

NULL приходит из запросов, в коде его обычно получают из выборки. Сравнивать с ним через = можно, но универсальнее ЗначениеЗаполнено(): эта функция вернёт Ложь для Неопределено, NULL, нуля, пустой строки, пустой даты и пустой ссылки сразу. Если нужно именно отличить «тип не определён» от «тип есть, но пусто», берите ТипЗнч() и Пустая() по отдельности.

В запросе NULL лучше гасить сразу: ЕСТЬNULL(Таблица.Сумма, 0). Тогда в коде не придётся отдельно проверять тип NULL.

Что из методов определения типа быстрее

Для разовой проверки разница незаметна. Она проявляется в циклах на тысячах строк, при обходе большой таблицы значений или в обработчиках проведения. Основной расход — не сам ТипЗнч() (он быстрый), а повторный разбор строки в Тип("...").

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

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

Практический вывод: внутри цикла не должно быть ни одного «голого» Тип("..."). Соберите нужные типы в переменные до входа в цикл — и перебор ускорится без изменения логики.

Практические примеры и отладка типов

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

Функция ПредставлениеЗначения(Знач Значение)

    Если НЕ ЗначениеЗаполнено(Значение) Тогда
        Возврат "<пусто>";
    КонецЕсли;

    ТипЗначения = ТипЗнч(Значение);

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

    ИначеЕсли Справочники.ТипВсеСсылки().СодержитТип(ТипЗначения)
        ИЛИ Документы.ТипВсеСсылки().СодержитТип(ТипЗначения) Тогда
        Возврат Строка(Значение) + " (" + Значение.Метаданные().Имя + ")";

    Иначе
        Возврат Строка(ТипЗначения);
    КонецЕсли;

КонецФункции

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

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

Не всегда тип нужно проверять кодом — иногда достаточно подсмотреть его во время отладки.

  1. Поставьте точку останова (F9) на нужной строке и запустите отладку (F5).
  2. Когда выполнение остановится, наведите курсор на переменную — во всплывающей подсказке виден и значение, и тип.
  3. Для детального разбора откройте окно Вычислить выражение (Shift+F9), введите ТипЗнч(Перем) и выполните — в дереве результата отобразится тип.
  4. Переменную можно добавить в Табло (меню Отладка), чтобы следить за её типом и значением по мере выполнения.
Что проверить, если ТипЗнч даёт неожиданный результат
Выполнено: 0 / 5

Проверьте себя

Проверьте себя
Что вернёт `ТипЗнч()` для пустой ссылки на справочник?
Какой метод показывает ВСЕ допустимые типы составного реквизита?
Как сравнить тип значения со строкой в коде?

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

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