typebad 1.0.0-alpha.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,54 @@
1
+ export type InputType<T> = T extends Type<infer U> ? U : never;
2
+ type InputSchema<T extends TypeSchema> = Simplify<{
3
+ [K in keyof T as undefined extends InputType<T[K]> ? never : unknown extends InputType<T[K]> ? never : K]: InputType<T[K]>;
4
+ } & {
5
+ [K in keyof T as undefined extends InputType<T[K]> ? unknown extends InputType<T[K]> ? never : K : never]?: InputType<T[K]>;
6
+ }>;
7
+ type Simplify<T> = {
8
+ [K in keyof T]: T[K];
9
+ } & {};
10
+ export type TypeSchema = Record<string, Type<any>>;
11
+ type IsTuple<T> = T extends readonly any[] ? number extends T["length"] ? false : true : false;
12
+ type OutputValue<T> = T extends undefined ? OutputValue<Exclude<T, undefined>> : T extends Date ? Date : IsTuple<T> extends true ? {
13
+ [K in keyof T]: OutputValue<T[K]>;
14
+ } : T extends (infer U)[] ? OutputValue<U>[] : T extends Record<string, any> ? {
15
+ [K in keyof T]-?: OutputValue<T[K]>;
16
+ } : T;
17
+ type ExtendedValue<T> = T extends Date ? Date : T extends any[] ? any[] : T extends Record<string, any> ? Record<string, any> : T;
18
+ export type OutputType<T extends Type<any>> = OutputValue<InputType<T>>;
19
+ export interface ParseOptions {
20
+ allowUnknownProperties: boolean;
21
+ }
22
+ export declare class Type<T> {
23
+ private matcher;
24
+ private parser;
25
+ private onParseCallback;
26
+ private allowMissingProperty;
27
+ onParse(callback: (value: OutputValue<T>) => void): Type<T>;
28
+ private parse;
29
+ private isFinalType;
30
+ extend(matcher: (value: ExtendedValue<T>) => boolean): Type<T>;
31
+ nullable(): Type<T | null>;
32
+ optional(): Type<T | null | undefined>;
33
+ default(defaultValue: T | (() => T)): Type<T | undefined>;
34
+ static match<T>(matcher: (value: any) => boolean): Type<T>;
35
+ static enum<const V extends readonly (string | number)[]>(values: V): Type<V[number]>;
36
+ static array<T extends Type<any>>(elementType: T): Type<InputType<T>[]>;
37
+ static object<S extends TypeSchema>(schema: S): Type<InputSchema<S>>;
38
+ static union<U extends Type<any>[]>(types: U): Type<InputType<U[number]>>;
39
+ static tuple<const U extends Type<any>[]>(types: U): Type<{
40
+ [K in keyof U]: InputType<U[K]>;
41
+ }>;
42
+ static lazy<T extends Type<any>>(callback: () => T): T;
43
+ static parse<T extends Type<any>>(type: T, value: InputType<T>, options?: Partial<ParseOptions>): OutputType<T>;
44
+ private static getValueError;
45
+ private static getUnknownPropertyError;
46
+ private static getMissingPropertyError;
47
+ static readonly String: Type<string>;
48
+ static readonly Boolean: Type<boolean>;
49
+ static readonly Number: Type<number>;
50
+ static readonly Integer: Type<number>;
51
+ static readonly Float: Type<number>;
52
+ static readonly Date: Type<Date>;
53
+ }
54
+ export {};
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Type = void 0;
4
+ class Type {
5
+ matcher = () => true;
6
+ parser = (value) => value;
7
+ onParseCallback = null;
8
+ allowMissingProperty = false;
9
+ onParse(callback) {
10
+ this.onParseCallback = callback;
11
+ return this;
12
+ }
13
+ parse(value, propertyName, options) {
14
+ const isMatching = this.matcher(value);
15
+ if (!isMatching) {
16
+ throw new Error(Type.getValueError(propertyName));
17
+ }
18
+ const parsedValue = this.parser(value, propertyName, options) ?? null;
19
+ this.onParseCallback?.(parsedValue);
20
+ return parsedValue;
21
+ }
22
+ isFinalType() {
23
+ return this.onParseCallback !== null;
24
+ }
25
+ extend(matcher) {
26
+ if (this.isFinalType()) {
27
+ throw new Error("Cannot extend a Type that has onParse callback.");
28
+ }
29
+ const type = new Type();
30
+ type.matcher = (value) => {
31
+ return this.matcher(value) && matcher(value);
32
+ };
33
+ type.parser = this.parser;
34
+ type.allowMissingProperty = this.allowMissingProperty;
35
+ return type;
36
+ }
37
+ nullable() {
38
+ if (this.isFinalType()) {
39
+ throw new Error("Cannot extend a Type that has onParse callback.");
40
+ }
41
+ const type = new Type();
42
+ type.matcher = (value) => {
43
+ if (value === null) {
44
+ return true;
45
+ }
46
+ return this.matcher(value);
47
+ };
48
+ type.parser = (value, propertyName, options) => {
49
+ if (value === null) {
50
+ return null;
51
+ }
52
+ return this.parser(value, propertyName, options);
53
+ };
54
+ type.allowMissingProperty = this.allowMissingProperty;
55
+ return type;
56
+ }
57
+ optional() {
58
+ if (this.isFinalType()) {
59
+ throw new Error("Cannot extend a Type that has onParse callback.");
60
+ }
61
+ const type = new Type();
62
+ type.matcher = (value) => {
63
+ if (value === null || value === undefined) {
64
+ return true;
65
+ }
66
+ return this.matcher(value);
67
+ };
68
+ type.parser = (value, propertyName, options) => {
69
+ if (value === null || value === undefined) {
70
+ return null;
71
+ }
72
+ return this.parser(value, propertyName, options);
73
+ };
74
+ type.allowMissingProperty = true;
75
+ type.onParseCallback = this.onParseCallback;
76
+ return type;
77
+ }
78
+ default(defaultValue) {
79
+ if (this.isFinalType()) {
80
+ throw new Error("Cannot extend a Type that has onParse callback.");
81
+ }
82
+ const type = new Type();
83
+ type.matcher = (value) => {
84
+ if (value === undefined) {
85
+ return true;
86
+ }
87
+ return this.matcher(value);
88
+ };
89
+ type.parser = (value, propertyName, options) => {
90
+ if (value === undefined) {
91
+ const tmpValue = typeof defaultValue === "function"
92
+ ? defaultValue()
93
+ : defaultValue;
94
+ return this.parser(tmpValue, propertyName, options);
95
+ }
96
+ return this.parser(value, propertyName, options);
97
+ };
98
+ type.allowMissingProperty = true;
99
+ return type;
100
+ }
101
+ static match(matcher) {
102
+ const type = new Type();
103
+ type.matcher = matcher;
104
+ return type;
105
+ }
106
+ static enum(values) {
107
+ const type = new Type();
108
+ type.matcher = (value) => {
109
+ return values.includes(value);
110
+ };
111
+ return type;
112
+ }
113
+ static array(elementType) {
114
+ const type = new Type();
115
+ type.matcher = (value) => {
116
+ return Array.isArray(value);
117
+ };
118
+ type.parser = (value, propertyName, options) => {
119
+ return value.map((tmpValue, i) => {
120
+ const tmpPropertyName = `${propertyName ?? ""}[${i}]`;
121
+ return elementType.parse(tmpValue, tmpPropertyName, options);
122
+ });
123
+ };
124
+ return type;
125
+ }
126
+ static object(schema) {
127
+ const type = new Type();
128
+ type.matcher = (value) => {
129
+ return (value !== null && !Array.isArray(value) && typeof value === "object");
130
+ };
131
+ type.parser = (value, propertyName, options) => {
132
+ const objectValue = {};
133
+ if (!options.allowUnknownProperties) {
134
+ for (const key in value) {
135
+ const tmpPropertyName = propertyName !== null ? `${propertyName}.${key}` : key;
136
+ if (!(key in schema)) {
137
+ throw new Error(this.getUnknownPropertyError(tmpPropertyName));
138
+ }
139
+ }
140
+ }
141
+ for (const key in schema) {
142
+ const tmpPropertyName = propertyName !== null ? `${propertyName}.${key}` : key;
143
+ const tmpType = schema[key];
144
+ const tmpValue = value[key];
145
+ if (!(key in value) && !tmpType.allowMissingProperty) {
146
+ throw new Error(this.getMissingPropertyError(tmpPropertyName));
147
+ }
148
+ objectValue[key] = tmpType.parse(tmpValue, tmpPropertyName, options);
149
+ }
150
+ return objectValue;
151
+ };
152
+ return type;
153
+ }
154
+ static union(types) {
155
+ const type = new Type();
156
+ type.matcher = (value) => {
157
+ for (const tmpType of types) {
158
+ if (tmpType.matcher(value)) {
159
+ return true;
160
+ }
161
+ }
162
+ return false;
163
+ };
164
+ type.parser = (value, propertyName, options) => {
165
+ for (const tmpType of types) {
166
+ try {
167
+ return tmpType.parse(value, propertyName, options);
168
+ }
169
+ catch { }
170
+ }
171
+ throw new Error(Type.getValueError(propertyName));
172
+ };
173
+ return type;
174
+ }
175
+ static tuple(types) {
176
+ const type = new Type();
177
+ type.matcher = (value) => {
178
+ return Array.isArray(value) && value.length === types.length;
179
+ };
180
+ type.parser = (value, propertyName, options) => {
181
+ return types.map((tmpType, i) => {
182
+ const tmpValue = value[i];
183
+ const tmpPropertyName = `${propertyName ?? ""}[${i}]`;
184
+ return tmpType.parse(tmpValue, tmpPropertyName, options);
185
+ });
186
+ };
187
+ return type;
188
+ }
189
+ static lazy(callback) {
190
+ const type = new Type();
191
+ type.matcher = (value) => {
192
+ const tmpType = callback();
193
+ return tmpType.matcher(value);
194
+ };
195
+ type.parser = (value, propertyName, options) => {
196
+ const tmpType = callback();
197
+ return tmpType.parser(value, propertyName, options);
198
+ };
199
+ return type;
200
+ }
201
+ static parse(type, value, options = {}) {
202
+ const tmpOptions = {
203
+ allowUnknownProperties: options.allowUnknownProperties ?? false,
204
+ };
205
+ return type.parse(value, null, tmpOptions);
206
+ }
207
+ static getValueError(propertyName) {
208
+ return ("Invalid value" +
209
+ (propertyName !== null ? ` for "${propertyName}".` : "."));
210
+ }
211
+ static getUnknownPropertyError(propertyName) {
212
+ return `Unknown property "${propertyName}".`;
213
+ }
214
+ static getMissingPropertyError(propertyName) {
215
+ return `Missing property "${propertyName}".`;
216
+ }
217
+ static String = Type.match((value) => typeof value === "string");
218
+ static Boolean = Type.match((value) => typeof value === "boolean");
219
+ static Number = Type.match((value) => typeof value === "number");
220
+ static Integer = Type.match((value) => typeof value === "number" && Number.isInteger(value));
221
+ static Float = Type.match((value) => typeof value === "number" && !Number.isInteger(value));
222
+ static Date = Type.match((value) => value instanceof Date);
223
+ }
224
+ exports.Type = Type;
@@ -0,0 +1,54 @@
1
+ export type InputType<T> = T extends Type<infer U> ? U : never;
2
+ type InputSchema<T extends TypeSchema> = Simplify<{
3
+ [K in keyof T as undefined extends InputType<T[K]> ? never : unknown extends InputType<T[K]> ? never : K]: InputType<T[K]>;
4
+ } & {
5
+ [K in keyof T as undefined extends InputType<T[K]> ? unknown extends InputType<T[K]> ? never : K : never]?: InputType<T[K]>;
6
+ }>;
7
+ type Simplify<T> = {
8
+ [K in keyof T]: T[K];
9
+ } & {};
10
+ export type TypeSchema = Record<string, Type<any>>;
11
+ type IsTuple<T> = T extends readonly any[] ? number extends T["length"] ? false : true : false;
12
+ type OutputValue<T> = T extends undefined ? OutputValue<Exclude<T, undefined>> : T extends Date ? Date : IsTuple<T> extends true ? {
13
+ [K in keyof T]: OutputValue<T[K]>;
14
+ } : T extends (infer U)[] ? OutputValue<U>[] : T extends Record<string, any> ? {
15
+ [K in keyof T]-?: OutputValue<T[K]>;
16
+ } : T;
17
+ type ExtendedValue<T> = T extends Date ? Date : T extends any[] ? any[] : T extends Record<string, any> ? Record<string, any> : T;
18
+ export type OutputType<T extends Type<any>> = OutputValue<InputType<T>>;
19
+ export interface ParseOptions {
20
+ allowUnknownProperties: boolean;
21
+ }
22
+ export declare class Type<T> {
23
+ private matcher;
24
+ private parser;
25
+ private onParseCallback;
26
+ private allowMissingProperty;
27
+ onParse(callback: (value: OutputValue<T>) => void): Type<T>;
28
+ private parse;
29
+ private isFinalType;
30
+ extend(matcher: (value: ExtendedValue<T>) => boolean): Type<T>;
31
+ nullable(): Type<T | null>;
32
+ optional(): Type<T | null | undefined>;
33
+ default(defaultValue: T | (() => T)): Type<T | undefined>;
34
+ static match<T>(matcher: (value: any) => boolean): Type<T>;
35
+ static enum<const V extends readonly (string | number)[]>(values: V): Type<V[number]>;
36
+ static array<T extends Type<any>>(elementType: T): Type<InputType<T>[]>;
37
+ static object<S extends TypeSchema>(schema: S): Type<InputSchema<S>>;
38
+ static union<U extends Type<any>[]>(types: U): Type<InputType<U[number]>>;
39
+ static tuple<const U extends Type<any>[]>(types: U): Type<{
40
+ [K in keyof U]: InputType<U[K]>;
41
+ }>;
42
+ static lazy<T extends Type<any>>(callback: () => T): T;
43
+ static parse<T extends Type<any>>(type: T, value: InputType<T>, options?: Partial<ParseOptions>): OutputType<T>;
44
+ private static getValueError;
45
+ private static getUnknownPropertyError;
46
+ private static getMissingPropertyError;
47
+ static readonly String: Type<string>;
48
+ static readonly Boolean: Type<boolean>;
49
+ static readonly Number: Type<number>;
50
+ static readonly Integer: Type<number>;
51
+ static readonly Float: Type<number>;
52
+ static readonly Date: Type<Date>;
53
+ }
54
+ export {};
@@ -0,0 +1,220 @@
1
+ export class Type {
2
+ matcher = () => true;
3
+ parser = (value) => value;
4
+ onParseCallback = null;
5
+ allowMissingProperty = false;
6
+ onParse(callback) {
7
+ this.onParseCallback = callback;
8
+ return this;
9
+ }
10
+ parse(value, propertyName, options) {
11
+ const isMatching = this.matcher(value);
12
+ if (!isMatching) {
13
+ throw new Error(Type.getValueError(propertyName));
14
+ }
15
+ const parsedValue = this.parser(value, propertyName, options) ?? null;
16
+ this.onParseCallback?.(parsedValue);
17
+ return parsedValue;
18
+ }
19
+ isFinalType() {
20
+ return this.onParseCallback !== null;
21
+ }
22
+ extend(matcher) {
23
+ if (this.isFinalType()) {
24
+ throw new Error("Cannot extend a Type that has onParse callback.");
25
+ }
26
+ const type = new Type();
27
+ type.matcher = (value) => {
28
+ return this.matcher(value) && matcher(value);
29
+ };
30
+ type.parser = this.parser;
31
+ type.allowMissingProperty = this.allowMissingProperty;
32
+ return type;
33
+ }
34
+ nullable() {
35
+ if (this.isFinalType()) {
36
+ throw new Error("Cannot extend a Type that has onParse callback.");
37
+ }
38
+ const type = new Type();
39
+ type.matcher = (value) => {
40
+ if (value === null) {
41
+ return true;
42
+ }
43
+ return this.matcher(value);
44
+ };
45
+ type.parser = (value, propertyName, options) => {
46
+ if (value === null) {
47
+ return null;
48
+ }
49
+ return this.parser(value, propertyName, options);
50
+ };
51
+ type.allowMissingProperty = this.allowMissingProperty;
52
+ return type;
53
+ }
54
+ optional() {
55
+ if (this.isFinalType()) {
56
+ throw new Error("Cannot extend a Type that has onParse callback.");
57
+ }
58
+ const type = new Type();
59
+ type.matcher = (value) => {
60
+ if (value === null || value === undefined) {
61
+ return true;
62
+ }
63
+ return this.matcher(value);
64
+ };
65
+ type.parser = (value, propertyName, options) => {
66
+ if (value === null || value === undefined) {
67
+ return null;
68
+ }
69
+ return this.parser(value, propertyName, options);
70
+ };
71
+ type.allowMissingProperty = true;
72
+ type.onParseCallback = this.onParseCallback;
73
+ return type;
74
+ }
75
+ default(defaultValue) {
76
+ if (this.isFinalType()) {
77
+ throw new Error("Cannot extend a Type that has onParse callback.");
78
+ }
79
+ const type = new Type();
80
+ type.matcher = (value) => {
81
+ if (value === undefined) {
82
+ return true;
83
+ }
84
+ return this.matcher(value);
85
+ };
86
+ type.parser = (value, propertyName, options) => {
87
+ if (value === undefined) {
88
+ const tmpValue = typeof defaultValue === "function"
89
+ ? defaultValue()
90
+ : defaultValue;
91
+ return this.parser(tmpValue, propertyName, options);
92
+ }
93
+ return this.parser(value, propertyName, options);
94
+ };
95
+ type.allowMissingProperty = true;
96
+ return type;
97
+ }
98
+ static match(matcher) {
99
+ const type = new Type();
100
+ type.matcher = matcher;
101
+ return type;
102
+ }
103
+ static enum(values) {
104
+ const type = new Type();
105
+ type.matcher = (value) => {
106
+ return values.includes(value);
107
+ };
108
+ return type;
109
+ }
110
+ static array(elementType) {
111
+ const type = new Type();
112
+ type.matcher = (value) => {
113
+ return Array.isArray(value);
114
+ };
115
+ type.parser = (value, propertyName, options) => {
116
+ return value.map((tmpValue, i) => {
117
+ const tmpPropertyName = `${propertyName ?? ""}[${i}]`;
118
+ return elementType.parse(tmpValue, tmpPropertyName, options);
119
+ });
120
+ };
121
+ return type;
122
+ }
123
+ static object(schema) {
124
+ const type = new Type();
125
+ type.matcher = (value) => {
126
+ return (value !== null && !Array.isArray(value) && typeof value === "object");
127
+ };
128
+ type.parser = (value, propertyName, options) => {
129
+ const objectValue = {};
130
+ if (!options.allowUnknownProperties) {
131
+ for (const key in value) {
132
+ const tmpPropertyName = propertyName !== null ? `${propertyName}.${key}` : key;
133
+ if (!(key in schema)) {
134
+ throw new Error(this.getUnknownPropertyError(tmpPropertyName));
135
+ }
136
+ }
137
+ }
138
+ for (const key in schema) {
139
+ const tmpPropertyName = propertyName !== null ? `${propertyName}.${key}` : key;
140
+ const tmpType = schema[key];
141
+ const tmpValue = value[key];
142
+ if (!(key in value) && !tmpType.allowMissingProperty) {
143
+ throw new Error(this.getMissingPropertyError(tmpPropertyName));
144
+ }
145
+ objectValue[key] = tmpType.parse(tmpValue, tmpPropertyName, options);
146
+ }
147
+ return objectValue;
148
+ };
149
+ return type;
150
+ }
151
+ static union(types) {
152
+ const type = new Type();
153
+ type.matcher = (value) => {
154
+ for (const tmpType of types) {
155
+ if (tmpType.matcher(value)) {
156
+ return true;
157
+ }
158
+ }
159
+ return false;
160
+ };
161
+ type.parser = (value, propertyName, options) => {
162
+ for (const tmpType of types) {
163
+ try {
164
+ return tmpType.parse(value, propertyName, options);
165
+ }
166
+ catch { }
167
+ }
168
+ throw new Error(Type.getValueError(propertyName));
169
+ };
170
+ return type;
171
+ }
172
+ static tuple(types) {
173
+ const type = new Type();
174
+ type.matcher = (value) => {
175
+ return Array.isArray(value) && value.length === types.length;
176
+ };
177
+ type.parser = (value, propertyName, options) => {
178
+ return types.map((tmpType, i) => {
179
+ const tmpValue = value[i];
180
+ const tmpPropertyName = `${propertyName ?? ""}[${i}]`;
181
+ return tmpType.parse(tmpValue, tmpPropertyName, options);
182
+ });
183
+ };
184
+ return type;
185
+ }
186
+ static lazy(callback) {
187
+ const type = new Type();
188
+ type.matcher = (value) => {
189
+ const tmpType = callback();
190
+ return tmpType.matcher(value);
191
+ };
192
+ type.parser = (value, propertyName, options) => {
193
+ const tmpType = callback();
194
+ return tmpType.parser(value, propertyName, options);
195
+ };
196
+ return type;
197
+ }
198
+ static parse(type, value, options = {}) {
199
+ const tmpOptions = {
200
+ allowUnknownProperties: options.allowUnknownProperties ?? false,
201
+ };
202
+ return type.parse(value, null, tmpOptions);
203
+ }
204
+ static getValueError(propertyName) {
205
+ return ("Invalid value" +
206
+ (propertyName !== null ? ` for "${propertyName}".` : "."));
207
+ }
208
+ static getUnknownPropertyError(propertyName) {
209
+ return `Unknown property "${propertyName}".`;
210
+ }
211
+ static getMissingPropertyError(propertyName) {
212
+ return `Missing property "${propertyName}".`;
213
+ }
214
+ static String = Type.match((value) => typeof value === "string");
215
+ static Boolean = Type.match((value) => typeof value === "boolean");
216
+ static Number = Type.match((value) => typeof value === "number");
217
+ static Integer = Type.match((value) => typeof value === "number" && Number.isInteger(value));
218
+ static Float = Type.match((value) => typeof value === "number" && !Number.isInteger(value));
219
+ static Date = Type.match((value) => value instanceof Date);
220
+ }
@@ -0,0 +1 @@
1
+ export * from "./Type";
@@ -0,0 +1 @@
1
+ export * from "./Type";
package/package.json CHANGED
@@ -1,16 +1,31 @@
1
1
  {
2
2
  "name": "typebad",
3
- "version": "1.0.0-alpha.2",
3
+ "version": "1.0.0",
4
4
  "description": "Efficient type validator for complex structures.",
5
- "main": "dist/index.js",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/iconshot/typebad.git"
8
+ },
9
+ "main": "./dist/cjs/index.js",
10
+ "module": "./dist/esm/index.js",
11
+ "types": "./dist/esm/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "require": "./dist/cjs/index.js",
15
+ "import": "./dist/esm/index.js"
16
+ }
17
+ },
6
18
  "scripts": {
7
19
  "clean": "rm -rf dist",
8
- "build": "npm run clean && tsc",
20
+ "build-cjs": "tsc --project tsconfig.cjs.json",
21
+ "build-esm": "tsc --project tsconfig.esm.json",
22
+ "build": "npm run clean && npm run build-cjs && npm run build-esm",
9
23
  "prepare": "npm run build"
10
24
  },
