diff --git a/.vscode/settings.json b/.vscode/settings.json index 7d0c8f8d..42c8954c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -55,6 +55,7 @@ "outdir", "outfile", "pmpa", + "Strigifyable", "tsbuildinfo", "typeof" ] diff --git a/package.json b/package.json index 90cebe77..b6d00ab6 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,8 @@ "@lerna-lite/publish": "^3.1.0", "@lerna-lite/run": "^3.1.0", "@lerna-lite/version": "^3.1.0", - "@typescript-eslint/eslint-plugin": "^6.15.0", - "@typescript-eslint/parser": "^6.15.0", + "@typescript-eslint/eslint-plugin": "^6.16.0", + "@typescript-eslint/parser": "^6.16.0", "eslint": "^8.56.0", "eslint-plugin-import": "^2.29.1", "prettier": "^3.1.1", diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index ede883e4..bf56bd37 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -50,8 +50,8 @@ "devDependencies": { "@alwatr/prettier-config": "workspace:^", "@alwatr/tsconfig-base": "workspace:^", - "@typescript-eslint/eslint-plugin": "^6.15.0", - "@typescript-eslint/parser": "^6.15.0", + "@typescript-eslint/eslint-plugin": "^6.16.0", + "@typescript-eslint/parser": "^6.16.0", "eslint": "^8.56.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.29.1", diff --git a/packages/type-helper/README.md b/packages/type-helper/README.md new file mode 100644 index 00000000..2667ee14 --- /dev/null +++ b/packages/type-helper/README.md @@ -0,0 +1,26 @@ +# Typescript Type Helpers + +Collection of useful typescript type helpers. + +## Installation + +```bash +yarn add @alwatr/type-helper +``` + +## Usage + +```typescript +import type {JSONObject} from '@alwatr/type-helper'; + +const obj: JSONObject = { + foo: 'bar', + baz: { + qux: 1, + arr: [1, 2, 3], + }, + qux: true, +}; +``` + +Read the [source code](https://github.com/Alwatr/nanolib/tree/next/packages/type-helper/src) for more details. diff --git a/packages/type-helper/package.json b/packages/type-helper/package.json new file mode 100644 index 00000000..85313a59 --- /dev/null +++ b/packages/type-helper/package.json @@ -0,0 +1,56 @@ +{ + "name": "@alwatr/type-helper", + "version": "1.0.0", + "description": "Collection of useful typescript type helpers.", + "author": "S. Ali Mihandoost ", + "keywords": [ + "type", + "type-helper", + "types", + "typescript", + "utility", + "util", + "utils", + "nanolib", + "alwatr" + ], + "main": "", + "types": "./dist/main.d.ts", + "license": "MIT", + "files": [ + "**/*.{js,mjs,cjs,map,d.ts,html,md}", + "!demo/**/*" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/Alwatr/nanolib", + "directory": "packages/type-helper" + }, + "homepage": "https://github.com/Alwatr/nanolib/tree/next/packages/type-helper#readme", + "bugs": { + "url": "https://github.com/Alwatr/nanolib/issues" + }, + "prettier": "@alwatr/prettier-config", + "scripts": { + "b": "yarn run build", + "w": "yarn run watch", + "c": "yarn run clean", + "cb": "yarn run clean && yarn run build", + "d": "yarn run build:es && ALWATR_DEBUG=1 yarn node", + "build": "yarn run build:ts", + "build:es": "echo skip build:es", + "build:ts": "tsc --build", + "watch": "yarn run watch:ts & yarn run watch:es", + "watch:es": "yarn run build:es --watch", + "watch:ts": "yarn run build:ts --watch --preserveWatchOutput", + "clean": "rm -rfv dist *.tsbuildinfo" + }, + "devDependencies": { + "@alwatr/prettier-config": "workspace:^", + "@alwatr/tsconfig-base": "workspace:^", + "typescript": "^5.3.3" + } +} diff --git a/packages/type-helper/src/main.ts b/packages/type-helper/src/main.ts new file mode 100644 index 00000000..f565a975 --- /dev/null +++ b/packages/type-helper/src/main.ts @@ -0,0 +1,192 @@ +/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/consistent-indexed-object-style, @typescript-eslint/ban-types */ + +/** + * Represents a primitive type in TypeScript. + * @typedef {string | number | bigint | boolean | symbol | null | undefined} Primitive + */ +export type Primitive = string | number | bigint | boolean | symbol | null | undefined; + +/** + * Represents a type that includes all falsy values: false, '', 0, null, and undefined. + */ +export type Falsy = false | '' | 0 | null | undefined; + +/** + * Represents a type that can be null or undefined. + */ +export type Nullish = null | undefined; + +/** + * Represents a type that can be either a value of type T or null. + * @template T - The type of the value. + */ +export type Nullable = T | null; + +/** + * Represents a type that can either be of type T or undefined. + * @template T - The type parameter. + */ +export type Maybe = T | undefined; + +/** + * Represents a type that can either be a value of type T or a promise that resolves to a value of type T. + * @template T - The type of the value or the resolved value. + */ +export type MaybePromise = T | Promise; + +/** + * Represents a type that can be either a single value or an array of values. + * @template T - The type of the value(s). + */ +export type SingleOrArray = T | T[]; + +/** + * Type helper that removes the undefined type from a given type. + * @template T The type to remove undefined from. + * @returns The type without undefined. + */ +export type NonUndefined = T extends undefined ? never : T; + +/** + * Returns the keys of an object type `T` that are required (not optional). + * + * @template T - The object type. + * @returns The keys of `T` that are required. + */ +export type RequiredKeys = { + [K in keyof T]-?: {} extends Pick ? never : K; +}[keyof T]; + +/** + * Returns the keys of an object type `T` that are optional. + * + * @template T - The object type. + * @returns The keys of `T` that are optional. + */ +export type OptionalKeys = { + [K in keyof T]-?: {} extends Pick ? K : never; +}[keyof T]; + +/** + * Represents a type that makes all properties of an object and its nested objects readonly. + * @template T - The type to make readonly. + * @returns The readonly version of the input type. + */ +export type DeepReadonly = T extends ((...args: any[]) => any) | Primitive + ? T + : T extends DeepReadonlyArray_ + ? DeepReadonlyArray_ + : T extends DeepReadonlyObject_ + ? DeepReadonlyObject_ + : T; +type DeepReadonlyArray_ = readonly DeepReadonly[]; +type DeepReadonlyObject_ = { + readonly [P in keyof T]: DeepReadonly; +}; + +/** + * Recursively makes all properties of an object and its nested objects/array required. + * + * @template T - The type to make deep required. + * @param {T} value - The value to make deep required. + * @returns {DeepRequired} - The deep required type. + */ +export type DeepRequired = T extends (...args: any[]) => any + ? T + : T extends any[] + ? DeepRequiredArray_ + : T extends object + ? DeepRequiredObject_ + : T; +type DeepRequiredArray_ = DeepRequired>[]; +type DeepRequiredObject_ = { + [P in keyof T]-?: DeepRequired>; +}; + +/** + * Represents a type that makes all properties of the given type optional recursively. + * @template T - The type to make partial. + */ +export type DeepPartial = {[P in keyof T]?: DeepPartial_}; +type DeepPartial_ = T extends ((...args: any[]) => any) | Primitive + ? T + : T extends (infer U)[] + ? DeepPartialArray_ + : T extends object + ? DeepPartial + : T | undefined; +type DeepPartialArray_ = DeepPartial_[]; + +/** + * Represents a class constructor. + * @template T - The type of the class. + */ +export type Class = new (...args: any[]) => T; + +/** + * Removes the first parameter from a function type. + * @template F The function type. + * @returns A new function type without the first parameter. + */ +export type OmitFirstParam = F extends (x: any, ...args: infer A) => infer R ? (...args: A) => R : never; + +/** + * Retrieves the type of a property from an object type. + * + * @template T - The object type. + * @template K - The property key. + * @returns {Prop} - The type of the property. + */ +export type Prop = K extends keyof T ? T[K] : never; + +/** + * Retrieves the union of all values in the given object type. + * @typeparam T - The object type. + * @returns The union of all values in the object type. + */ +export type Values = T[keyof T]; + +/** + * Extracts the type of individual items in an array. + * If the input type is an array, it returns the type of the array items. + * If the input type is not an array, it returns the input type itself. + * + * @typeParam T - The input type. + * @returns The type of individual items in the array, or the input type itself. + */ +export type ArrayItems = T extends (infer K)[] ? K : T; + +/** + * Merges two types together by omitting keys from the first type that exist in the second type, + * and then combining the remaining keys with the keys from the second type. + * @template M - The first type to merge. + * @template N - The second type to merge. + * @returns A new type that is the result of merging the two input types. + */ +export type Merge = Omit & N; + +/** + * Represents a dictionary object with string keys and values of type T. + */ +export interface Dictionary { + [key: string]: T; +} + +/** + * Strigifyable JSON value + */ +/** + * Represents a JSON value that can be of type string, number, boolean, null, undefined, + * JSONArray, or JSONObject. + */ +export type JSONValue = string | number | boolean | null | undefined | JSONArray | JSONObject; + +/** + * Represents an array of JSONValues. + */ +type JSONArray = JSONValue[]; + +/** + * Represents an object of Record + */ +type JSONObject = Dictionary; diff --git a/packages/type-helper/tsconfig.json b/packages/type-helper/tsconfig.json new file mode 100644 index 00000000..eadbf307 --- /dev/null +++ b/packages/type-helper/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "@alwatr/tsconfig-base/tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "declarationMap": false, + "emitDeclarationOnly": true, + "composite": true, + }, + "include": ["src/**/*.ts"] +} diff --git a/yarn.lock b/yarn.lock index 52944f85..63eeed25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -29,8 +29,8 @@ __metadata: dependencies: "@alwatr/prettier-config": "workspace:^" "@alwatr/tsconfig-base": "workspace:^" - "@typescript-eslint/eslint-plugin": "npm:^6.15.0" - "@typescript-eslint/parser": "npm:^6.15.0" + "@typescript-eslint/eslint-plugin": "npm:^6.16.0" + "@typescript-eslint/parser": "npm:^6.16.0" esbuild: "npm:^0.19.10" eslint: "npm:^8.56.0" eslint-import-resolver-typescript: "npm:^3.6.1" @@ -109,6 +109,16 @@ __metadata: languageName: unknown linkType: soft +"@alwatr/type-helper@workspace:packages/type-helper": + version: 0.0.0-use.local + resolution: "@alwatr/type-helper@workspace:packages/type-helper" + dependencies: + "@alwatr/prettier-config": "workspace:^" + "@alwatr/tsconfig-base": "workspace:^" + typescript: "npm:^5.3.3" + languageName: unknown + linkType: soft + "@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.21.4": version: 7.22.13 resolution: "@babel/code-frame@npm:7.22.13" @@ -1094,15 +1104,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/eslint-plugin@npm:6.15.0" +"@typescript-eslint/eslint-plugin@npm:^6.16.0": + version: 6.16.0 + resolution: "@typescript-eslint/eslint-plugin@npm:6.16.0" dependencies: "@eslint-community/regexpp": "npm:^4.5.1" - "@typescript-eslint/scope-manager": "npm:6.15.0" - "@typescript-eslint/type-utils": "npm:6.15.0" - "@typescript-eslint/utils": "npm:6.15.0" - "@typescript-eslint/visitor-keys": "npm:6.15.0" + "@typescript-eslint/scope-manager": "npm:6.16.0" + "@typescript-eslint/type-utils": "npm:6.16.0" + "@typescript-eslint/utils": "npm:6.16.0" + "@typescript-eslint/visitor-keys": "npm:6.16.0" debug: "npm:^4.3.4" graphemer: "npm:^1.4.0" ignore: "npm:^5.2.4" @@ -1115,44 +1125,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 78054afb0d4ab12d82db7a9cb005dfa2be42962341728abf4a81802e1f4c0f5b23de4870287f4b7e32aa4a4bc900bbc218f2d4d0c02aa77452e8e8e0b71fe3de + checksum: c8a68e0953d8b94f6b85d3a82090e61e670bcb0945cbee4d741321c56db727429ad47c48b8403ad1dab3b0842689bd2d4b85c99b76c51ac4f5be7f5f61c4c314 languageName: node linkType: hard -"@typescript-eslint/parser@npm:^6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/parser@npm:6.15.0" +"@typescript-eslint/parser@npm:^6.16.0": + version: 6.16.0 + resolution: "@typescript-eslint/parser@npm:6.16.0" dependencies: - "@typescript-eslint/scope-manager": "npm:6.15.0" - "@typescript-eslint/types": "npm:6.15.0" - "@typescript-eslint/typescript-estree": "npm:6.15.0" - "@typescript-eslint/visitor-keys": "npm:6.15.0" + "@typescript-eslint/scope-manager": "npm:6.16.0" + "@typescript-eslint/types": "npm:6.16.0" + "@typescript-eslint/typescript-estree": "npm:6.16.0" + "@typescript-eslint/visitor-keys": "npm:6.16.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: e7f265fd4abd3bc49fa5b304cd4b9c22801ac5a9da4ee342bbab0c117d629ac4aad6998555b61a8c5a0b279c443a44ae99f16669e24e3ef17ccec20c8b7019e7 + checksum: 9d573d14df4ec661dccaca785223a8a330d64f50a9279ff9170b1da22198ff91b9afa3ee7d3d7127c0cbc148c86831e76b33fc5b47d630799e98940ef666bfe0 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/scope-manager@npm:6.15.0" +"@typescript-eslint/scope-manager@npm:6.16.0": + version: 6.16.0 + resolution: "@typescript-eslint/scope-manager@npm:6.16.0" dependencies: - "@typescript-eslint/types": "npm:6.15.0" - "@typescript-eslint/visitor-keys": "npm:6.15.0" - checksum: 3428d99de440f227cbc2afb44cdcb25e44c4b49c5f490392f83e21d2048210a6ec2f2f68133376c842034f5b5ba4ec9721da7caa18e631e23b57e20927b5b6f0 + "@typescript-eslint/types": "npm:6.16.0" + "@typescript-eslint/visitor-keys": "npm:6.16.0" + checksum: 3b275e528d19f4f36c4acd6cb872b5f004175512dce30cef0ac7a9121bb23d21e5e0f4b62658dbfea2b15851e7fa930372696f25a6c87492f863171ab56f5364 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/type-utils@npm:6.15.0" +"@typescript-eslint/type-utils@npm:6.16.0": + version: 6.16.0 + resolution: "@typescript-eslint/type-utils@npm:6.16.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:6.15.0" - "@typescript-eslint/utils": "npm:6.15.0" + "@typescript-eslint/typescript-estree": "npm:6.16.0" + "@typescript-eslint/utils": "npm:6.16.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.0.1" peerDependencies: @@ -1160,59 +1170,60 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 32cb531a4b5e0ccd431cba553ec73b87d4453b48af288a33e359ba4f5278126390d82799b61d3f0fbf135cfde1ac6c2275c2cf37a676e8a2a2811e774e660f16 + checksum: a5339cc1375d12411fcb242249143b28401fb18890bb2a1cff5275ba946affb4a2066cd8203e83ac383bd9d791a79ea6ee1cbf7a30deed5c832ed002897bbf82 languageName: node linkType: hard -"@typescript-eslint/types@npm:6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/types@npm:6.15.0" - checksum: 6e33529ea301c8c4b8c1f589dadd5d2a66c1b24ec87a577524fbc996d4c7b65d4f4fdfa4a3937b691efee6a10a6b16f7bfcabe98a15e0fc0c0c57aa0d80dcc25 +"@typescript-eslint/types@npm:6.16.0": + version: 6.16.0 + resolution: "@typescript-eslint/types@npm:6.16.0" + checksum: 74d9a8b7fd1b85fd1824295c92bc2f506148e450c9897f65ddaa089091017df4e25676c5b098b75c8f00529b84492f303a6b1870bb0ffee83997081325891d53 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/typescript-estree@npm:6.15.0" +"@typescript-eslint/typescript-estree@npm:6.16.0": + version: 6.16.0 + resolution: "@typescript-eslint/typescript-estree@npm:6.16.0" dependencies: - "@typescript-eslint/types": "npm:6.15.0" - "@typescript-eslint/visitor-keys": "npm:6.15.0" + "@typescript-eslint/types": "npm:6.16.0" + "@typescript-eslint/visitor-keys": "npm:6.16.0" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" + minimatch: "npm:9.0.3" semver: "npm:^7.5.4" ts-api-utils: "npm:^1.0.1" peerDependenciesMeta: typescript: optional: true - checksum: 08955f6e84b8edb855a6769671e85889e52b15b82e00a64f595da867b21ad060e5342787c436d77702b2a1f39d411ac79b81a8d2e2006e9b1886eadb08b626df + checksum: c7109e90b40b3c8f1042beb7f1a7a97eeba3b6a903acd82df4947900d68bd31d04b530a190c099666c5ca4886efc162de7b42de754a44b189e41237210797d9e languageName: node linkType: hard -"@typescript-eslint/utils@npm:6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/utils@npm:6.15.0" +"@typescript-eslint/utils@npm:6.16.0": + version: 6.16.0 + resolution: "@typescript-eslint/utils@npm:6.16.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" "@types/json-schema": "npm:^7.0.12" "@types/semver": "npm:^7.5.0" - "@typescript-eslint/scope-manager": "npm:6.15.0" - "@typescript-eslint/types": "npm:6.15.0" - "@typescript-eslint/typescript-estree": "npm:6.15.0" + "@typescript-eslint/scope-manager": "npm:6.16.0" + "@typescript-eslint/types": "npm:6.16.0" + "@typescript-eslint/typescript-estree": "npm:6.16.0" semver: "npm:^7.5.4" peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: 53519a2027681bdc8f028f9421c65f193f91b5bb1659465fedb8043376c693c2391211f1c01d8ba25bfaa7f7b3a102263d7123f9dfade12032159f4b4490f0fb + checksum: 586c4c0e1ca249daf9958f0d88df3af010a7592a19db1a7dc198754542b584314896536fe56ea9c93dd0ddd531154e7697002643d46e24a8d3a459721a626e91 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:6.15.0": - version: 6.15.0 - resolution: "@typescript-eslint/visitor-keys@npm:6.15.0" +"@typescript-eslint/visitor-keys@npm:6.16.0": + version: 6.16.0 + resolution: "@typescript-eslint/visitor-keys@npm:6.16.0" dependencies: - "@typescript-eslint/types": "npm:6.15.0" + "@typescript-eslint/types": "npm:6.16.0" eslint-visitor-keys: "npm:^3.4.1" - checksum: bf9f71af60bd63d1073900e75c5a0aa6eddd672f6c3ac6092c765d67deb7a0c32d2a5f6f3aee9e95f93a93d58563a76da209bd8487aadafd4d013100ffe38520 + checksum: 13c4d90355e288eac432d2845e37bb2acc03dab6d8568564558c1914a9aa44352f2a7ff29d0f50e0b3e68d66cca5f27b2732af5ff193b82571b4366309842880 languageName: node linkType: hard @@ -1320,8 +1331,8 @@ __metadata: "@lerna-lite/publish": "npm:^3.1.0" "@lerna-lite/run": "npm:^3.1.0" "@lerna-lite/version": "npm:^3.1.0" - "@typescript-eslint/eslint-plugin": "npm:^6.15.0" - "@typescript-eslint/parser": "npm:^6.15.0" + "@typescript-eslint/eslint-plugin": "npm:^6.16.0" + "@typescript-eslint/parser": "npm:^6.16.0" eslint: "npm:^8.56.0" eslint-plugin-import: "npm:^2.29.1" prettier: "npm:^3.1.1" @@ -3990,6 +4001,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:9.0.3, minimatch@npm:^9.0.0, minimatch@npm:^9.0.1, minimatch@npm:^9.0.3": + version: 9.0.3 + resolution: "minimatch@npm:9.0.3" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac + languageName: node + linkType: hard + "minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -3999,15 +4019,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.0, minimatch@npm:^9.0.1, minimatch@npm:^9.0.3": - version: 9.0.3 - resolution: "minimatch@npm:9.0.3" - dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac - languageName: node - linkType: hard - "minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6": version: 1.2.8 resolution: "minimist@npm:1.2.8"