Skip to content

Commit 0e17770

Browse files
JakobJingleheimertargos
authored andcommitted
errors: extract type detection & use in ERR_INVALID_RETURN_VALUE
PR-URL: #43558 Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 3752c5b commit 0e17770

File tree

4 files changed

+187
-30
lines changed

4 files changed

+187
-30
lines changed

lib/internal/errors.js

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,31 @@ const genericNodeError = hideStackFrames(function genericNodeError(message, erro
845845
return err;
846846
});
847847

848+
/**
849+
* Determine the specific type of a value for type-mismatch errors.
850+
* @param {*} value
851+
* @returns {string}
852+
*/
853+
function determineSpecificType(value) {
854+
if (value == null) {
855+
return '' + value;
856+
}
857+
if (typeof value === 'function' && value.name) {
858+
return `function ${value.name}`;
859+
}
860+
if (typeof value === 'object') {
861+
if (value.constructor?.name) {
862+
return `an instance of ${value.constructor.name}`;
863+
}
864+
return `${lazyInternalUtilInspect().inspect(value, { depth: -1 })}`;
865+
}
866+
let inspected = lazyInternalUtilInspect()
867+
.inspect(value, { colors: false });
868+
if (inspected.length > 28) { inspected = `${StringPrototypeSlice(inspected, 0, 25)}...`; }
869+
870+
return `type ${typeof value} (${inspected})`;
871+
}
872+
848873
module.exports = {
849874
AbortError,
850875
aggregateTwoErrors,
@@ -853,6 +878,7 @@ module.exports = {
853878
connResetException,
854879
dnsException,
855880
// This is exported only to facilitate testing.
881+
determineSpecificType,
856882
E,
857883
errnoException,
858884
exceptionWithHostPort,
@@ -1221,25 +1247,8 @@ E('ERR_INVALID_ARG_TYPE',
12211247
}
12221248
}
12231249

1224-
if (actual == null) {
1225-
msg += `. Received ${actual}`;
1226-
} else if (typeof actual === 'function' && actual.name) {
1227-
msg += `. Received function ${actual.name}`;
1228-
} else if (typeof actual === 'object') {
1229-
if (actual.constructor?.name) {
1230-
msg += `. Received an instance of ${actual.constructor.name}`;
1231-
} else {
1232-
const inspected = lazyInternalUtilInspect()
1233-
.inspect(actual, { depth: -1 });
1234-
msg += `. Received ${inspected}`;
1235-
}
1236-
} else {
1237-
let inspected = lazyInternalUtilInspect()
1238-
.inspect(actual, { colors: false });
1239-
if (inspected.length > 25)
1240-
inspected = `${StringPrototypeSlice(inspected, 0, 25)}...`;
1241-
msg += `. Received type ${typeof actual} (${inspected})`;
1242-
}
1250+
msg += `. Received ${determineSpecificType(actual)}`;
1251+
12431252
return msg;
12441253
}, TypeError);
12451254
E('ERR_INVALID_ARG_VALUE', (name, value, reason = 'is invalid') => {
@@ -1321,12 +1330,8 @@ E('ERR_INVALID_RETURN_PROPERTY_VALUE', (input, name, prop, value) => {
13211330
` "${name}" function but got ${type}.`;
13221331
}, TypeError);
13231332
E('ERR_INVALID_RETURN_VALUE', (input, name, value) => {
1324-
let type;
1325-
if (value?.constructor?.name) {
1326-
type = `instance of ${value.constructor.name}`;
1327-
} else {
1328-
type = `type ${typeof value}`;
1329-
}
1333+
const type = determineSpecificType(value);
1334+
13301335
return `Expected ${input} to be returned from the "${name}"` +
13311336
` function but got ${type}.`;
13321337
}, TypeError, RangeError);

test/common/index.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -716,14 +716,15 @@ function invalidArgTypeHelper(input) {
716716
return ` Received function ${input.name}`;
717717
}
718718
if (typeof input === 'object') {
719-
if (input.constructor && input.constructor.name) {
719+
if (input.constructor?.name) {
720720
return ` Received an instance of ${input.constructor.name}`;
721721
}
722722
return ` Received ${inspect(input, { depth: -1 })}`;
723723
}
724+
724725
let inspected = inspect(input, { colors: false });
725-
if (inspected.length > 25)
726-
inspected = `${inspected.slice(0, 25)}...`;
726+
if (inspected.length > 28) { inspected = `${inspected.slice(inspected, 0, 25)}...`; }
727+
727728
return ` Received type ${typeof input} (${inspected})`;
728729
}
729730

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// Flags: --expose-internals
2+
3+
import '../common/index.mjs';
4+
import { strictEqual } from 'node:assert';
5+
import errorsModule from 'internal/errors';
6+
7+
8+
const { determineSpecificType } = errorsModule;
9+
10+
strictEqual(
11+
determineSpecificType(1n),
12+
'type bigint (1n)',
13+
);
14+
15+
strictEqual(
16+
determineSpecificType(false),
17+
'type boolean (false)',
18+
);
19+
20+
strictEqual(
21+
determineSpecificType(2),
22+
'type number (2)',
23+
);
24+
25+
strictEqual(
26+
determineSpecificType(NaN),
27+
'type number (NaN)',
28+
);
29+
30+
strictEqual(
31+
determineSpecificType(Infinity),
32+
'type number (Infinity)',
33+
);
34+
35+
strictEqual(
36+
determineSpecificType(Object.create(null)),
37+
'[Object: null prototype] {}',
38+
);
39+
40+
strictEqual(
41+
determineSpecificType(''),
42+
"type string ('')",
43+
);
44+
45+
strictEqual(
46+
determineSpecificType(Symbol('foo')),
47+
'type symbol (Symbol(foo))',
48+
);
49+
50+
strictEqual(
51+
determineSpecificType(function foo() {}),
52+
'function foo',
53+
);
54+
55+
strictEqual(
56+
determineSpecificType(null),
57+
'null',
58+
);
59+
60+
strictEqual(
61+
determineSpecificType(undefined),
62+
'undefined',
63+
);
64+
65+
strictEqual(
66+
determineSpecificType([]),
67+
'an instance of Array',
68+
);
69+
70+
strictEqual(
71+
determineSpecificType(new Array()),
72+
'an instance of Array',
73+
);
74+
strictEqual(
75+
determineSpecificType(new BigInt64Array()),
76+
'an instance of BigInt64Array',
77+
);
78+
strictEqual(
79+
determineSpecificType(new BigUint64Array()),
80+
'an instance of BigUint64Array',
81+
);
82+
strictEqual(
83+
determineSpecificType(new Int8Array()),
84+
'an instance of Int8Array',
85+
);
86+
strictEqual(
87+
determineSpecificType(new Int16Array()),
88+
'an instance of Int16Array',
89+
);
90+
strictEqual(
91+
determineSpecificType(new Int32Array()),
92+
'an instance of Int32Array',
93+
);
94+
strictEqual(
95+
determineSpecificType(new Float32Array()),
96+
'an instance of Float32Array',
97+
);
98+
strictEqual(
99+
determineSpecificType(new Float64Array()),
100+
'an instance of Float64Array',
101+
);
102+
strictEqual(
103+
determineSpecificType(new Uint8Array()),
104+
'an instance of Uint8Array',
105+
);
106+
strictEqual(
107+
determineSpecificType(new Uint8ClampedArray()),
108+
'an instance of Uint8ClampedArray',
109+
);
110+
strictEqual(
111+
determineSpecificType(new Uint16Array()),
112+
'an instance of Uint16Array',
113+
);
114+
strictEqual(
115+
determineSpecificType(new Uint32Array()),
116+
'an instance of Uint32Array',
117+
);
118+
119+
strictEqual(
120+
determineSpecificType(new Date()),
121+
'an instance of Date',
122+
);
123+
124+
strictEqual(
125+
determineSpecificType(new Map()),
126+
'an instance of Map',
127+
);
128+
strictEqual(
129+
determineSpecificType(new WeakMap()),
130+
'an instance of WeakMap',
131+
);
132+
133+
strictEqual(
134+
determineSpecificType({}),
135+
'an instance of Object',
136+
);
137+
138+
strictEqual(
139+
determineSpecificType(Promise.resolve('foo')),
140+
'an instance of Promise',
141+
);
142+
143+
strictEqual(
144+
determineSpecificType(new Set()),
145+
'an instance of Set',
146+
);
147+
strictEqual(
148+
determineSpecificType(new WeakSet()),
149+
'an instance of WeakSet',
150+
);

test/parallel/test-assert-async.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,9 @@ const invalidThenableFunc = () => {
103103
promises.push(assert.rejects(promise, {
104104
name: 'TypeError',
105105
code: 'ERR_INVALID_RETURN_VALUE',
106+
// FIXME(JakobJingleheimer): This should match on key words, like /Promise/ and /undefined/.
106107
message: 'Expected instance of Promise to be returned ' +
107-
'from the "promiseFn" function but got type undefined.'
108+
'from the "promiseFn" function but got undefined.'
108109
}));
109110

110111
promise = assert.rejects(Promise.resolve(), common.mustNotCall());
@@ -162,7 +163,7 @@ promises.push(assert.rejects(
162163
let promise = assert.doesNotReject(() => new Map(), common.mustNotCall());
163164
promises.push(assert.rejects(promise, {
164165
message: 'Expected instance of Promise to be returned ' +
165-
'from the "promiseFn" function but got instance of Map.',
166+
'from the "promiseFn" function but got an instance of Map.',
166167
code: 'ERR_INVALID_RETURN_VALUE',
167168
name: 'TypeError'
168169
}));

0 commit comments

Comments
 (0)