@alessiofrittoli/type-utils 1.10.1 → 2.1.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.
package/README.md CHANGED
@@ -1,43 +1,76 @@
1
- # Type Utils ❇️
1
+ <h1 align="center">Type Utils ❇️</h1>
2
+ <h2 align="center">
3
+ Lightweight TypeScript types utilities
4
+ </h2>
5
+ <p align="center">
6
+ <a href="https://npmjs.org/package/@alessiofrittoli/type-utils">
7
+ <img src="https://img.shields.io/npm/v/@alessiofrittoli/type-utils" alt="Latest version"/>
8
+ </a>
9
+ <a href="https://socket.dev/npm/package/@alessiofrittoli/type-utils/overview">
10
+ <img src="https://socket.dev/api/badge/npm/package/@alessiofrittoli/type-utils" alt="Socket Security score"/>
11
+ </a>
12
+ <a href="https://npmjs.org/package/@alessiofrittoli/type-utils">
13
+ <img src="https://img.shields.io/npm/dm/@alessiofrittoli/type-utils.svg" alt="npm downloads"/>
14
+ </a>
15
+ <a href="https://bundlephobia.com/package/@alessiofrittoli/type-utils">
16
+ <img src="https://badgen.net/bundlephobia/dependency-count/@alessiofrittoli/type-utils" alt="Dependencies"/>
17
+ </a>
18
+ <a href="https://libraries.io/npm/%40alessiofrittoli%2Ftype-utils">
19
+ <img src="https://img.shields.io/librariesio/release/npm/@alessiofrittoli/type-utils" alt="Dependencies status"/>
20
+ </a>
21
+ </p>
22
+ <p align="center">
23
+ <a href="https://bundlephobia.com/package/@alessiofrittoli/type-utils">
24
+ <img src="https://badgen.net/bundlephobia/min/@alessiofrittoli/type-utils" alt="minified"/>
25
+ </a>
26
+ <a href="https://bundlephobia.com/package/@alessiofrittoli/type-utils">
27
+ <img src="https://badgen.net/bundlephobia/minzip/@alessiofrittoli/type-utils" alt="minizipped"/>
28
+ </a>
29
+ <a href="https://bundlephobia.com/package/@alessiofrittoli/type-utils">
30
+ <img src="https://badgen.net/bundlephobia/tree-shaking/@alessiofrittoli/type-utils" alt="Tree shakable"/>
31
+ </a>
32
+ </p>
33
+ <p align="center">
34
+ <a href="https://github.com/sponsors/alessiofrittoli">
35
+ <img src="https://img.shields.io/static/v1?label=Fund%20this%20package&message=%E2%9D%A4&logo=GitHub&color=%23DB61A2" alt="Fund this package"/>
36
+ </a>
37
+ </p>
2
38
 
3
- [![NPM Latest Version][version-badge]][npm-url] [![Socket Status][socket-badge]][socket-url] [![NPM Monthly Downloads][downloads-badge]][npm-url] [![Dependencies][deps-badge]][deps-url]
4
-
5
- [![GitHub Sponsor][sponsor-badge]][sponsor-url]
6
-
7
- [version-badge]: https://img.shields.io/npm/v/%40alessiofrittoli%2Ftype-utils
8
- [npm-url]: https://npmjs.org/package/%40alessiofrittoli%2Ftype-utils
9
- [socket-badge]: https://socket.dev/api/badge/npm/package/@alessiofrittoli/type-utils
10
- [socket-url]: https://socket.dev/npm/package/@alessiofrittoli/type-utils/overview
11
- [downloads-badge]: https://img.shields.io/npm/dm/%40alessiofrittoli%2Ftype-utils.svg
12
- [deps-badge]: https://img.shields.io/librariesio/release/npm/%40alessiofrittoli%2Ftype-utils
13
- [deps-url]: https://libraries.io/npm/%40alessiofrittoli%2Ftype-utils
14
39
  [sponsor-badge]: https://img.shields.io/static/v1?label=Fund%20this%20package&message=%E2%9D%A4&logo=GitHub&color=%23DB61A2
15
40
  [sponsor-url]: https://github.com/sponsors/alessiofrittoli
16
41
 
17
- ## Lightweight TypeScript types utilities
18
-
19
- ### Table of Contents
42
+ ### Table of Content
20
43
 