11
25
  "keywords": [
12
26
  "type",
13
27
  "validation",
28
+ "normalization",
14
29
  "json"
15
30
  ],
16
31
  "license": "MIT",
package/dist/Type.d.ts DELETED
@@ -1,53 +0,0 @@
1
- export type InferType<T> = T extends Type<infer U> ? U : never;
2
- export type InferSchema<T extends TypeSchema> = Simplify<{
3
- [K in keyof T as undefined extends InferType<T[K]> ? never : unknown extends InferType<T[K]> ? never : K]: InferType<T[K]>;
4
- } & {
5
- [K in keyof T as undefined extends InferType<T[K]> ? unknown extends InferType<T[K]> ? never : K : never]?: InferType<T[K]>;
6
- }>;
7
- type Simplify<T> = {
8
- [K in keyof T]: T[K];
9
- } & {};
10
- export type TypeSchema = Record<string, Type<any>>;
11
- export type NormalizedValue<T> = T extends undefined ? Exclude<T, undefined> : T extends (infer U)[] ? NormalizedValue<U>[] : T extends Record<string, any> ? {
12
- [K in keyof T]-?: NormalizedValue<T[K]>;
13
- } : T;
14
- export type NormalizedType<T extends Type<any>> = NormalizedValue<InferType<T>>;
15
- export interface ValidateOptions {
16
- allowUnknownProperties: boolean;
17
- }
18
- export interface NormalizeOptions {
19
- allowUnknownProperties: boolean;
20
- }
21
- export declare class Type<T> {
22
- private matcher;
23
- private validator;
24
- private normalizer;
25
- private onValidateCallback;
26
- private onNormalizeCallback;
27
- private allowMissingProperty;
28
- onValidate(callback: (value: T) => void): Type<T>;
29
- onNormalize(callback: (value: NormalizedValue<T>) => void): Type<T>;
30
- private validate;
31
- private normalize;
32
- extend(matcher: (value: T) => boolean): Type<T>;
33
- default(defaultValue: T | (() => T)): Type<T | undefined>;
34
- optional(): Type<T | null | undefined>;
35
- static match<T>(matcher: (value: T) => boolean): Type<T>;
36
- static enum<const V extends readonly (string | number)[]>(values: V): Type<V[number]>;
37
- static array<T extends Type<any>>(type: T): Type<InferType<T>[]>;
38
- static schema<S extends TypeSchema>(schema: S): Type<InferSchema<S>>;
39
- static lazy<T extends Type<any>>(callback: () => T): T;
40
- static validate(type: Type<any>, value: any, options?: Partial<ValidateOptions>): void;
41
- static normalize<T extends Type<any>>(type: T, value: any, options?: Partial<NormalizeOptions>): NormalizedType<T>;
42
- private static getValueError;
43
- private static getUnknownPropertyError;
44
- private static getMissingPropertyError;
45
- static readonly Null: Type<null>;
46
- static readonly String: Type<string>;
47
- static readonly Boolean: Type<boolean>;
48
- static readonly Number: Type<number>;
49
- static readonly Integer: Type<number>;
50
- static readonly Float: Type<number>;
51
- static readonly Date: Type<Date>;
52
- }
53
- export {};
package/dist/Type.js DELETED
@@ -1,214 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Type = void 0;
4
- class Type {
5
- constructor() {
6
- this.matcher = () => true;
7
- this.validator = () => true;
8
- this.normalizer = (value) => value;
9
- this.onValidateCallback = null;
10
- this.onNormalizeCallback = null;
11
- this.allowMissingProperty = false;
12
- }
13
- onValidate(callback) {
14
- this.onValidateCallback = callback;
15
- return this;
16
- }
17
- onNormalize(callback) {
18
- this.onNormalizeCallback = callback;
19
- return this;
20
- }
21
- validate(value, propertyName, options) {
22
- if (!this.matcher(value) || !this.validator(value, propertyName, options)) {
23
- throw new Error(Type.getValueError(propertyName));
24
- }
25
- this.onValidateCallback?.(value);
26
- }
27
- normalize(value, propertyName, options) {
28
- if (!this.matcher(value)) {
29
- throw new Error(Type.getValueError(propertyName));
30
- }
31
- const normalizedValue = this.normalizer(value, propertyName, options) ?? null;
32
- this.onNormalizeCallback?.(normalizedValue);
33
- return normalizedValue;
34
- }
35
- extend(matcher) {
36
- const type = new Type();
37
- type.matcher = (value) => {
38
- return this.matcher(value) && matcher(value);
39
- };
40
- type.validator = this.validator;
41
- type.normalizer = this.normalizer;
42
- type.allowMissingProperty = this.allowMissingProperty;
43
- return type;
44
- }
45
- default(defaultValue) {
46
- const type = new Type();
47
- type.matcher = (value) => {
48
- return value === undefined || this.matcher(value);
49
- };
50
- type.validator = (value, propertyName, options) => {
51
- if (value === undefined) {
52
- return true;
53
- }
54
- return this.validator(value, propertyName, options);
55
- };
56
- type.normalizer = (value, propertyName, options) => {
57
- if (value === undefined) {
58
- const tmpValue = typeof defaultValue === "function"
59
- ? defaultValue()
60
- : defaultValue;
61
- return this.normalizer(tmpValue, propertyName, options);
62
- }
63
- return this.normalizer(value, propertyName, options);
64
- };
65
- type.allowMissingProperty = true;
66
- return type;
67
- }
68
- optional() {
69
- const type = new Type();
70
- type.matcher = (value) => {
71
- return value === null || value === undefined || this.matcher(value);
72
- };
73
- type.validator = (value, propertyName, options) => {
74
- if (value === null || value === undefined) {
75
- return true;
76
- }
77
- return this.validator(value, propertyName, options);
78
- };
79
- type.normalizer = (value, propertyName, options) => {
80
- if (value === null || value === undefined) {
81
- return null;
82
- }
83
- return this.normalizer(value, propertyName, options);
84
- };
85
- type.allowMissingProperty = true;
86
- return type;
87
- }
88
- static match(matcher) {
89
- const type = new Type();
90
- type.matcher = matcher;
91
- return type;
92
- }
93
- static enum(values) {
94
- const type = new Type();
95
- type.matcher = (value) => {
96
- return values.includes(value);
97
- };
98
- return type;
99
- }
100
- static array(type) {
101
- const arrayType = new Type();
102
- arrayType.matcher = (value) => {
103
- return Array.isArray(value);
104
- };
105
- arrayType.validator = (value, propertyName, options) => {
106
- value.forEach((tmpValue, i) => {
107
- const tmpPropertyName = `${propertyName ?? ""}[${i}]`;
108
- type.validate(tmpValue, tmpPropertyName, options);
109
- });
110
- return true;
111
- };
112
- arrayType.normalizer = (value, propertyName, options) => {
113
- return value.map((tmpValue, i) => {
114
- const tmpPropertyName = `${propertyName ?? ""}[${i}]`;
115
- return type.normalize(tmpValue, tmpPropertyName, options);
116
- });
117
- };
118
- return arrayType;
119
- }
120
- static schema(schema) {
121
- const type = new Type();
122
- type.matcher = (value) => {
123
- return (value !== null && !Array.isArray(value) && typeof value === "object");
124
- };
125
- type.validator = (value, propertyName, options) => {
126
- if (!options.allowUnknownProperties) {
127
- for (const key in value) {
128
- const tmpPropertyName = propertyName !== null ? `${propertyName}.${key}` : key;
129
- if (!(key in schema)) {
130
- throw new Error(this.getUnknownPropertyError(tmpPropertyName));
131
- }
132
- }
133
- }
134
- for (const key in schema) {
135
- const tmpPropertyName = propertyName !== null ? `${propertyName}.${key}` : key;
136
- const tmpType = schema[key];
137
- const tmpValue = value[key];
138
- if (!(key in value) && !tmpType.allowMissingProperty) {
139
- throw new Error(this.getMissingPropertyError(tmpPropertyName));
140
- }
141
- tmpType.validate(tmpValue, tmpPropertyName, options);
142
- }
143
- return true;
144
- };
145
- type.normalizer = (value, propertyName, options) => {
146
- const objectValue = {};
147
- if (!options.allowUnknownProperties) {
148
- for (const key in value) {
149
- const tmpPropertyName = propertyName !== null ? `${propertyName}.${key}` : key;
150
- if (!(key in schema)) {
151
- throw new Error(this.getUnknownPropertyError(tmpPropertyName));
152
- }
153
- }
154
- }
155
- for (const key in schema) {
156
- const tmpPropertyName = propertyName !== null ? `${propertyName}.${key}` : key;
157
- const tmpType = schema[key];
158
- const tmpValue = value[key];
159
- if (!(key in value) && !tmpType.allowMissingProperty) {
160
- throw new Error(this.getMissingPropertyError(tmpPropertyName));
161
- }
162
- objectValue[key] = tmpType.normalize(tmpValue, tmpPropertyName, options);
163
- }
164
- return objectValue;
165
- };
166
- return type;
167
- }
168
- static lazy(callback) {
169
- const type = new Type();
170
- type.matcher = (value) => {
171
- const tmpType = callback();
172
- return tmpType.matcher(value);
173
- };
174
- type.validator = (value, propertyName, options) => {
175
- const tmpType = callback();
176
- return tmpType.validator(value, propertyName, options);
177
- };
178
- type.normalizer = (value, propertyName, options) => {
179
- const tmpType = callback();
180
- return tmpType.normalizer(value, propertyName, options);
181
- };
182
- return type;
183
- }
184
- static validate(type, value, options = {}) {
185
- const tmpOptions = {
186
- allowUnknownProperties: options.allowUnknownProperties ?? false,
187
- };
188
- type.validate(value, null, tmpOptions);
189
- }
190
- static normalize(type, value, options = {}) {
191
- const tmpOptions = {
192
- allowUnknownProperties: options.allowUnknownProperties ?? false,
193
- };
194
- return type.normalize(value, null, tmpOptions);
195
- }
196
- static getValueError(propertyName) {
197
- return ("Invalid value" +
198
- (propertyName !== null ? ` for "${propertyName}".` : "."));
199
- }
200
- static getUnknownPropertyError(propertyName) {
201
- return `Unknown property "${propertyName}".`;
202
- }
203
- static getMissingPropertyError(propertyName) {
204
- return `Missing property "${propertyName}".`;
205
- }
206
- }
207
- exports.Type = Type;
208
- Type.Null = Type.match((value) => value === null);
209
- Type.String = Type.match((value) => typeof value === "string");
210
- Type.Boolean = Type.match((value) => typeof value === "boolean");
211
- Type.Number = Type.match((value) => typeof value === "number");
212
- Type.Integer = Type.match((value) => typeof value === "number" && Number.isInteger(value));
213
- Type.Float = Type.match((value) => typeof value === "number" && !Number.isInteger(value));
214
- Type.Date = Type.match((value) => value instanceof Date && !isNaN(value.getTime()));
File without changes
File without changes