Валидация данных и приведение типов для TypeScript.
- DataValidator - Сервис валидации со встроенной проверкой типов.
- DataTypeCaster - Сервис конвертации значений согласно схеме данных.
- DefaultValuesApplier - Сервис заполняющий данные значениями по-умолчанию.
- Расширяемая архитектура, позволяющая добавлять пользовательские валидаторы и преобразователи типов.
- Подробная отладка.
DataSchema
является объектом, определяющим структуру для валидации
данных и приведения типов. Схема предоставляет гибкий способ описания
и ограничения входящих данных. Рассмотрим определение объекта схемы.
type DataSchema = {
type: DataType;
items?: DataSchema;
properties?: {[key: string]: DataSchema};
required?: boolean;
default?: unknown;
validate?: CallableValidator | CallableValidator[];
};
Определяет тип значения с помощью констант указанных ниже.
-
DataType.ANY
- принимает любой тип -
DataType.STRING
- строковые значения -
DataType.NUMBER
- числовые значения -
DataType.BOOLEAN
- логические значения -
DataType.ARRAY
- массивы -
DataType.OBJECT
- объекты (не экземпляры)
import {DataType} from '@e22m4u/ts-data-schema';
const schema = {
type: DataSchema.STRING,
}
Используется массивами для определения схемы элементов. Это вложенный
объект DataSchema
, описывающий каждый элемент массива.
const schema = {
type: DataSchema.ARRAY,
items: {type: DataSchema.STRING},
}
Используется объектами для определения схемы свойств. Каждое свойство
представляет собой пару ключ-значение, где значением является вложенный
объект DataSchema
.
const schema = {
type: DataSchema.OBJECT,
properties: {
foo: {type: DataSchema.STRING},
bar: {type: DataSchema.NUMBER},
},
}
Параметр исключает допуск пустых значений для данной схемы.
const schema = {
type: DataSchema.ANY,
required: true,
}
Параметр определяет значение по умолчанию на случай, если входящее значение является пустым.
const schema = {
type: DataSchema.STRING,
default: 'John Doe',
}
Пользовательская функция(и) валидации для применения дополнительных правил. Может быть одной функцией или массивом функций.
const schema = {
type: DataSchema.ANY,
validate: (value: unknown) => {
if (typeof value !== 'string')
throw new Error('A string required.');
},
}
Использование нескольких валидаторов.
const schema = {
type: DataSchema.ANY,
validate: [
myValidator1,
myValidator2,
],
}
Использование аргументов валидатора.
import {DataSchema} from '@e22m4u/ts-data-schema';
// import {ServiceContainer} from '@e22m4u/js-service';
function nonEmptyString(
value: unknown,
schema: DataSchema,
sourcePath?: string,
// services: ServiceContainer,
) {
if (!value || typeof value !== 'string') {
if (sourcePath)
throw new ValidationError(
'Value of %v must be a non-empty String, but %v given.',
sourcePath,
value,
);
throw new ValidationError(
'Value must be a non-empty String, but %v given.',
value,
);
}
}
Проверка наличия полезной нагрузки выполняется встроенным модулем @e22m4u/js-empty-values (не требует установки). Согласно спецификации модуля, разные типы данных имеют собственные наборы пустых значений. Эти наборы используются для определения наличия полезной нагрузки при проверке обязательных значений или определения необходимости использования значения по умолчанию.
константа | тип | пустые значения |
---|---|---|
DataType.ANY |
'any' |
undefined , null
|
DataType.STRING |
'string' |
undefined , null , ''
|
DataType.NUMBER |
'number' |
undefined , null , 0
|
DataType.BOOLEAN |
'boolean' |
undefined , null
|
DataType.ARRAY |
'array' |
undefined , null , []
|
DataType.OBJECT |
'object' |
undefined , null , {}
|
В первой колонке указаны константы каждого типа, которые могут быть использованы вместо строки названия типа (вторая колонка).
Декораторы предоставляют удобный способ определения схем данных с использованием классов.
Общие декораторы:
-
@dataSchema
- базовый декоратор для определения схемы данных -
@dsProperty
- декоратор для определения свойств схемы
Декораторы конкретных типов данных:
-
@dsAny
- для значений любого типа -
@dsString
- для строковых значений -
@dsNumber
- для числовых значений -
@dsBoolean
- для логических значений -
@dsArray
- для массивов -
@dsObject
- для объектов
Декоратор @dsObject
определяет класс как схему объекта. Он позволяет
генерировать объект схемы через утилиту getDataSchemaFromClass
.
import {dsObject} from '@e22m4u/ts-data-schema';
import {getDataSchemaFromClass} from '@e22m4u/ts-data-schema';
@dsObject()
class PostSchema {
// ...
}
const postSchema = getDataSchemaFromClass(PostSchema);
console.log(postSchema);
// {
// "type": "object",
// "properties": { ... }
// }
Проверка простых значений.
import {DataType} from '@e22m4u/ts-data-schema';
import {DataValidator} from '@e22m4u/ts-data-schema';
const validator = new DataValidator();
// определение схемы
const schema = {
type: DataType.STRING,
// дополнительные опции
};
// валидация значения согласно схеме
validator.validate('John', schema); // вернет undefined
validator.validate(10, schema); // ошибка ValidationError
Пользовательская функция-валидатор.
import {DataType} from '@e22m4u/ts-data-schema';
import {DataValidator} from '@e22m4u/ts-data-schema';
import {ValidationError} from '@e22m4u/ts-data-schema';
const validator = new DataValidator();
// определение функции-валидатора
function nonEmptyString(value) {
if (!value || typeof value !== 'string')
throw new ValidationError('Non-empty string required.');
}
// определение схемы
const schema = {
type: DataType.STRING,
validate: nonEmptyString, // установка вашего валидатора
// validate: [myValidator1, myValidator2, ...],
};
// проверка значений
validator.validate('John', schema); // вернет undefined
validator.validate('', schema); // ошибка ValidationError
Конвертация значений согласно схеме.
import {DataType} from '@e22m4u/ts-data-schema';
import {DataTypeCaster} from '@e22m4u/ts-data-schema';
const typeCaster = new DataTypeCaster();
// определение схемы
const schema = {
type: DataType.NUMBER,
// дополнительные опции
};
// приведение типа согласно схеме,
// или выброс ошибки TypeCastError
typeCaster.cast('10', schema); // вернет 10 как number
typeCaster.cast('foo', schema); // ошибка TypeCastError
// приведение типа согласно схеме,
// или возврат значения без изменений
typeCaster.cast('10', schema, {noTypeCastError: true}); // вернет 10
typeCaster.cast('foo', schema, {noTypeCastError: true}); // вернет "foo"
Получить значение по умолчанию согласно указанной схеме, если входящее значение является пустым. В противном случае возвращается оригинал без изменений.
import {DataType} from '@e22m4u/ts-data-schema';
import {DefaultValuesApplier} from './default-values-applier';
const defaultsApplier = new DefaultValuesApplier();
// определение схемы
// числового значения
const schema = {
type: DataType.NUMBER,
default: 10, // <- по умолчанию
};
// метод `applyDefaultValuesIfNeeded` возвращает
// значение по умолчанию, если входящее значение
// является пустым
const res1 = defaultsApplier.applyDefaultValuesIfNeeded(5, schema);
const res2 = defaultsApplier.applyDefaultValuesIfNeeded(0, schema);
const res3 = defaultsApplier.applyDefaultValuesIfNeeded(undefined, schema);
console.log(res1); // 5 (без изменений)
console.log(res2); // 10 (по умолчанию вместо 0)
console.log(res3); // 10 (по умолчанию вместо undefined)
Заполнение свойств объекта значениями по умолчанию согласно указанной схеме (если свойство имеет пустое значение).
import {DataType} from '@e22m4u/ts-data-schema';
import {DefaultValuesApplier} from './default-values-applier';
const defaultsApplier = new DefaultValuesApplier();
// определене схемы объекта и значений
// по умолчанию для каждого свойства
const schema = {
type: DataType.OBJECT,
properties: {
foo: {
type: DataType.STRING,
default: 'myDefaultValue',
},
bar: {
type: DataType.NUMBER,
default: 10,
},
},
};
// метод `applyDefaultValuesIfNeeded` по необходимости
// устанавливает стандартные значения для каждого свойства,
// и возвращает новый объект (не затрагивая оригинал)
const res = defaultsApplier.applyDefaultValuesIfNeeded(
{foo: null, baz: 'qux'},
schema,
);
console.log(res);
// {
// foo: 'myDefaultValue', <- значение по умолчанию вместо null
// bar: 10, <- значение по умолчанию (не определено)
// baz: 'qux' <- осталось без изменений (новое свойство)
// }
Использование декораторов для построения схемы объекта.
import {dsNumber} from '@e22m4u/ts-data-schema';
import {dsObject} from '@e22m4u/ts-data-schema';
import {dsString} from '@e22m4u/ts-data-schema';
import {ClassToPlain} from '@e22m4u/ts-data-schema';
import {getDataSchemaFromClass} from '@e22m4u/ts-data-schema';
@dsObject()
class AuthorSchema {
@dsNumber({required: true})
id!: number;
@dsString({validate: nonEmptyString})
name?: string;
}
type Author = ClassToPlain<AuthorSchema>;
// {
// id: string,
// name?: string | undefined,
// }
const authorSchema = getDataSchemaFromClass(AuthorSchema);
console.log(authorSchema);
// {
// type: "object",
// properties: {
// id: {
// type: "number",
// required: true,
// },
// name: {
// type: "string",
// validate() {...}
// },
// },
// }
Построение схемы вложенных объектов с помощью декораторов.
import {dsNumber} from '@e22m4u/ts-data-schema';
import {dsObject} from '@e22m4u/ts-data-schema';
import {dsString} from '@e22m4u/ts-data-schema';
import {ClassToPlain} from '@e22m4u/ts-data-schema';
import {getDataSchemaFromClass} from '@e22m4u/ts-data-schema';
@dsObject()
class PostSchema {
@dsNumber({required: true})
id!: number;
@dsString({validate: nonEmptyString})
title?: string;
@dsObject(() => AuthorSchema, {required: true})
author!: Author;
// AuthorSchema и Author
// определены в примере выше
}
type Post = ClassToPlain<PostSchema>;
// {
// id: string,
// title?: string | undefined,
// author: {
// id: string,
// name?: string | undefined,
// }
// }
const postSchema = getDataSchemaFromClass(PostSchema);
console.log(postSchema);
// {
// type: "object"
// properties: {
// id: {
// type: "number",
// required: true,
// },
// title: {
// type: "string",
// validate() {...},
// },
// author: {
// type: "object",
// required: true,
// properties: {
// id: {
// type: "number",
// required: true,
// },
// name: {
// type: "string",
// validate() {...},
// },
// },
// }
// }
// }
Установка переменной DEBUG
включает вывод логов.
DEBUG=tsDataSchema* npm run test
npm run test
MIT