21
44
  - [Getting started](#getting-started)
45
+ - [Utility types usage](#utility-types-usage)
46
+ - [Global extenders setup](#global-extenders-setup)
47
+ - [Development](#development)
48
+ - [Local development](#local-development)
49
+ - [Production build](#production-build)
50
+ - [Lint](#lint)
51
+ - [Contributing](#contributing)
52
+ - [Security](#security)
22
53
  - [Utility types](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md)
23
54
  - [ValueOf](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#valueof)
24
55
  - [ValueOfKey](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#valueofkey)
25
56
  - [OptionalKey](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#optionalkey)
57
+ - [ReadOnly](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#readonly)
26
58
  - [Writeable](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#writeable)
27
59
  - [RenameKey](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#renamekey)
28
- - [ArgumentTypes](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#argumenttypes)
60
+ - [DeepPartial](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#deeppartial)
61
+ - [Full](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#full)
62
+ - [DeepFull](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#deepfull)
63
+ - [NonNullableFields](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#nonnullablefields)
64
+ - [MergedEnumValue](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#mergedenumvalue)
65
+ - [UnflatDotKeys](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#unflatdotkeys)
66
+ - [RecursivelyUnflatDotKeys](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#recursivelyunflatdotkeys)
67
+ - [ArgumentTypes](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md#argumenttypes) (deprecated)
29
68
  - [Extenders types](https://github.com/alessiofrittoli/type-utils/blob/master/docs/extenders/README.md)
30
69
  - [JSON](https://github.com/alessiofrittoli/type-utils/blob/master/docs/extenders/README.md#json)
31
70
  - [JSON.stringify\<T\>()](https://github.com/alessiofrittoli/type-utils/blob/master/docs/extenders/README.md#jsonstringifyt)
32
71
  - [JSON.parse\<T\>()](https://github.com/alessiofrittoli/type-utils/blob/master/docs/extenders/README.md#jsonparset)
33
72
  - [Body](https://github.com/alessiofrittoli/type-utils/blob/master/docs/extenders/README.md#body)
34
73
  - [Body.json\<T\>()](https://github.com/alessiofrittoli/type-utils/blob/master/docs/extenders/README.md#bodyjsont)
35
- - [Development](#development)
36
- - [Install dependencies](#install-dependencies)
37
- - [Build the source code](#build-the-source-code)
38
- - [ESLint](#eslint)
39
- - [Contributing](#contributing)
40
- - [Security](#security)
41
74
  - [Credits](#made-with-)
42
75
 
43
76
  ---
@@ -56,64 +89,94 @@ or using `pnpm`
56
89
  pnpm i @alessiofrittoli/type-utils
57
90
  ```
58
91
 
59
- This tiny library defines some global types which are not imported in your IDE by default.\
60
- After installation a `postinstall` script is executed to create the `alessiofrittoli-env.d.ts` file at the root of your project.
61
- This script will try to update your `tsconfig.json` file.
92
+ #### Utility types usage
93
+
94
+ Utility types are exported from the package entry point and can be imported where needed:
95
+
96
+ ```ts
97
+ import type { DeepPartial, ValueOf } from '@alessiofrittoli/type-utils'
98
+ ```
62
99
 
63
- If that fail, please, make sure to add `alessiofrittoli-env.d.ts` in the array of the "include" property in your `tsconfig.json`.
100
+ See the [Utility types docs](https://github.com/alessiofrittoli/type-utils/blob/master/docs/utils/README.md) for the full list.
101
+
102
+ #### Global extenders setup
103
+
104
+ [Extenders types](https://github.com/alessiofrittoli/type-utils/blob/master/docs/extenders/README.md) augment built-in global interfaces (such as `JSON` and `Body`) and they are the only global types defined by this library. Global types are not loaded by your IDE by default.
105
+
106
+ After installation a `postinstall` script is executed to create (or update) the `globals.d.ts` file at the root of your project, adding the following globals types reference:
107
+
108
+ ```ts
109
+ /// <reference types="@alessiofrittoli/type-utils/globals" />
110
+ ```
111
+
112
+ If the file get ignored by the TypeScript program of your project, please, manually add `globals.d.ts` in the array of the "include" property in your `tsconfig.json`.
64
113
 
65
114
  ```json
66
115
  {
67
116
  "compilerOptions": {
68
117
  ...
69
118
  },
70
- "include": [ ..., "alessiofrittoli-env.d.ts" ],
119
+ "include": [ ..., "globals.d.ts" ],
71
120
  }
72
121
  ```
73
122
 
74
123
  You can skip this process if `*.ts` is already in your "include" array.
75
124
 
76
- ⚠️ If the `alessiofrittoli-env.d.ts` file creation fails you can run the following command to manually execute the script:
125
+ ⚠️ If the `globals.d.ts` file creation fails you can run the following command to manually execute the script:
77
126
 
78
127
  ```bash
79
- node node_modules/@alessiofrittoli/type-utils/scripts/postinstall/index.js
128
+ node node_modules/@alessiofrittoli/type-utils/scripts/postinstall/index.cjs
80
129
  ```
81
130
 
131
+ 💡 If you don't want to use the global extenders, simply remove the reference above from your `globals.d.ts` file. Utility types will keep working since they are imported explicitly.
132
+
82
133
  ---
83
134
 
84
135
  ### Development
85
136
 
86
- #### Install dependencies
137
+ Run the following to start development
87
138
 
88
- ```bash
89
- npm install
139
+ ```shell
140
+ nvm use
141
+
142
+ pnpm i
90
143
  ```
91
144
 
92
- or using `pnpm`
145
+ #### Local development
93
146
 
94
- ```bash
95
- pnpm i
147
+ Run the following
148
+
149
+ ```shell
150
+ pnpm dev
96
151
  ```
97
152
 
98
- #### Build the source code
153
+ This will generate unminified output code, sourcemaps and will enable source file watcher.
99
154
 
100
- Run the following command to test and build code for distribution.
155
+ ---
101
156
 
102
- ```bash
157
+ #### Production build
158
+
159
+ Run the following to create a local production build
160
+
161
+ ```shell
103
162
  pnpm build
104
163
  ```
105
164
 
106
- #### [ESLint](https://www.npmjs.com/package/eslint)
165
+ This will lint and type-check the source code, then generate minified output code without sourcemaps.
107
166
 
108
- Run warnings and errors checks.
167
+ ---
109
168
 
110
- ```bash
169
+ #### Lint
170
+
171
+ Run warnings and errors checks with [oxlint](https://oxc.rs/docs/guide/usage/linter):
172
+
173
+ ```shell
111
174
  pnpm lint
112
175
  ```
113
176
 
114
177
  ---
115
178
 
116
- ### Contributing
179
+ #### Contributing
117
180
 
118
181
  Contributions are truly welcome!
119
182
 
@@ -125,10 +188,12 @@ Help keep this project up to date with [GitHub Sponsor][sponsor-url].
125
188
 
126
189
  ---
127
190
 
128
- ### Security
191
+ #### Security
129
192
 
130
193
  If you believe you have found a security vulnerability, we encourage you to **_responsibly disclose this and NOT open a public issue_**. We will investigate all legitimate reports. Email `security@alessiofrittoli.it` to disclose any security vulnerabilities.
131
194
 
195
+ ---
196
+
132
197
  ### Made with ☕
133
198
 
134
199
  <table style='display:flex;gap:20px;'>
File without changes
@@ -0,0 +1,17 @@
1
+ declare class Stringified<T> extends String {
2
+ private ___stringified;
3
+ }
4
+ declare global {
5
+ interface JSON {
6
+ stringify<T>(value: T, replacer?: (this: {
7
+ '': T;
8
+ } | T, key: string, value: T) => any, space?: string | number): T extends Date ? string : string & Stringified<T>;
9
+ stringify<T>(value: T, replacer?: (T extends object ? (keyof T)[] : (number | string)[]) | null, space?: string | number): T extends Date ? string : string & Stringified<T>;
10
+ parse<T, U extends keyof T = keyof T>(text: string | Stringified<T>, reviver?: (key: U, value: T[U]) => any): T;
11
+ parse(text: string, reviver?: (key: any, value: any) => any): any;
12
+ }
13
+ interface Body {
14
+ json<T = any>(): Promise<T>;
15
+ }
16
+ }
17
+ export {};
@@ -0,0 +1,17 @@
1
+ declare class Stringified<T> extends String {
2
+ private ___stringified;
3
+ }
4
+ declare global {
5
+ interface JSON {
6
+ stringify<T>(value: T, replacer?: (this: {
7
+ '': T;
8
+ } | T, key: string, value: T) => any, space?: string | number): T extends Date ? string : string & Stringified<T>;
9
+ stringify<T>(value: T, replacer?: (T extends object ? (keyof T)[] : (number | string)[]) | null, space?: string | number): T extends Date ? string : string & Stringified<T>;
10
+ parse<T, U extends keyof T = keyof T>(text: string | Stringified<T>, reviver?: (key: U, value: T[U]) => any): T;
11
+ parse(text: string, reviver?: (key: any, value: any) => any): any;
12
+ }
13
+ interface Body {
14
+ json<T = any>(): Promise<T>;
15
+ }
16
+ }
17
+ export {};
@@ -0,0 +1 @@
1
+ export{};
package/dist/index.d.cts CHANGED
@@ -1,104 +1,204 @@
1
- //#region src/globals/extenders/json.d.ts
2
- declare class Stringified<T> extends String {
3
- private ___stringified;
4
- }
5
- declare global {
6
- interface JSON {
7
- stringify<T>(value: T, replacer?: (this: {
8
- '': T;
9
- } | T, key: string, value: T) => any, space?: string | number): T extends Date ? string : string & Stringified<T>;
10
- stringify<T>(value: T, replacer?: (T extends object ? (keyof T)[] : (number | string)[]) | null, space?: string | number): T extends Date ? string : string & Stringified<T>;
11
- parse<T, U extends keyof T = keyof T>(text: string | Stringified<T>, reviver?: (key: U, value: T[U]) => any): T;
12
- parse(text: string, reviver?: (key: any, value: any) => any): any;
13
- }
14
- interface Body {
15
- json<T = any>(): Promise<T>;
16
- }
17
- }
18
- //#endregion
19
- //#region src/globals/utils/index.d.ts
20
- declare global {
21
- /**
22
- * Get the Type of the Value of a object/array property.
23
- */
24
- type ValueOf<T> = T[keyof T];
25
- /**
26
- * Get the Type of the Value of object/array specific property.
27
- */
28
- type ValueOfKey<T, P extends keyof T> = T[P];
29
- /**
30
- * Make a specific property optional.
31
- */
32
- type OptionalKey<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
33
- /**
34
- * Make read-only properties of an object recursively writeable.
35
- */
36
- type ReadOnly<T> = keyof T extends never ? T : { readonly [P in keyof T]: Writeable<T[P]>; };
37
- /**
38
- * Make read-only properties of an object recursively writeable.
39
- */
40
- type Writeable<T> = keyof T extends never ? T : { -readonly [P in keyof T]: Writeable<T[P]>; };
41
- /**
42
- * Renames a property while inheriting its type.
43
- */
44
- type RenameKey<T, K extends keyof T, R extends PropertyKey> = Omit<T, K> & { [P in R]: T[K]; };
45
- /** Get the Type of a function arguments. */
46
- type ArgumentTypes<F extends Function> = F extends ((...args: infer A) => any) ? A : never;
47
- /**
48
- * Makes all 1st level properties of a type required.
49
- */
50
- type Full<T> = { [P in keyof T]-?: T[P]; };
51
- /**
52
- * Recursively makes all properties of a type required at all nested levels.
53
- */
54
- type DeepFull<T> = { [P in keyof T]-?: DeepFull<T[P]>; };
55
- /**
56
- * Removes `null` and `undefined` from all properties of a type.
57
- */
58
- type NonNullableFields<T> = { [P in keyof T]: NonNullable<T[P]>; };
59
- /**
60
- * Get the type of the value of merged enums.
61
- *
62
- * @example
63
- *
64
- * ```ts
65
- * enum Mammals {
66
- * Humans,
67
- * Bats,
68
- * Dolphins
69
- * }
70
- *
71
- * enum Reptiles {
72
- * Snakes,
73
- * Alligators,
74
- * Lizards
75
- * }
76
- *
77
- * // merged enums.
78
- * const Animals = { Mammals, Reptiles }
79
- * type Animals = typeof Animals
80
- *
81
- * type Animal = MergedEnumValueType<typeof Animals>
82
- *
83
- * const testAnimal: Animal = 0 as any
84
- *
85
- * switch ( testAnimal ) {
86
- * case Animals.Mammals.Humans:
87
- * case Animals.Mammals.Bats:
88
- * case Animals.Mammals.Dolphins:
89
- * case Animals.Reptiles.Snakes:
90
- * case Animals.Reptiles.Alligators:
91
- * case Animals.Reptiles.Lizards:
92
- * break
93
- * default: {
94
- * const invalid = testAnimal // `testAnimal` is type of never
95
- * }
96
- * }
97
- * ```
98
- */
99
- type MergedEnumValue<T> = { [K in keyof T]: { [K2 in keyof T[K]]: T[K][K2]; }[keyof T[K]]; }[keyof T];
100
- }
101
- //#endregion
102
- //#region src/index.d.ts
103
- export type DoNotUse = never;
104
- //#endregion
1
+ type RequiredKey<T> = { [K in keyof T]-?: object extends Pick<T, K> ? never : K; }[keyof T];
2
+ type Head<K> = K extends `${infer H}.${string}` ? H : never;
3
+ /**
4
+ * Picks the `${P}.*` keys of `T`, stripping the `${P}.` prefix. When `P` is `never`, `T` is returned as is.
5
+ *
6
+ * __INTERNAL__
7
+ */
8
+ type PickDotKeys<T, P extends string> = [P] extends [never] ? T : { [K in keyof T as K extends `${P}.${infer R}` ? R : never]: T[K]; };
9
+ /**
10
+ * When `Deep` is `true`, unflattens plain object values, leaving functions, arrays and primitives untouched.
11
+ *
12
+ * __INTERNAL__
13
+ */
14
+ type UnflatValue<V, Deep extends boolean> = Deep extends false ? V : V extends ((...args: any[]) => unknown) | readonly unknown[] ? V : V extends object ? Unflat<V, Deep> : V;
15
+ /**
16
+ * Turns dotted keys into nested objects. A nested object is required when at least one of its keys is required.
17
+ *
18
+ * __INTERNAL__
19
+ */
20
+ type Unflat<T, Deep extends boolean> = { [K in keyof T as K extends `${string}.${string}` ? never : K]: UnflatValue<T[K], Deep>; } & { [H in Head<Extract<RequiredKey<T>, string>>]: Unflat<PickDotKeys<T, H>, Deep>; } & { [H in Exclude<Head<keyof T>, Head<Extract<RequiredKey<T>, string>>>]?: Unflat<PickDotKeys<T, H>, Deep>; };
21
+ /**
22
+ * Turns dotted keys (`"a.b"?: T`) into nested objects (`a?: { b?: T }`).
23
+ *
24
+ * @template T The object interface to work with.
25
+ * @template K The object interface specific key to work with (`${P}.*`).
26
+ */
27
+ type UnflatDotKeys<T, K extends string = never> = Unflat<PickDotKeys<T, K>, false>;
28
+ /**
29
+ * Same as {@link UnflatDotKeys}, but dotted keys of nested object values are unflattened too.
30
+ *
31
+ * @template T The object interface to work with.
32
+ * @template K The object interface specific key to work with (`${K}.*`).
33
+ */
34
+ type RecursivelyUnflatDotKeys<T, K extends string = never> = Unflat<PickDotKeys<T, K>, true>;
35
+ /**
36
+ * Get the Type of the Value of a object/array property.
37
+ *
38
+ */
39
+ export type ValueOf<T> = T[keyof T];
40
+ /**
41
+ * Get the Type of the Value of object/array specific property.
42
+ *
43
+ */
44
+ export type ValueOfKey<T, P extends keyof T> = T[P];
45
+ /**
46
+ * Make a specific property optional.
47
+ *
48
+ */
49
+ export type OptionalKey<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
50
+ /**
51
+ * Recursively set to `readonly` all properties of the given type/interface.
52
+ *
53
+ * @template T The type/interface where each property will be set to `readonly`.
54
+ */
55
+ export type ReadOnly<T> = keyof T extends never ? T : { readonly [k in keyof T]: ReadOnly<T[k]>; };
56
+ /**
57
+ * Make read-only properties of an object recursively writeable.
58
+ *
59
+ */
60
+ export type Writeable<T> = keyof T extends never ? T : { -readonly [P in keyof T]: Writeable<T[P]>; };
61
+ /**
62
+ * Renames a property while inheriting its type and modifiers (`?` and `readonly`).
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * interface User {
67
+ * // the user name
68
+ * name?: string
69
+ * // the user age
70
+ * age: number
71
+ * }
72
+ *
73
+ * type ModifiedUser = RenameKey<User, 'name', 'username'>
74
+ * // { username?: string, age: number }
75
+ * ```
76
+ */
77
+ export type RenameKey<T, K extends keyof T, R extends PropertyKey> = { [P in keyof T as P extends K ? R : P]: T[P]; };
78
+ /**
79
+ * Get the Type of a function arguments.
80
+ *
81
+ * @deprecated use `Parameters` instead.
82
+ */
83
+ export type ArgumentTypes<F extends Function> = F extends ((...args: infer A) => any) ? A : never;
84
+ /**
85
+ * Recursively convert to optional all properties of the given type/interface.
86
+ *
87
+ * @template T The type/interface where each property will be set as optional.
88
+ */
89
+ export type DeepPartial<T> = T extends ((...args: any[]) => unknown) ? T : T extends object ? { [P in keyof T]?: DeepPartial<T[P]>; } : T;
90
+ /**
91
+ * Makes all 1st level properties of a type required.
92
+ *
93
+ */
94
+ export type Full<T> = { [P in keyof T]-?: T[P]; };
95
+ /**
96
+ * Recursively makes all properties of a type required at all nested levels.
97
+ *
98
+ */
99
+ export type DeepFull<T> = { [P in keyof T]-?: DeepFull<T[P]>; };
100
+ /**
101
+ * Removes `null` and `undefined` from all properties of a type.
102
+ *
103
+ */
104
+ export type NonNullableFields<T> = { [P in keyof T]: NonNullable<T[P]>; };
105
+ /**
106
+ * Get the type of the value of merged enums.
107
+ *
108
+ * @example
109
+ *
110
+ * ```ts
111
+ * enum Mammals {
112
+ * Humans,
113
+ * Bats,
114
+ * Dolphins
115
+ * }
116
+ *
117
+ * enum Reptiles {
118
+ * Snakes,
119
+ * Alligators,
120
+ * Lizards
121
+ * }
122
+ *
123
+ * // merged enums.
124
+ * const Animals = { Mammals, Reptiles }
125
+ * type Animals = typeof Animals
126
+ *
127
+ * type Animal = MergedEnumValueType<typeof Animals>
128
+ *
129
+ * const testAnimal: Animal = 0 as any
130
+ *
131
+ * switch ( testAnimal ) {
132
+ * case Animals.Mammals.Humans:
133
+ * case Animals.Mammals.Bats:
134
+ * case Animals.Mammals.Dolphins:
135
+ * case Animals.Reptiles.Snakes:
136
+ * case Animals.Reptiles.Alligators:
137
+ * case Animals.Reptiles.Lizards:
138
+ * break
139
+ * default: {
140
+ * const invalid = testAnimal // `testAnimal` is type of never
141
+ * }
142
+ * }
143
+ * ```
144
+ */
145
+ export type MergedEnumValue<T> = { [K in keyof T]: { [K2 in keyof T[K]]: T[K][K2]; }[keyof T[K]]; }[keyof T];
146
+ /**
147
+ * Builds a discriminated union of `[event, args]` tuples from an event map.
148
+ *
149
+ * Each key of the given map becomes a tuple whose first element is the event name
150
+ * and whose second element is its payload type. Since the tuples are discriminated
151
+ * by the event name, checking `event` narrows `args` to the matching payload.
152
+ *
153
+ * @template M The event map, where each key is an event name and each value is its payload type.
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * interface Events {
158
+ * play: { position: number }
159
+ * error: Error
160
+ * }
161
+ *
162
+ * type OnEventHandler = (...args: MappedArgs<Events>) => void
163
+ *
164
+ * const onEvent: OnEventHandler = (event, args): void => {
165
+ * if (event === 'play') {
166
+ * console.log(args.position) // `args` is narrowed to `{ position: number }`
167
+ * }
168
+ * if (event === 'error') {
169
+ * console.log(args) // `args` is narrowed to `Error`
170
+ * }
171
+ * }
172
+ *
173
+ * onEvent('play', { position: 0 })
174
+ * onEvent('error', new Error())
175
+ * ```
176
+ *
177
+ * ---
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * interface Events {
182
+ * play: [data: { position: number }]
183
+ * error: [error: Error]
184
+ * }
185
+ *
186
+ * type OnEventHandler = (...args: MappedArgs<Events>) => void
187
+ *
188
+ * const onEvent: OnEventHandler = (event, args): void => {
189
+ * if (event === 'play') {
190
+ * const [payload] = args
191
+ * console.log(payload.position) // `args` is narrowed to [`{ position: number }`]
192
+ * }
193
+ * if (event === 'error') {
194
+ * const [error] = args // `args` is narrowed to [`Error`]
195
+ * console.log(error)
196
+ * }
197
+ * }
198
+ *
199
+ * onEvent('play', [{ position: 0 }])
200
+ * onEvent('error', [new Error()])
201
+ * ```
202
+ */
203
+ export type MappedArgs<M> = { [K in keyof M]: [event: K, args: M[K]]; }[keyof M];
204
+ export type { RecursivelyUnflatDotKeys, UnflatDotKeys };
package/dist/index.d.mts CHANGED
@@ -1,104 +1,204 @@
1
- //#region src/globals/extenders/json.d.ts
2
- declare class Stringified<T> extends String {
3
- private ___stringified;
4
- }
5
- declare global {
6
- interface JSON {
7
- stringify<T>(value: T, replacer?: (this: {
8
- '': T;
9
- } | T, key: string, value: T) => any, space?: string | number): T extends Date ? string : string & Stringified<T>;
10
- stringify<T>(value: T, replacer?: (T extends object ? (keyof T)[] : (number | string)[]) | null, space?: string | number): T extends Date ? string : string & Stringified<T>;
11
- parse<T, U extends keyof T = keyof T>(text: string | Stringified<T>, reviver?: (key: U, value: T[U]) => any): T;
12
- parse(text: string, reviver?: (key: any, value: any) => any): any;
13
- }
14
- interface Body {
15
- json<T = any>(): Promise<T>;
16
- }
17
- }
18
- //#endregion
19
- //#region src/globals/utils/index.d.ts
20
- declare global {
21
- /**
22
- * Get the Type of the Value of a object/array property.
23
- */
24
- type ValueOf<T> = T[keyof T];
25
- /**
26
- * Get the Type of the Value of object/array specific property.
27
- */
28
- type ValueOfKey<T, P extends keyof T> = T[P];
29
- /**
30
- * Make a specific property optional.
31
- */
32
- type OptionalKey<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
33
- /**
34
- * Make read-only properties of an object recursively writeable.
35
- */
36
- type ReadOnly<T> = keyof T extends never ? T : { readonly [P in keyof T]: Writeable<T[P]>; };
37
- /**
38
- * Make read-only properties of an object recursively writeable.
39
- */
40
- type Writeable<T> = keyof T extends never ? T : { -readonly [P in keyof T]: Writeable<T[P]>; };
41
- /**
42
- * Renames a property while inheriting its type.
43
- */
44
- type RenameKey<T, K extends keyof T, R extends PropertyKey> = Omit<T, K> & { [P in R]: T[K]; };
45
- /** Get the Type of a function arguments. */
46
- type ArgumentTypes<F extends Function> = F extends ((...args: infer A) => any) ? A : never;
47
- /**
48
- * Makes all 1st level properties of a type required.
49
- */
50
- type Full<T> = { [P in keyof T]-?: T[P]; };
51
- /**
52
- * Recursively makes all properties of a type required at all nested levels.
53
- */
54
- type DeepFull<T> = { [P in keyof T]-?: DeepFull<T[P]>; };
55
- /**
56
- * Removes `null` and `undefined` from all properties of a type.
57
- */
58
- type NonNullableFields<T> = { [P in keyof T]: NonNullable<T[P]>; };
59
- /**
60
- * Get the type of the value of merged enums.
61
- *
62
- * @example
63
- *
64
- * ```ts
65
- * enum Mammals {
66
- * Humans,
67
- * Bats,
68
- * Dolphins
69
- * }
70
- *
71
- * enum Reptiles {
72
- * Snakes,
73
- * Alligators,
74
- * Lizards
75
- * }
76
- *
77
- * // merged enums.
78
- * const Animals = { Mammals, Reptiles }
79
- * type Animals = typeof Animals
80
- *
81
- * type Animal = MergedEnumValueType<typeof Animals>
82
- *
83
- * const testAnimal: Animal = 0 as any
84
- *
85
- * switch ( testAnimal ) {
86
- * case Animals.Mammals.Humans:
87
- * case Animals.Mammals.Bats:
88
- * case Animals.Mammals.Dolphins:
89
- * case Animals.Reptiles.Snakes:
90
- * case Animals.Reptiles.Alligators:
91
- * case Animals.Reptiles.Lizards:
92
- * break
93
- * default: {
94
- * const invalid = testAnimal // `testAnimal` is type of never
95
- * }
96
- * }
97
- * ```
98
- */
99
- type MergedEnumValue<T> = { [K in keyof T]: { [K2 in keyof T[K]]: T[K][K2]; }[keyof T[K]]; }[keyof T];
100
- }
101
- //#endregion
102
- //#region src/index.d.ts
103
- export type DoNotUse = never;
104
- //#endregion
1
+ type RequiredKey<T> = { [K in keyof T]-?: object extends Pick<T, K> ? never : K; }[keyof T];
2
+ type Head<K> = K extends `${infer H}.${string}` ? H : never;
3
+ /**
4
+ * Picks the `${P}.*` keys of `T`, stripping the `${P}.` prefix. When `P` is `never`, `T` is returned as is.
5
+ *
6
+ * __INTERNAL__
7
+ */
8
+ type PickDotKeys<T, P extends string> = [P] extends [never] ? T : { [K in keyof T as K extends `${P}.${infer R}` ? R : never]: T[K]; };
9
+ /**
10
+ * When `Deep` is `true`, unflattens plain object values, leaving functions, arrays and primitives untouched.
11
+ *
12
+ * __INTERNAL__
13
+ */
14
+ type UnflatValue<V, Deep extends boolean> = Deep extends false ? V : V extends ((...args: any[]) => unknown) | readonly unknown[] ? V : V extends object ? Unflat<V, Deep> : V;
15
+ /**
16
+ * Turns dotted keys into nested objects. A nested object is required when at least one of its keys is required.
17
+ *
18
+ * __INTERNAL__
19
+ */
20
+ type Unflat<T, Deep extends boolean> = { [K in keyof T as K extends `${string}.${string}` ? never : K]: UnflatValue<T[K], Deep>; } & { [H in Head<Extract<RequiredKey<T>, string>>]: Unflat<PickDotKeys<T, H>, Deep>; } & { [H in Exclude<Head<keyof T>, Head<Extract<RequiredKey<T>, string>>>]?: Unflat<PickDotKeys<T, H>, Deep>; };
21
+ /**
22
+ * Turns dotted keys (`"a.b"?: T`) into nested objects (`a?: { b?: T }`).
23
+ *
24
+ * @template T The object interface to work with.
25
+ * @template K The object interface specific key to work with (`${P}.*`).
26
+ */
27
+ type UnflatDotKeys<T, K extends string = never> = Unflat<PickDotKeys<T, K>, false>;
28
+ /**
29
+ * Same as {@link UnflatDotKeys}, but dotted keys of nested object values are unflattened too.
30
+ *
31
+ * @template T The object interface to work with.
32
+ * @template K The object interface specific key to work with (`${K}.*`).
33
+ */
34
+ type RecursivelyUnflatDotKeys<T, K extends string = never> = Unflat<PickDotKeys<T, K>, true>;
35
+ /**
36
+ * Get the Type of the Value of a object/array property.
37
+ *
38
+ */
39
+ export type ValueOf<T> = T[keyof T];
40
+ /**
41
+ * Get the Type of the Value of object/array specific property.
42
+ *
43
+ */
44
+ export type ValueOfKey<T, P extends keyof T> = T[P];
45
+ /**
46
+ * Make a specific property optional.
47
+ *
48
+ */
49
+ export type OptionalKey<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
50
+ /**
51
+ * Recursively set to `readonly` all properties of the given type/interface.
52
+ *
53
+ * @template T The type/interface where each property will be set to `readonly`.
54
+ */
55
+ export type ReadOnly<T> = keyof T extends never ? T : { readonly [k in keyof T]: ReadOnly<T[k]>; };
56
+ /**
57
+ * Make read-only properties of an object recursively writeable.
58
+ *
59
+ */
60
+ export type Writeable<T> = keyof T extends never ? T : { -readonly [P in keyof T]: Writeable<T[P]>; };
61
+ /**
62
+ * Renames a property while inheriting its type and modifiers (`?` and `readonly`).
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * interface User {
67
+ * // the user name
68
+ * name?: string
69
+ * // the user age
70
+ * age: number
71
+ * }
72
+ *
73
+ * type ModifiedUser = RenameKey<User, 'name', 'username'>
74
+ * // { username?: string, age: number }
75
+ * ```
76
+ */
77
+ export type RenameKey<T, K extends keyof T, R extends PropertyKey> = { [P in keyof T as P extends K ? R : P]: T[P]; };
78
+ /**
79
+ * Get the Type of a function arguments.
80
+ *
81
+ * @deprecated use `Parameters` instead.
82
+ */
83
+ export type ArgumentTypes<F extends Function> = F extends ((...args: infer A) => any) ? A : never;
84
+ /**
85
+ * Recursively convert to optional all properties of the given type/interface.
86
+ *
87
+ * @template T The type/interface where each property will be set as optional.
88
+ */
89
+ export type DeepPartial<T> = T extends ((...args: any[]) => unknown) ? T : T extends object ? { [P in keyof T]?: DeepPartial<T[P]>; } : T;
90
+ /**
91
+ * Makes all 1st level properties of a type required.
92
+ *
93
+ */
94
+ export type Full<T> = { [P in keyof T]-?: T[P]; };
95
+ /**
96
+ * Recursively makes all properties of a type required at all nested levels.
97
+ *
98
+ */
99
+ export type DeepFull<T> = { [P in keyof T]-?: DeepFull<T[P]>; };
100
+ /**
101
+ * Removes `null` and `undefined` from all properties of a type.
102
+ *
103
+ */
104
+ export type NonNullableFields<T> = { [P in keyof T]: NonNullable<T[P]>; };
105
+ /**
106
+ * Get the type of the value of merged enums.
107
+ *
108
+ * @example
109
+ *
110
+ * ```ts
111
+ * enum Mammals {
112
+ * Humans,
113
+ * Bats,
114
+ * Dolphins
115
+ * }
116
+ *
117
+ * enum Reptiles {
118
+ * Snakes,
119
+ * Alligators,
120
+ * Lizards
121
+ * }
122
+ *
123
+ * // merged enums.
124
+ * const Animals = { Mammals, Reptiles }
125
+ * type Animals = typeof Animals
126
+ *
127
+ * type Animal = MergedEnumValueType<typeof Animals>
128
+ *
129
+ * const testAnimal: Animal = 0 as any
130
+ *
131
+ * switch ( testAnimal ) {
132
+ * case Animals.Mammals.Humans:
133
+ * case Animals.Mammals.Bats:
134
+ * case Animals.Mammals.Dolphins:
135
+ * case Animals.Reptiles.Snakes:
136
+ * case Animals.Reptiles.Alligators:
137
+ * case Animals.Reptiles.Lizards:
138
+ * break
139
+ * default: {
140
+ * const invalid = testAnimal // `testAnimal` is type of never
141
+ * }
142
+ * }
143
+ * ```
144
+ */
145
+ export type MergedEnumValue<T> = { [K in keyof T]: { [K2 in keyof T[K]]: T[K][K2]; }[keyof T[K]]; }[keyof T];
146
+ /**
147
+ * Builds a discriminated union of `[event, args]` tuples from an event map.
148
+ *
149
+ * Each key of the given map becomes a tuple whose first element is the event name
150
+ * and whose second element is its payload type. Since the tuples are discriminated
151
+ * by the event name, checking `event` narrows `args` to the matching payload.
152
+ *
153
+ * @template M The event map, where each key is an event name and each value is its payload type.
154
+ *
155
+ * @example
156
+ * ```ts
157
+ * interface Events {
158
+ * play: { position: number }
159
+ * error: Error
160
+ * }
161
+ *
162
+ * type OnEventHandler = (...args: MappedArgs<Events>) => void
163
+ *
164
+ * const onEvent: OnEventHandler = (event, args): void => {
165
+ * if (event === 'play') {
166
+ * console.log(args.position) // `args` is narrowed to `{ position: number }`
167
+ * }
168
+ * if (event === 'error') {
169
+ * console.log(args) // `args` is narrowed to `Error`
170
+ * }
171
+ * }
172
+ *
173
+ * onEvent('play', { position: 0 })
174
+ * onEvent('error', new Error())
175
+ * ```
176
+ *
177
+ * ---
178
+ *
179
+ * @example
180
+ * ```ts
181
+ * interface Events {
182
+ * play: [data: { position: number }]
183
+ * error: [error: Error]
184
+ * }
185
+ *
186
+ * type OnEventHandler = (...args: MappedArgs<Events>) => void
187
+ *
188
+ * const onEvent: OnEventHandler = (event, args): void => {
189
+ * if (event === 'play') {
190
+ * const [payload] = args
191
+ * console.log(payload.position) // `args` is narrowed to [`{ position: number }`]
192
+ * }
193
+ * if (event === 'error') {
194
+ * const [error] = args // `args` is narrowed to [`Error`]
195
+ * console.log(error)
196
+ * }
197
+ * }
198
+ *
199
+ * onEvent('play', [{ position: 0 }])
200
+ * onEvent('error', [new Error()])
201
+ * ```
202
+ */
203
+ export type MappedArgs<M> = { [K in keyof M]: [event: K, args: M[K]]; }[keyof M];
204
+ export type { RecursivelyUnflatDotKeys, UnflatDotKeys };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alessiofrittoli/type-utils",
3
- "version": "1.10.1",
3
+ "version": "2.1.0",
4
4
  "description": "Lightweight TypeScript types utilities",
5
5
  "type": "module",
6
6
  "author": {
@@ -63,30 +63,30 @@
63
63
  },
64
64
  "sideEffects": false,
65
65
  "peerDependencies": {
66
- "@alessiofrittoli/chain-functions": "^2.7.0",
67
- "@alessiofrittoli/node-scripts": "^4.0.0-alpha.5"
66
+ "@alessiofrittoli/chain-functions": "^2.8.0",
67
+ "@alessiofrittoli/node-scripts": "^4.2.0"
68
68
  },
69
69
  "devDependencies": {
70
- "@alessiofrittoli/chain-functions": "^2.7.0",
71
- "@alessiofrittoli/node-scripts": "^4.0.0-alpha.5",
72
- "@alessiofrittoli/package-configs": "^1.6.0",
70
+ "@alessiofrittoli/chain-functions": "^2.8.0",
71
+ "@alessiofrittoli/node-scripts": "^4.2.0",
72
+ "@alessiofrittoli/package-configs": "^1.9.0",
73
73
  "@types/node": "^24.19.1",
74
- "@vitest/coverage-v8": "^5.0.3",
75
- "concurrently": "^9.2.1",
74
+ "@vitest/coverage-v8": "^5.0.5",
75
+ "concurrently": "^10.0.6",
76
76
  "http-server": "^14.1.1",
77
- "oxlint": "^1.86.0",
78
- "prettier": "^3.9.9",
77
+ "oxlint": "^1.89.0",
78
+ "oxlint-tsgolint": "^7.0.2005",
79
+ "prettier": "^3.9.10",
79
80
  "prettier-plugin-sort-imports": "^1.8.11",
80
- "tsdown": "^0.23.0",
81
- "typescript": "^6.0.3",
82
- "vite": "^8.3.2"
81
+ "tsdown": "^0.23.2",
82
+ "typescript": "^6.0.3"
83
83
  },
84
84
  "scripts": {
85
85
  "//1a": "*********************************************************************",
86
86
  "//1b": "******************** DEV - BUILD - LINT - RELEASE *******************",
87
87
  "//1c": "*********************************************************************",
88
88
  "dev": "tsdown --watch",
89
- "build": "pnpm lint && pnpm tsc && pnpm tsdown",
89
+ "build": "pnpm lint && pnpm tsdown",
90
90
  "lint": "oxlint --report-unused-disable-directives --max-warnings 0",
91
91
  "release": "node scripts/release.ts --verbose --npm",
92
92
  "//2a": "*********************************************************************",
@@ -13,4 +13,5 @@ const lastChainLink = () => () => {
13
13
  /** @type {import( '@alessiofrittoli/chain-functions/types' ).ChainFactory<() => void | Promise<void>>} */
14
14
  const chain = [tsSetup, lastChainLink]
15
15
 
16
+ // oxlint-disable-next-line typescript/no-floating-promises
16
17
  Chain.functions(chain)()
@@ -1,14 +1,14 @@
1
- const { addTypesReference } = require( '@alessiofrittoli/node-scripts/postinstall' )
2
- const project = require( '../../package.json' )
1
+ const { addTypesReference } = require('@alessiofrittoli/node-scripts/postinstall')
2
+ const project = require('../../package.json')
3
3
 
4
4
  /** @type {import( '@alessiofrittoli/chain-functions/types' ).ChainLink<() => void | Promise<void>>} */
5
5
  const tsSetup = next => () => {
6
-
7
- addTypesReference( {
6
+ addTypesReference({
8
7
  name: project.name,
9
- } )
8
+ scopes: [`${project.name}/globals`],
9
+ })
10
10
 
11
11
  return next()
12
12
  }
13
13
 
14
- module.exports = tsSetup
14
+ module.exports = tsSetup