From 61f465fe99f2dc818a5c6b70e8788f2ce8e1b2f9 Mon Sep 17 00:00:00 2001 From: Revone Date: Mon, 28 Aug 2023 18:43:09 +0800 Subject: [PATCH] Added a method for data type validation through type conversion to Zod schemas. --- README.md | 2 +- package-lock.json | 15 ++- package.json | 3 + .../binary-tree/tree-multiset.ts | 18 +-- .../interfaces/tree-multiset.ts | 6 +- src/data-structures/types/tree-multiset.ts | 6 +- src/utils/index.ts | 3 +- src/utils/types/index.ts | 3 +- src/utils/types/utils.ts | 27 ---- src/utils/types/validate-type.ts | 21 +++ src/utils/utils.ts | 120 +++--------------- src/utils/validate-type.ts | 69 ++++++++++ 12 files changed, 145 insertions(+), 148 deletions(-) create mode 100644 src/utils/types/validate-type.ts create mode 100644 src/utils/validate-type.ts diff --git a/README.md b/README.md index 3eedb83..e8df0d6 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ wide range of data structures Tree Multiset -TreeMultiSet +TreeMultiset diff --git a/package-lock.json b/package-lock.json index e08f5a2..07a9d0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,16 @@ { "name": "data-structure-typed", - "version": "1.18.7", + "version": "1.18.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "data-structure-typed", - "version": "1.18.7", + "version": "1.18.8", "license": "MIT", + "dependencies": { + "zod": "^3.22.2" + }, "devDependencies": { "@types/jest": "^29.5.3", "@types/node": "^20.4.9", @@ -5768,6 +5771,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.22.2", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.2.tgz", + "integrity": "sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index e0ad0e5..447fd46 100644 --- a/package.json +++ b/package.json @@ -63,5 +63,8 @@ "ts-jest": "^29.1.1", "typedoc": "^0.24.8", "typescript": "^4.9.5" + }, + "dependencies": { + "zod": "^3.22.2" } } diff --git a/src/data-structures/binary-tree/tree-multiset.ts b/src/data-structures/binary-tree/tree-multiset.ts index e454055..7fceb64 100644 --- a/src/data-structures/binary-tree/tree-multiset.ts +++ b/src/data-structures/binary-tree/tree-multiset.ts @@ -5,11 +5,11 @@ * @copyright Copyright (c) 2022 Tyler Zeng * @license MIT License */ -import type {BinaryTreeNodeId, TreeMultiSetNodeNested, TreeMultiSetOptions} from '../types'; -import {ITreeMultiSet, ITreeMultiSetNode} from '../interfaces'; +import type {BinaryTreeNodeId, TreeMultisetNodeNested, TreeMultisetOptions} from '../types'; +import {ITreeMultiset, ITreeMultisetNode} from '../interfaces'; import {AVLTree, AVLTreeNode} from './avl-tree'; -export class TreeMultiSetNode = TreeMultiSetNodeNested> extends AVLTreeNode implements ITreeMultiSetNode { +export class TreeMultisetNode = TreeMultisetNodeNested> extends AVLTreeNode implements ITreeMultisetNode { /** * The function creates a new node in a binary tree with an optional value and count. * @param {BinaryTreeNodeId} id - The `id` parameter is the identifier for the binary tree node. It is used to uniquely @@ -18,18 +18,18 @@ export class TreeMultiSetNode = TreeMultiSetNode> extends AVLTree implements ITreeMultiSet { - constructor(options?: TreeMultiSetOptions) { +export class TreeMultiset = TreeMultisetNode> extends AVLTree implements ITreeMultiset { + constructor(options?: TreeMultisetOptions) { super({...options, isMergeDuplicatedVal: true}); } @@ -43,6 +43,6 @@ export class TreeMultiSet = TreeMultiSet * @returns A new instance of the BSTNode class with the specified id, value, and count (if provided). */ override createNode(id: BinaryTreeNodeId, val?: N['val'], count?: number): N { - return new TreeMultiSetNode(id, val, count) as N; + return new TreeMultisetNode(id, val, count) as N; } } diff --git a/src/data-structures/interfaces/tree-multiset.ts b/src/data-structures/interfaces/tree-multiset.ts index 72d9c3e..32fe4dd 100644 --- a/src/data-structures/interfaces/tree-multiset.ts +++ b/src/data-structures/interfaces/tree-multiset.ts @@ -1,12 +1,12 @@ -import {TreeMultiSetNode} from '../binary-tree'; +import {TreeMultisetNode} from '../binary-tree'; import {IBSTNode} from './bst'; import {IAVLTree} from './avl-tree'; -export interface ITreeMultiSetNode> extends IBSTNode { +export interface ITreeMultisetNode> extends IBSTNode { } -export interface ITreeMultiSet> extends IAVLTree { +export interface ITreeMultiset> extends IAVLTree { } \ No newline at end of file diff --git a/src/data-structures/types/tree-multiset.ts b/src/data-structures/types/tree-multiset.ts index b65f64f..89e994d 100644 --- a/src/data-structures/types/tree-multiset.ts +++ b/src/data-structures/types/tree-multiset.ts @@ -1,8 +1,8 @@ -import {TreeMultiSetNode} from '../binary-tree'; +import {TreeMultisetNode} from '../binary-tree'; import {AVLTreeOptions} from './avl-tree'; -export type TreeMultiSetNodeNested = TreeMultiSetNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> +export type TreeMultisetNodeNested = TreeMultisetNode>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> -export type TreeMultiSetOptions = Omit & { +export type TreeMultisetOptions = Omit & { isMergeDuplicatedVal: true, } diff --git a/src/utils/index.ts b/src/utils/index.ts index be7aaf8..7e60a97 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,2 +1,3 @@ export * from './utils'; -export * from './types'; \ No newline at end of file +export * from './types'; +export * from './validate-type'; \ No newline at end of file diff --git a/src/utils/types/index.ts b/src/utils/types/index.ts index 038522d..4ecd603 100644 --- a/src/utils/types/index.ts +++ b/src/utils/types/index.ts @@ -1 +1,2 @@ -export * from './utils'; \ No newline at end of file +export * from './utils'; +export * from './validate-type'; \ No newline at end of file diff --git a/src/utils/types/utils.ts b/src/utils/types/utils.ts index 78403d6..dc1f129 100644 --- a/src/utils/types/utils.ts +++ b/src/utils/types/utils.ts @@ -4,30 +4,3 @@ export type TrlFn = (...args: any[]) => any; export type TrlAsyncFn = (...args: any[]) => any; export type SpecifyOptional = Omit & Partial>; - -export type KeyValueObject = { [key: string]: any }; - -export type KeyValueObjectWithId = { [key: string]: any, id: string | number | symbol }; - -export type NonNumberNonObjectButDefined = string | boolean | symbol | null; - -// export type ObjectWithoutId = Omit; -export type ObjectWithoutId = Omit; - -// export type ObjectWithNonNumberId = object & { -// id: string | boolean | symbol | null | object | undefined; -// } -export type ObjectWithNonNumberId = { - [key: string]: any, - id: string | boolean | symbol | null | object | undefined; -} - -// export type ObjectWithNumberId = object & { -// id: number; -// } -export type ObjectWithNumberId = { - [key: string]: any, - id: number; -} - -export type DummyAny = string | number | boolean | null | undefined | object | symbol | void | Function | never; \ No newline at end of file diff --git a/src/utils/types/validate-type.ts b/src/utils/types/validate-type.ts new file mode 100644 index 0000000..d448b9e --- /dev/null +++ b/src/utils/types/validate-type.ts @@ -0,0 +1,21 @@ +export type KeyValueObject = { [key: string]: any }; + +export type KeyValueObjectWithId = { [key: string]: any, id: string | number | symbol }; + +export type NonNumberNonObjectButDefined = string | boolean | symbol | null; + +export type ObjectWithoutId = Omit; + +export type ObjectWithNonNumberId = { + [key: string]: any, + id: string | boolean | symbol | null | object | undefined; +} + +export type ObjectWithNumberId = { + [key: string]: any, + id: number; +} + +export type RestrictValById = NonNumberNonObjectButDefined | ObjectWithoutId | ObjectWithNonNumberId | ObjectWithNumberId; + +export type DummyAny = string | number | boolean | null | undefined | object | symbol | void | Function | never; \ No newline at end of file diff --git a/src/utils/utils.ts b/src/utils/utils.ts index a28334f..50187f3 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1,3 +1,22 @@ +import {z} from 'zod' +/** + * data-structure-typed + * + * @author Tyler Zeng + * @copyright Copyright (c) 2022 Tyler Zeng + * @license MIT License + */ +import type { + NonNumberNonObjectButDefined, + ObjectWithNonNumberId, + ObjectWithNumberId, + ObjectWithoutId, + Thunk, + ToThunkFn, + TrlAsyncFn, + TrlFn +} from './types'; + export const uuidV4 = function () { return 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.replace(/[x]/g, function (c) { const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); @@ -22,24 +41,6 @@ export const arrayRemove = function (array: T[], predicate: (item: T, index: }; -/** - * data-structure-typed - * - * @author Tyler Zeng - * @copyright Copyright (c) 2022 Tyler Zeng - * @license MIT License - */ -import type { - NonNumberNonObjectButDefined, - ObjectWithNonNumberId, - ObjectWithNumberId, - ObjectWithoutId, - Thunk, - ToThunkFn, - TrlAsyncFn, - TrlFn -} from './types'; - export const THUNK_SYMBOL = Symbol('thunk') export const isThunk = (fnOrValue: any) => { @@ -85,86 +86,3 @@ export const trampolineAsync = (fn: TrlAsyncFn) => { {cont} ) } - -// export class AutoPruneMap extends Map { -// -// private _proxySet: Set; -// get proxySet(): Set { -// return this._proxySet; -// } -// -// set proxySet(value: Set) { -// this._proxySet = value; -// } -// -// private _isEmptyArrayAllowed: boolean; -// -// get isEmptyArrayAllowed(): boolean { -// return this._isEmptyArrayAllowed; -// } -// -// set isEmptyArrayAllowed(value: boolean) { -// this._isEmptyArrayAllowed = value; -// } -// -// constructor(isEmptyArrayAllowed: boolean = false) { -// super(); -// this._isEmptyArrayAllowed = isEmptyArrayAllowed; -// this._proxySet = new Set(); -// } -// -// set(key: K, value: V): this { -// if (Array.isArray(value) && !this.proxySet.has(value)) { -// if(!this.isEmptyArrayAllowed && value.length === 0) return this; -// value = this.createArrayProxy(value, key); -// if (!this.proxySet.has(value)) this.proxySet.add(value); -// } -// super.set(key, value); -// return this; -// } -// -// private createArrayProxy(array: V & any[], key: K) { -// const that = this; -// const proxyHandler: ProxyHandler = { -// set(target: any, property: PropertyKey, value: any): boolean { -// const result = Reflect.set(target, property, value); -// that.checkAndDeleteEmptyArray(key); -// return result; -// }, -// deleteProperty(target: any, property: PropertyKey): boolean { -// const result = Reflect.deleteProperty(target, property); -// that.checkAndDeleteEmptyArray(key); -// return result; -// }, -// } -// return new Proxy(array, proxyHandler); -// } -// -// private checkAndDeleteEmptyArray(key: K): void { -// const value = this.get(key); -// -// if (Array.isArray(value) && value.length === 0) { -// super.delete(key); -// } -// } -// } - -export function isNonNumberNonObjectButDefined(val: any): val is NonNumberNonObjectButDefined { - return typeof val !== 'number' && typeof val !== 'object' && val !== undefined; -} - -export function isObjectWithoutId(val: any): val is ObjectWithoutId { - return typeof val === 'object' && !('id' in val); -} - -export function isObjectWithNonNumberId(val: any): val is ObjectWithNonNumberId { - return typeof val === 'object' && 'id' in val && typeof val.id !== 'number'; -} - -export function isObjectWithNumberId(val: any): val is ObjectWithNumberId { - return typeof val === 'object' && 'id' in val && typeof val.id === 'number'; -} - -export function isNumber(val: any): val is number { - return typeof val === 'number'; -} \ No newline at end of file diff --git a/src/utils/validate-type.ts b/src/utils/validate-type.ts new file mode 100644 index 0000000..ab0b1cb --- /dev/null +++ b/src/utils/validate-type.ts @@ -0,0 +1,69 @@ +import {z} from 'zod'; +import {NonNumberNonObjectButDefined, ObjectWithNonNumberId, ObjectWithNumberId, ObjectWithoutId} from './types'; + + +export const nonNumberNonObjectButDefinedSchema = z.union([z.string(), + z.boolean(), z.any()]) + .nullable() + +export const keyValueObjectSchema = z.record(z.unknown()) + +export const objectWithoutIdSchema = keyValueObjectSchema.refine(obj => !('id' in obj), { + message: 'Object cannot contain the \'id\' field', +}); + +export const keyValueObjectWithIdSchema = z.record(z.any()).and( + z.object({ + id: z.union([z.string(), z.number(), z.any()]) + }) +) + +export const objectWithNonNumberIdSchema = z.record(z.any()).and( + z.object({ + id: z + .union([z.string(), z.boolean(), z.any(), z.any(), z.undefined()]) + .nullable() + }) +) + +export const objectWithNumberIdSchema = z.record(z.any()).and( + z.object({ + id: z.number() + }) +) + +export const binaryTreeNodeValWithId = z.union([ + nonNumberNonObjectButDefinedSchema, + objectWithoutIdSchema, + objectWithNonNumberIdSchema, + objectWithNumberIdSchema +]) + +export function parseBySchema(schema: z.Schema, val: any) { + try { + schema.parse(val); + return true; + } catch (error) { + return false; + } +} + +export function isNonNumberNonObjectButDefined(val: any): val is NonNumberNonObjectButDefined { + return parseBySchema(nonNumberNonObjectButDefinedSchema, val); +} + +export function isObjectWithoutId(val: any): val is ObjectWithoutId { + return parseBySchema(objectWithoutIdSchema, val); +} + +export function isObjectWithNonNumberId(val: any): val is ObjectWithNonNumberId { + return parseBySchema(objectWithNonNumberIdSchema, val); +} + +export function isObjectWithNumberId(val: any): val is ObjectWithNumberId { + return parseBySchema(objectWithNonNumberIdSchema, val); +} + +export function isNumber(val: any): val is number { + return typeof val === 'number'; +} \ No newline at end of file