Исправление ошибки Joi AssertError: Must be invoked on a Joi instance
Узнайте, как исправить Joi AssertError: Must be invoked on a Joi instance в проектах TypeScript. Полное руководство с использованием namespace imports, настройки TypeScript и лучших практик для Joi v18.0.1.
Joi AssertError: “Должна вызываться для экземпляра Joi” в проекте TypeScript
Я столкнулся с ошибкой AssertError в своем проекте TypeScript при использовании Joi v18.0.1. Сообщение об ошибке:
throw new AssertError(msgs.join(' '), assert);
^
AssertError: Must be invoked on a Joi instance.
Определения схем Joi
Вот определения схем, которые я использую:
import Joi from "joi";
import {env} from "#services/env";
export const AUTHENTICATE_ACCOUNT_SCHEMA = Joi.object({
username: Joi.string().min(4).max(20),
email: Joi.string()
.email({tlds: {allow: env.ALLOW_TLDS_EMAIL_VALIDATION === "true"}})
.min(4)
.max(40),
password: Joi.string().min(4).max(20).required(),
captcha: Joi.string().required(),
}).required();
export const GET_ACCOUNT_SCHEMA = Joi.object({
accountId: Joi.number().min(0).required(),
});
export const DELETE_ACCOUNT_SCHEMA = Joi.object({
accountId: Joi.number().min(0).required(),
});
export const SEARCH_ACCOUNTS_SCHEMA = Joi.object({
searchText: Joi.string(),
});
export const CREATE_ACCOUNT_SCHEMA = Joi.object({
email: Joi.string().email(),
username: Joi.string().min(4).max(20).required(),
password: Joi.string().min(4).max(20).required(),
role: Joi.string().valid("default", "teacher").required(),
captcha: Joi.string().required(),
}).required();
export const UPDATE_ACCOUNT_SCHEMA = Joi.object({
accountId: Joi.number().min(0),
balance: Joi.number(),
role: Joi.string().valid("root", "default", "teacher"),
blocked: Joi.boolean(),
email: Joi.string().email(),
username: Joi.string().min(4).max(20),
password: Joi.string().min(4).max(20),
})
.min(1)
.required();
Что я уже пробовал
Я уже проверил GitHub issue #1349, но это не помогло решить мою проблему. Я также пробовал получать ответы от LLMs безуспешно.
Детали окружения
- Версия Node.js: 24.5.0
- Версия Joi: 18.0.1
- Проект на TypeScript
Вопрос
Как исправить ошибку “AssertError: Must be invoked on a Joi instance” в моем проекте TypeScript с использованием Joi v18.0.1?
Ошибка “AssertError: Must be invoked on a Joi instance” в Joi v18.0.1
Ошибка “AssertError: Must be invoked on a Joi instance” в Joi v18.0.1 обычно возникает из-за изменений в том, как Joi обрабатывает импорты и цепочки вызовов методов в TypeScript. Эта ошибка происходит, когда методы Joi вызываются без правильного контекста или при неправильном использовании деструктурирующих импортов. Наиболее распространенным решением является изменение подхода к импорту и обеспечение правильной конфигурации TypeScript.
Содержание
- Основные причины ошибки
- Немедленные решения
- Исправления конфигурации TypeScript
- Лучшие практики использования Joi
- Руководство по миграции на Joi v18
- Расширенное устранение неполадок
Основные причины ошибки
Ошибка “Must be invoked on a Joi instance” обычно вызвана одной из следующих проблем:
1. Деструктурирующие импорты
При деструктуризации методов Joi, такой как import { string, object } from 'joi', методы теряют свой контекст и не могут правильно получить доступ к экземпляру Joi. Это является критическим изменением в Joi v18.0.1.
2. Проблемы с импортом по умолчанию
Использование import Joi from "joi" иногда вызывает проблемы с привязкой методов, где методы Joi не правильно привязаны к экземпляру.
3. Несоответствие типов TypeScript
Определения типов могут быть неправильно согласованы с поведением Joi во время выполнения, что вызывает ошибки утверждения во время проверки.
Немедленные решения
Решение 1: Использование импорта по пространству имен
Замените текущий оператор импорта на импорт по пространству имен:
import * as Joi from "joi";
Затем обновите определения схем, используя префикс пространства имен:
export const AUTHENTICATE_ACCOUNT_SCHEMA = Joi.object({
username: Joi.string().min(4).max(20),
email: Joi.string()
.email({tlds: {allow: env.ALLOW_TLDS_EMAIL_VALIDATION === "true"}})
.min(4)
.max(40),
password: Joi.string().min(4).max(20).required(),
captcha: Joi.string().required(),
}).required();
Решение 2: Использование именованных импортов с явной привязкой
Если вы предпочитаете именованные импорты, привяжите методы к экземпляру Joi:
import Joi from "joi";
// Привязка методов к экземпляру
const { object, string, number, boolean } = Joi;
export const AUTHENTICATE_ACCOUNT_SCHEMA = object({
username: string().min(4).max(20),
email: string()
.email({tlds: {allow: env.ALLOW_TLDS_EMAIL_VALIDATION === "true"}})
.min(4)
.max(40),
password: string().min(4).max(20).required(),
captcha: string().required(),
}).required();
Решение 3: Использование Joi.create() для сложных схем
Для более сложных сценариев используйте метод Joi.create():
import Joi from "joi";
export const AUTHENTICATE_ACCOUNT_SCHEMA = Joi.create()
.object({
username: Joi.string().min(4).max(20),
email: Joi.string()
.email({tlds: {allow: env.ALLOW_TLDS_EMAIL_VALIDATION === "true"}})
.min(4)
.max(40),
password: Joi.string().min(4).max(20).required(),
captcha: Joi.string().required(),
})
.required();
Исправления конфигурации TypeScript
Убедитесь, что ваш файл tsconfig.json содержит следующие настройки:
{
"compilerOptions": {
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true,
"moduleResolution": "node",
"resolveJsonModule": true
}
}
Настройка esModuleInterop: true помогает с совместимостью импорта по умолчанию, в то время как allowSyntheticDefaultImports обеспечивает более гибкие шаблоны импорта.
Лучшие практики использования Joi
1. Последовательный метод импорта
Придерживайтесь одного метода импорта на протяжении всего проекта. Импорт по пространству имен (import * as Joi from "joi") обычно является наиболее надежным подходом.
2. Шаблон цепочки вызовов методов
Всегда используйте правильную цепочку вызовов методов и избегайте хранения промежуточных результатов:
// Хорошо
const schema = Joi.object({
field: Joi.string().min(3).max(10).required()
});
// Плохо - может вызвать проблемы с контекстом
const string = Joi.string();
const schema = Joi.object({
field: string.min(3).max(10).required()
});
3. Типобезопасные функции валидации
Создавайте типобезопасные функции валидации для раннего обнаружения ошибок:
import { ValidationOptions } from 'joi';
export function validate<T>(schema: Joi.Schema, data: unknown, options?: ValidationOptions): T {
const { error, value } = schema.validate(data, options);
if (error) {
throw new Error(`Валидация не пройдена: ${error.message}`);
}
return value as T;
}
// Использование
const validatedData = validate<AuthenticateAccountPayload>(
AUTHENTICATE_ACCOUNT_SCHEMA,
inputData
);
Руководство по миграции на Joi v18
Ключевые изменения в Joi v18
- Изменения в методах импорта: Деструктурирующие импорты теперь требуют правильного контекста
- Определения типов: Обновленные типы TypeScript с более строгой валидацией
- Привязка методов: Методы должны вызываться на правильных экземплярах Joi
Пошаговая миграция
-
Обновление операторов импорта:
typescript// До (v17) import { object, string } from 'joi'; // После (v18) import * as Joi from 'joi'; -
Замена определений схем:
typescript// До const schema = object({ name: string().min(3) }); // После const schema = Joi.object({ name: Joi.string().min(3) }); -
Обновление вызовов валидации:
typescript// До const result = schema.validate(data); // После const result = Joi.validate(data, schema);
Расширенное устранение неполадок
1. Проблемы с расширениями Joi
Если вы используете расширения Joi, убедитесь, что они правильно импортированы и расширены:
import Joi from 'joi';
import customExtension from 'joi-custom-extension';
// Расширение Joi с пользовательскими расширениями
const extendedJoi = Joi.extend(customExtension);
// Использование расширенного Joi
const schema = extendedJoi.object({
field: extendedJoi.customType()
});
2. Динамическое создание схем
Для динамических схем используйте Joi.alternatives() с правильной проверкой экземпляра:
const dynamicSchema = Joi.alternatives().try(
Joi.object({ type: 'A', fieldA: Joi.string() }),
Joi.object({ type: 'B', fieldB: Joi.number() })
).describe();
3. Пользовательские сообщения об ошибках валидации
Реализуйте пользовательскую обработку ошибок для предоставления лучшей информации для отладки:
const schema = Joi.object({
email: Joi.string()
.email()
.error(errors => {
return errors.map(error => {
if (error.type === 'string.email') {
return new Error('Пожалуйста, укажите действительный адрес электронной почты');
}
return error;
});
})
});
Источники
- StackOverflow - AssertionError: you must pass Joi as an argument
- GitHub Issue #2295 - Must be invoked on a Joi instance
- GitHub Issue #1349 - Breaking changes in the latest versions
- GitHub Issue #1631 - Destructuring Throws Fatal
- StackOverflow - Joi getting validationSchema: Joi.object() error
- GitHub - Joi 18.0.0 Release Notes
Заключение
Ошибка “AssertError: Must be invoked on a Joi instance” в Joi v18.0.1 обычно решается путем использования импорта по пространству имен (import * as Joi from "joi") и обеспечения правильной цепочки вызовов методов. Ключевые выводы включают:
- Используйте импорт по пространству имен для поддержания правильного контекста для методов Joi
- Настройте TypeScript с
esModuleInterop: trueдля лучшей совместимости импорта - Избегайте деструктурирующих импортов, которые теряют контекст метода
- Реализуйте типобезопасные функции валидации для раннего обнаружения ошибок
- Следуйте последовательным шаблонам на протяжении всего проекта для предотвращения проблем
Если вы все еще сталкиваетесь с проблемами, рассмотрите возможность временного понижения версии до Joi v17.x или проверьте наличие любых пользовательских расширений Joi, которые могут вызывать конфликты. Миграция на Joi v18 требует внимания к шаблонам импорта и привязки методов, но улучшенная поддержка TypeScript и возможности валидации делают это worthwhile.