Проверка типа значения во встроенном языке 1С и в запросах
Содержание
Тип объекта в 1С определяют функцией ТипЗнч(Значение) и сравнивают результат с Тип("ИмяТипа"): Если ТипЗнч(Док) = Тип("ДокументСсылка.РеализацияТоваровУслуг") Тогда. Это основной способ для кода на платформе 8.3. Когда нужно узнать не конкретный тип, а вид объекта (справочник это или документ), подключают метаданные: Метаданные.НайтиПоТипу() или метод ссылки Метаданные(). В запросе ту же задачу решают операторы ССЫЛКА и ТИПЗНАЧЕНИЯ().
Какой способ выбрать, зависит от вопроса, который вы задаёте значению. «Это ли конкретный тип?» — ТипЗнч плюс Тип. «Входит ли тип в список допустимых?» — ОписаниеТипов.СодержитТип(). «Какой это объект конфигурации и как он называется?» — метаданные. Всё, что ниже, работает одинаково в любой конфигурации: это механизм платформы, а не БП или УТ.
Сводная таблица: чем определить тип объекта 1С
| Задача | Чем решать | Где работает |
|---|---|---|
| Сравнить с одним конкретным типом | ТипЗнч(З) = Тип("...") | везде |
| Проверить вхождение в набор типов | ОписаниеТипов.СодержитТип(ТипЗнч(З)) | везде |
| Узнать, справочник ли это вообще | Справочники.ТипВсеСсылки().СодержитТип(...) | сервер |
| Получить имя объекта метаданных | Метаданные.НайтиПоТипу(ТипЗнч(З)).ПолноеИмя() | сервер, толстый клиент |
| Отобрать записи по типу в запросе | Поле ССЫЛКА Документ.Имя | запрос |
| Получить тип поля в выборке | ТИПЗНАЧЕНИЯ(Поле) | запрос |
Последние две строки — для запросов, остальные — для встроенного языка. Каждую разберём с примерами.
Функция ТипЗнч: как узнать тип значения
ТипЗнч() принимает любое значение и возвращает объект типа Тип. Сам по себе он мало что даёт: его не выводят пользователю, а сравнивают с эталоном.
Если ТипЗнч(Значение) = Тип("Число") Тогда
Сумма = Сумма + Значение;
ИначеЕсли ТипЗнч(Значение) = Тип("Строка") Тогда
Текст = Текст + Значение;
ИначеЕсли ТипЗнч(Значение) = Тип("Дата") Тогда
Период = НачалоМесяца(Значение);
КонецЕсли;
Что вернёт ТипЗнч() для разных значений:
- 🔢 примитивы —
Число,Строка,Дата,Булево; - 🔗 ссылки —
СправочникСсылка.Контрагенты,ДокументСсылка.ПоступлениеТоваровУслуг,ПеречислениеСсылка.ВидыДоговоров; - 📦 объекты —
СправочникОбъект.Номенклатура,ДокументОбъект.ПриходныйКассовыйОрдер; - 🧰 универсальные коллекции —
Структура,Массив,Соответствие,ТаблицаЗначений; - ❔ пустота —
НеопределеноиNull, у каждого свой тип.
Ссылка и объект — разные типы. Если в процедуру пришёл ДокументОбъект, а вы сравниваете с ДокументСсылка того же документа, условие не сработает. Это частая причина «код не заходит в ветку».
ТипЗнч в форме: ловушка с данными формы
В управляемой форме реквизит Объект — это не ДокументОбъект, а данные формы. ТипЗнч(Объект) вернёт ДанныеФормыСтруктура, и сравнение с Тип("ДокументОбъект.Счет") всегда даст Ложь.
Проверяйте тип ссылки, она в данных формы есть всегда:
&НаКлиенте
Процедура ПроверитьТип()
Если ТипЗнч(Объект.Ссылка) = Тип("ДокументСсылка.СчетНаОплатуПокупателю") Тогда
// логика для счёта
КонецЕсли;
КонецПроцедуры
Для нового, ещё не записанного документа Объект.Ссылка пустая, но тип у неё уже правильный. Пустая ссылка документа «Счёт» остаётся ДокументСсылка.СчетНаОплатуПокупателю, а не Неопределено.
Тип и строковые имена типов: как получить эталон для сравнения
Функция Тип() строит объект типа по строке с именем. Имя пишут по правилу «вид + Ссылка/Объект/Менеджер + точка + имя объекта из конфигуратора».
| Что нужно | Строка для Тип() |
|---|---|
| Ссылка на справочник | "СправочникСсылка.Номенклатура" |
| Объект документа | "ДокументОбъект.РеализацияТоваровУслуг" |
| Значение перечисления | "ПеречислениеСсылка.СтавкиНДС" |
| Счёт плана счетов | "ПланСчетовСсылка.Хозрасчетный" |
| Вид характеристики | "ПланВидовХарактеристикСсылка.ВидыСубконтоХозрасчетные" |
| Бизнес-процесс | "БизнесПроцессСсылка.Задание" |
Имя объекта берут из свойства «Имя» в конфигураторе, а не из синонима. Синоним «Реализация (акты, накладные, УПД)» в Тип() не подставить.
Если строка с ошибкой или такого объекта в конфигурации нет, платформа выдаёт исключение Тип не определен (СправочникСсылка.Контрагент). Код в этом месте падает целиком. Для универсальных обработок, которые запускают в разных конфигурациях, такую проверку оборачивают:
Функция ТипПоИмени(ИмяТипа)
Попытка
Возврат Тип(ИмяТипа);
Исключение
Возврат Неопределено;
КонецПопытки;
КонецФункции
Строковое представление типа
Обратная операция — Строка(ТипЗнч(З)) — даёт представление типа для человека. Для сравнения оно не годится: зависит от синонимов и языка интерфейса. Если нужна стабильная строка с именем, есть два пути:
- 🏷
XMLТип(ТипЗнч(З)).ИмяТипа— вернёт английское XML-имя видаCatalogRef.Номенклатура; - 📚
Метаданные.НайтиПоТипу(ТипЗнч(З)).ПолноеИмя()— вернётСправочник.Номенклатура.
«Класс» объекта: определяем вид через метаданные
В языке 1С нет свойства Класс, как в объектных языках. Тот же вопрос — «к какому виду объектов относится значение» — решают через метаданные и описания типов.
Метод Метаданные() у ссылки и объекта
У любой ссылки и любого объекта прикладного типа есть метод Метаданные(). Он возвращает объект метаданных, а у того есть имя, синоним и полное имя:
МетаОбъект = Ссылка.Метаданные();
ИмяОбъекта = МетаОбъект.Имя; // "Контрагенты"
ПолноеИмя = МетаОбъект.ПолноеИмя(); // "Справочник.Контрагенты"
ВидОбъекта = СтрРазделить(ПолноеИмя, ".")[0]; // "Справочник"
СтрРазделить есть в платформе с 8.3.6. Первая часть полного имени и есть «класс» в привычном смысле: Справочник, Документ, РегистрСведений, ПланСчетов.
НайтиПоТипу: когда на руках только тип
Если значения нет, а есть только Тип (например, из описания типов реквизита), используйте Метаданные.НайтиПоТипу():
МетаОбъект = Метаданные.НайтиПоТипу(ТипЗнч(Значение));
Если МетаОбъект = Неопределено Тогда
// примитив или коллекция: у Числа и Массива объекта метаданных нет
КонецЕсли;
Для Число, Строка, Структура функция вернёт Неопределено. Это удобный способ отличить прикладной тип конфигурации от платформенного.
Принадлежность к виду: Содержит и ТипВсеСсылки
Проверить, что объект — документ, а не справочник, можно двумя способами:
// по коллекции метаданных
ЭтоДокумент = Метаданные.Документы.Содержит(Ссылка.Метаданные());
// по описанию всех ссылок вида
ЭтоСправочник = Справочники.ТипВсеСсылки().СодержитТип(ТипЗнч(Ссылка));
ТипВсеСсылки() есть у менеджеров Справочники, Документы, Перечисления, ПланыСчетов и других видов. Второй вариант не требует получать объект метаданных и короче.
В типовых конфигурациях на БСП то же самое уже обёрнуто в общий модуль: функции ОбщегоНазначения.ЭтоСсылка(Тип), ОбщегоНазначения.ЭтоСправочник(ОбъектМетаданных), ОбщегоНазначения.ЭтоДокумент(ОбъектМетаданных). В доработках типовых баз лучше пользоваться ими, чем писать своё.
Составной тип: проверка значения из ОписаниеТипов
Составной тип — реквизит, который может хранить значения разных типов. Классика — «Субконто» в проводке или поле «Документ-основание», куда кладут и поступление, и реализацию.
Что лежит в составном реквизите
Значение в таком реквизите всегда одного конкретного типа. ТипЗнч() вернёт именно его, а не «составной тип». Поэтому обработка выглядит как цепочка проверок:
Основание = Объект.ДокументОснование;
Если ТипЗнч(Основание) = Тип("ДокументСсылка.ЗаказКлиента") Тогда
ЗаполнитьПоЗаказу(Основание);
ИначеЕсли ТипЗнч(Основание) = Тип("ДокументСсылка.СчетНаОплату") Тогда
ЗаполнитьПоСчету(Основание);
ИначеЕсли Не ЗначениеЗаполнено(Основание) Тогда
Возврат;
КонецЕсли;
Имена документов в примере условные — подставьте свои.
Работа с описанием типов реквизита
Чтобы узнать, какие типы реквизит допускает, читают его описание типов в метаданных:
ОписаниеРеквизита = Метаданные.Документы.РеализацияТоваровУслуг.Реквизиты.Контрагент.Тип;
Для Каждого ДопустимыйТип Из ОписаниеРеквизита.Типы() Цикл
Сообщить(Строка(ДопустимыйТип));
КонецЦикла;
Если ОписаниеРеквизита.СодержитТип(ТипЗнч(НовоеЗначение)) Тогда
Объект.Контрагент = НовоеЗначение;
КонецЕсли;
Три метода ОписаниеТипов, которые закрывают почти все задачи:
Типы()— массив всех типов описания.СодержитТип(Тип)— входит ли тип в описание, возвращает Булево.ПривестиЗначение(Значение)— приводит значение к описанию. Строку"15"превратит в число 15, а неподходящую ссылку — в значение по умолчанию.
Описание типов можно собрать и в коде: Новый ОписаниеТипов("Число,Строка") или с массивом типов. Удобно, когда список допустимых типов нужен в нескольких местах модуля.
У составного реквизита пустое значение — Неопределено, а не пустая ссылка одного из типов. Проверка Основание.Пустая() на таком значении упадёт с ошибкой: у Неопределено нет методов. Сначала ЗначениеЗаполнено(), потом методы ссылки.
Неопределено, Null и пустая ссылка: проверки перед работой с типом
Эти три значения путают чаще всего, а от них зависит, отработает ли проверка типа.
| Значение | Откуда берётся | ТипЗнч() вернёт | ЗначениеЗаполнено() |
|---|---|---|---|
Неопределено | пустой составной реквизит, необязательный параметр, ненайденный ключ | Тип("Неопределено") | Ложь |
Null | поле левого соединения в запросе без пары | Тип("Null") | Ложь |
| Пустая ссылка | незаполненный реквизит одного ссылочного типа | тип этой ссылки | Ложь |
Порядок проверок, который не даёт исключений:
Если Значение = Неопределено Или Значение = Null Тогда
Возврат;
КонецЕсли;
Если ТипЗнч(Значение) = Тип("СправочникСсылка.Номенклатура")
И ЗначениеЗаполнено(Значение) Тогда
Артикул = Значение.Артикул;
КонецЕсли;
Обращение Значение.Артикул к Неопределено или Null даёт ошибку Поле объекта не обнаружено. К пустой ссылке — не даёт: вернётся пустое значение реквизита. Поэтому проверку типа ставьте до разыменования, а ЗначениеЗаполнено() — когда вам важно, что значение реально выбрано.
Тип поля в запросе: ССЫЛКА, ТИПЗНАЧЕНИЯ и ВЫРАЗИТЬ
В запросе встроенные функции языка недоступны, у него свои средства.
Отбор по типу
ВЫБРАТЬ
Хозрасчетный.Регистратор,
Хозрасчетный.Сумма
ИЗ
РегистрБухгалтерии.Хозрасчетный КАК Хозрасчетный
ГДЕ
Хозрасчетный.Регистратор ССЫЛКА Документ.ПоступлениеТоваровУслуг
ССЫЛКА проверяет, что значение поля — ссылка на указанную таблицу. Тот же смысл даёт условие ТИПЗНАЧЕНИЯ(Поле) = ТИП(Документ.ПоступлениеТоваровУслуг). Обратите внимание: в запросе ТИП() пишут без кавычек и без слова «Ссылка».
Получить тип в выборку
ТИПЗНАЧЕНИЯ(Поле) можно вывести как колонку. В результате запроса будет значение типа Тип, его сравнивают в коде обычным способом. Так удобно группировать регистраторы по видам документов, не разыменовывая каждую ссылку.
Сужение составного типа
Если поле составное, а нужен реквизит одного из типов, используйте ВЫРАЗИТЬ:
ВЫРАЗИТЬ(Хозрасчетный.СубконтоДт1 КАК Справочник.Контрагенты).ИНН КАК ИНН
Без ВЫРАЗИТЬ запрос к реквизиту составного поля соединяется со всеми таблицами из состава типа. Для «Субконто» в плане счетов это десятки таблиц.
Скорость и правила хорошего кода при проверке типа
Сама ТипЗнч() работает быстро: ей не нужно читать базу. Медленными проверки делают окружающие операции.
- ⏱ Выносите
Тип()из цикла. Строка разбирается при каждом вызове. Если проверяете 50 000 строк таблицы, создайте переменнуюТипНоменклатура = Тип("СправочникСсылка.Номенклатура")до цикла. - 🗄 Не разыменовывайте ради типа.
Ссылка.Метаданные()не читает данные объекта, аСсылка.ПолучитьОбъект()— читает весь объект с табличными частями. Для проверки типа второе не нужно никогда. - 🧮 Набор типов — через ОписаниеТипов. Десять
ИначеЕслисТип()читать труднее, чем одноСодержитТип(). Если для каждого типа своя логика, держите соответствие «тип → имя процедуры». - 🌐 Метаданные — на сервере. На тонком клиенте глобальный объект
Метаданныенедоступен. Проверку вида объекта выносите в серверную функцию без контекста:&НаСервереБезКонтекста.ТипЗнчиТипдля ссылок на клиенте работают. - 🔍 В запросах —
ССЫЛКАиВЫРАЗИТЬ. Отбирайте нужные типы в запросе, а не выбирайте всё и фильтруйте в цикле. - 📌 Сравнивайте типы, а не строки.
Строка(ТипЗнч(З)) = "Номенклатура"сломается после переименования синонима или включения английского интерфейса.
Не сравнивайте тип по строковому представлению — это единственная ошибка из списка, которая молча ломает логику без всякого сообщения: условие просто перестаёт выполняться.
Частые вопросы
Чем отличается ТипЗнч от Тип?
ТипЗнч(Значение) узнаёт тип у готового значения. Тип("Имя") строит тип по строке. Первое — что проверяем, второе — с чем сравниваем.Как проверить, что значение — любая ссылка, а не примитив?
ОбщегоНазначения.ЭтоСсылка(ТипЗнч(Значение)). Без БСП — Метаданные.НайтиПоТипу(ТипЗнч(Значение)) <> Неопределено вместе с проверкой, что найденный объект относится к ссылочному виду.Почему ТипЗнч у пустого реквизита вернул Неопределено, хотя тип у реквизита один?
Можно ли определить тип в обработчике подписки на событие?
Источник содержит объект, и ТипЗнч(Источник) вернёт, например, ДокументОбъект.Имя. Если подписка общая на несколько документов, ветвите логику по этому типу.Как получить список всех типов документов конфигурации?
Документы.ТипВсеСсылки().Типы() вернёт массив типов ссылок на все документы. Обход Метаданные.Документы даст то же самое в виде объектов метаданных.