@@ -58,7 +58,225 @@ yarn add @flex-development/errnode@flex-development/errnode
5858
5959## Use
6060
61- ** TODO** : Update documentation.
61+ ``` typescript
62+ import {
63+ createNodeError ,
64+ determineSpecificType ,
65+ ErrorCode ,
66+ type MessageFn ,
67+ type NodeError ,
68+ type NodeErrorConstructor
69+ } from ' @flex-development/errnode'
70+ import type { OneOrMany } from ' @flex-development/tutils'
71+ import assert from ' node:assert'
72+
73+ /**
74+ * Creates an [`ERR_INVALID_ARG_TYPE`][1] message.
75+ *
76+ * [1]: https://nodejs.org/api/errors.html#err_invalid_arg_type
77+ *
78+ * @see https://github.com/nodejs/node/blob/v19.3.0/lib/internal/errors.js#L1197-L1286
79+ *
80+ * @param {string} name - Name of invalid argument or property
81+ * @param {OneOrMany<string>} expected - Expected type(s)
82+ * @param {unknown} actual - Value supplied by user
83+ * @return {string} Error message
84+ */
85+ const msg: MessageFn <[string , OneOrMany <string >, unknown ]> = (
86+ name : string ,
87+ expected : OneOrMany <string >,
88+ actual : unknown
89+ ): string => {
90+ // ensure name is a string
91+ assert (typeof name === ' string' , " 'name' must be a string" )
92+
93+ // ensure expected is an array
94+ if (! Array .isArray (expected )) expected = [expected ]
95+
96+ /**
97+ * Primitive value names.
98+ *
99+ * Sorted by a rough estimate on most frequently used entries.
100+ *
101+ * @const {Set<string>} kTypes
102+ */
103+ const kTypes: Set <string > = new Set ([
104+ ' string' ,
105+ ' function' ,
106+ ' number' ,
107+ ' object' ,
108+ ' Function' ,
109+ ' Object' ,
110+ ' boolean' ,
111+ ' bigint' ,
112+ ' symbol'
113+ ])
114+
115+ /**
116+ * Error message.
117+ *
118+ * @var {string} msg
119+ */
120+ let msg: string = ' The '
121+
122+ // stylize invalid argument name
123+ msg += name .endsWith (' argument' )
124+ ? name
125+ : ` "${name }" ${name .includes (' .' ) ? ' property' : ' argument' } `
126+
127+ // continue building error message
128+ msg += ' must be '
129+
130+ /**
131+ * Names of expected class instances.
132+ *
133+ * @const {string[]} instances
134+ */
135+ const instances: string [] = []
136+
137+ /**
138+ * Names of other expected types.
139+ */
140+ const other: string [] = []
141+
142+ /**
143+ * Names of expected primitive types.
144+ */
145+ const types: string [] = []
146+
147+ // get expected types
148+ for (const value of expected ) {
149+ assert (typeof value === ' string' , ' `expected` should be of type string[]' )
150+
151+ if (kTypes .has (value )) types .push (value .toLowerCase ())
152+ else if (/ ^ ([A-Z ][\d a-z ] * )+ $ / .exec (value )) instances .push (value )
153+ else other .push (value === ' object' ? ' Object' : value )
154+ }
155+
156+ // special case: handle `object` in case other instances are allowed to
157+ // outline the differences between each other
158+ if (instances .length > 0 ) {
159+ /**
160+ * Position of `'object'` in {@linkcode types } .
161+ *
162+ * @const {number} pos
163+ */
164+ const pos: number = types .indexOf (' object' )
165+
166+ // replace 'object' in types with "Object" in instances
167+ if (pos !== - 1 ) {
168+ types .splice (pos , 1 )
169+ instances .push (' Object' )
170+ }
171+ }
172+
173+ // add expected primitive types to error message
174+ if (types .length > 0 ) {
175+ if (types .length > 2 ) {
176+ /**
177+ * Last primitive type name in {@linkcode types } .
178+ *
179+ * @const {string} last_type
180+ */
181+ const last_type: string = types .pop ()!
182+
183+ msg += ` one of type ${types .join (' , ' )}, or ${last_type } `
184+ } else if (types .length === 2 ) {
185+ msg += ` one of type ${types [0 ]} or ${types [1 ]} `
186+ } else {
187+ msg += ` of type ${types [0 ]} `
188+ }
189+
190+ if (instances .length > 0 || other .length > 0 ) msg += ' or '
191+ }
192+
193+ // add expected class instances to error message
194+ if (instances .length > 0 ) {
195+ if (instances .length > 2 ) {
196+ /**
197+ * Last instance type name in {@linkcode instances } .
198+ *
199+ * @const {string} last_instance
200+ */
201+ const last_instance: string = types .pop ()!
202+
203+ msg += ` an instance of ${instances .join (' , ' )}, or ${last_instance } `
204+ } else {
205+ msg += ` an instance of ${instances [0 ]} `
206+ if (instances .length === 2 ) msg += ` or ${instances [1 ]} `
207+ }
208+
209+ if (other .length > 0 ) msg += ' or '
210+ }
211+
212+ // add other expected types to error message
213+ if (other .length > 0 ) {
214+ if (other .length > 2 ) {
215+ /**
216+ * Last other type name in {@linkcode other } .
217+ *
218+ * @const {string} last_other
219+ */
220+ const last_other: string = other .pop ()!
221+
222+ msg += ` one of ${other .join (' , ' )}, or ${last_other } `
223+ } else if (other .length === 2 ) {
224+ msg += ` one of ${other [0 ]} or ${other [1 ]} `
225+ } else {
226+ if (other [0 ]?.toLowerCase () !== other [0 ]) msg += ' an '
227+ msg += ` ${other [0 ]} `
228+ }
229+ }
230+
231+ // end error message
232+ msg += ` . Received ${determineSpecificType (actual )} `
233+
234+ return msg
235+ }
236+
237+ /**
238+ * [`ERR_INVALID_ARG_TYPE`][1] model.
239+ *
240+ * Thrown when an argument of the wrong type was passed to a Node.js API.
241+ *
242+ * [1]: https://nodejs.org/api/errors.html#err_invalid_arg_type
243+ *
244+ * @class
245+ * @implements {NodeError<TypeError>}
246+ */
247+ const ERR_INVALID_ARG_TYPE: NodeErrorConstructor <
248+ TypeErrorConstructor ,
249+ typeof msg
250+ > = createNodeError (ErrorCode .ERR_INVALID_ARG_TYPE , TypeError , msg )
251+
252+ /**
253+ * {@linkcode ERR_INVALID_ARG_TYPE } instance.
254+ *
255+ * @const {NodeError<TypeError>} err
256+ */
257+ const err: NodeError <TypeError > = new ERR_INVALID_ARG_TYPE (
258+ ' path' ,
259+ ' string' ,
260+ null
261+ )
262+
263+ console .error (err )
264+ console .debug (' err instanceof TypeError:' , err instanceof TypeError )
265+ ```
266+
267+ ...running that yields:
268+
269+ ``` zsh
270+ TypeError [ERR_INVALID_ARG_TYPE]: The " path" argument must be of type string. Received null
271+ at new NodeError (file:////home/runner/work/errnode/errnode/src/create-node-error.ts:94:5)
272+ at file:////home/runner/work/errnode/errnode/scratch.ts:201:35
273+ at ModuleJob.run (node:internal/modules/esm/module_job:193:25)
274+ at async Promise.all (index 0)
275+ at async ESMLoader.import (node:internal/modules/esm/loader:533:24)
276+ at async loadESM (node:internal/process/esm_loader:91:5)
277+ at async handleMainPromise (node:internal/modules/run_main:65:12)
278+ err instanceof TypeError: true
279+ ```
62280
63281## API
64282
0 commit comments