Skip to content

Commit 5951669

Browse files
committed
errors: introduce ERR_INVALID_OPT_TYPE for options of wrong type
This adds `ERR_INVALID_OPT_TYPE` error that is the equivalent of `ERR_INVALID_ARG_TYPE` for options.
1 parent 7d5a86c commit 5951669

File tree

2 files changed

+119
-106
lines changed

2 files changed

+119
-106
lines changed

doc/api/errors.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,6 +1303,11 @@ An invalid HTTP token was supplied.
13031303

13041304
An IP address is not valid.
13051305

1306+
<a id="ERR_INVALID_OPT_TYPE"></a>
1307+
### `ERR_INVALID_OPT_TYPE`
1308+
1309+
An option of the wrong type was passed in an options object.
1310+
13061311
<a id="ERR_INVALID_OPT_VALUE"></a>
13071312
### `ERR_INVALID_OPT_VALUE`
13081313

lib/internal/errors.js

Lines changed: 114 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,114 @@ const fatalExceptionStackEnhancers = {
686686
}
687687
};
688688

689+
const invalidValueTypeError = (name, expected, actual, valueType) => {
690+
assert(typeof name === 'string', "'name' must be a string");
691+
if (!ArrayIsArray(expected)) {
692+
expected = [expected];
693+
}
694+
695+
let msg = 'The ';
696+
if (valueType !== undefined) {
697+
msg += `"${name}" ${valueType} `;
698+
} else if (name.endsWith(' argument')) {
699+
// For cases like 'first argument'
700+
msg += `${name} `;
701+
} else {
702+
const type = name.includes('.') ? 'property' : 'argument';
703+
msg += `"${name}" ${type} `;
704+
}
705+
msg += 'must be ';
706+
707+
const types = [];
708+
const instances = [];
709+
const other = [];
710+
711+
for (const value of expected) {
712+
assert(typeof value === 'string',
713+
'All expected entries have to be of type string');
714+
if (kTypes.includes(value)) {
715+
types.push(value.toLowerCase());
716+
} else if (classRegExp.test(value)) {
717+
instances.push(value);
718+
} else {
719+
assert(value !== 'object',
720+
'The value "object" should be written as "Object"');
721+
other.push(value);
722+
}
723+
}
724+
725+
// Special handle `object` in case other instances are allowed to outline
726+
// the differences between each other.
727+
if (instances.length > 0) {
728+
const pos = types.indexOf('object');
729+
if (pos !== -1) {
730+
types.splice(pos, 1);
731+
instances.push('Object');
732+
}
733+
}
734+
735+
if (types.length > 0) {
736+
if (types.length > 2) {
737+
const last = types.pop();
738+
msg += `one of type ${types.join(', ')}, or ${last}`;
739+
} else if (types.length === 2) {
740+
msg += `one of type ${types[0]} or ${types[1]}`;
741+
} else {
742+
msg += `of type ${types[0]}`;
743+
}
744+
if (instances.length > 0 || other.length > 0)
745+
msg += ' or ';
746+
}
747+
748+
if (instances.length > 0) {
749+
if (instances.length > 2) {
750+
const last = instances.pop();
751+
msg += `an instance of ${instances.join(', ')}, or ${last}`;
752+
} else {
753+
msg += `an instance of ${instances[0]}`;
754+
if (instances.length === 2) {
755+
msg += ` or ${instances[1]}`;
756+
}
757+
}
758+
if (other.length > 0)
759+
msg += ' or ';
760+
}
761+
762+
if (other.length > 0) {
763+
if (other.length > 2) {
764+
const last = other.pop();
765+
msg += `one of ${other.join(', ')}, or ${last}`;
766+
} else if (other.length === 2) {
767+
msg += `one of ${other[0]} or ${other[1]}`;
768+
} else {
769+
if (other[0].toLowerCase() !== other[0])
770+
msg += 'an ';
771+
msg += `${other[0]}`;
772+
}
773+
}
774+
775+
if (actual == null) {
776+
msg += `. Received ${actual}`;
777+
} else if (typeof actual === 'function' && actual.name) {
778+
msg += `. Received function ${actual.name}`;
779+
} else if (typeof actual === 'object') {
780+
if (actual.constructor && actual.constructor.name) {
781+
msg += `. Received an instance of ${actual.constructor.name}`;
782+
} else {
783+
const inspected = lazyInternalUtilInspect()
784+
.inspect(actual, { depth: -1 });
785+
msg += `. Received ${inspected}`;
786+
}
787+
} else {
788+
let inspected = lazyInternalUtilInspect()
789+
.inspect(actual, { colors: false });
790+
if (inspected.length > 25)
791+
inspected = `${inspected.slice(0, 25)}...`;
792+
msg += `. Received type ${typeof actual} (${inspected})`;
793+
}
794+
return msg;
795+
};
796+
689797
module.exports = {
690798
addCodeToName, // Exported for NghttpError
691799
codes,
@@ -931,112 +1039,9 @@ E('ERR_INVALID_ADDRESS_FAMILY', function(addressType, host, port) {
9311039
this.port = port;
9321040
return `Invalid address family: ${addressType} ${host}:${port}`;
9331041
}, RangeError);
934-
E('ERR_INVALID_ARG_TYPE',
935-
(name, expected, actual) => {
936-
assert(typeof name === 'string', "'name' must be a string");
937-
if (!ArrayIsArray(expected)) {
938-
expected = [expected];
939-
}
940-
941-
let msg = 'The ';
942-
if (name.endsWith(' argument')) {
943-
// For cases like 'first argument'
944-
msg += `${name} `;
945-
} else {
946-
const type = name.includes('.') ? 'property' : 'argument';
947-
msg += `"${name}" ${type} `;
948-
}
949-
msg += 'must be ';
950-
951-
const types = [];
952-
const instances = [];
953-
const other = [];
954-
955-
for (const value of expected) {
956-
assert(typeof value === 'string',
957-
'All expected entries have to be of type string');
958-
if (kTypes.includes(value)) {
959-
types.push(value.toLowerCase());
960-
} else if (classRegExp.test(value)) {
961-
instances.push(value);
962-
} else {
963-
assert(value !== 'object',
964-
'The value "object" should be written as "Object"');
965-
other.push(value);
966-
}
967-
}
968-
969-
// Special handle `object` in case other instances are allowed to outline
970-
// the differences between each other.
971-
if (instances.length > 0) {
972-
const pos = types.indexOf('object');
973-
if (pos !== -1) {
974-
types.splice(pos, 1);
975-
instances.push('Object');
976-
}
977-
}
978-
979-
if (types.length > 0) {
980-
if (types.length > 2) {
981-
const last = types.pop();
982-
msg += `one of type ${types.join(', ')}, or ${last}`;
983-
} else if (types.length === 2) {
984-
msg += `one of type ${types[0]} or ${types[1]}`;
985-
} else {
986-
msg += `of type ${types[0]}`;
987-
}
988-
if (instances.length > 0 || other.length > 0)
989-
msg += ' or ';
990-
}
991-
992-
if (instances.length > 0) {
993-
if (instances.length > 2) {
994-
const last = instances.pop();
995-
msg += `an instance of ${instances.join(', ')}, or ${last}`;
996-
} else {
997-
msg += `an instance of ${instances[0]}`;
998-
if (instances.length === 2) {
999-
msg += ` or ${instances[1]}`;
1000-
}
1001-
}
1002-
if (other.length > 0)
1003-
msg += ' or ';
1004-
}
1005-
1006-
if (other.length > 0) {
1007-
if (other.length > 2) {
1008-
const last = other.pop();
1009-
msg += `one of ${other.join(', ')}, or ${last}`;
1010-
} else if (other.length === 2) {
1011-
msg += `one of ${other[0]} or ${other[1]}`;
1012-
} else {
1013-
if (other[0].toLowerCase() !== other[0])
1014-
msg += 'an ';
1015-
msg += `${other[0]}`;
1016-
}
1017-
}
1018-
1019-
if (actual == null) {
1020-
msg += `. Received ${actual}`;
1021-
} else if (typeof actual === 'function' && actual.name) {
1022-
msg += `. Received function ${actual.name}`;
1023-
} else if (typeof actual === 'object') {
1024-
if (actual.constructor && actual.constructor.name) {
1025-
msg += `. Received an instance of ${actual.constructor.name}`;
1026-
} else {
1027-
const inspected = lazyInternalUtilInspect()
1028-
.inspect(actual, { depth: -1 });
1029-
msg += `. Received ${inspected}`;
1030-
}
1031-
} else {
1032-
let inspected = lazyInternalUtilInspect()
1033-
.inspect(actual, { colors: false });
1034-
if (inspected.length > 25)
1035-
inspected = `${inspected.slice(0, 25)}...`;
1036-
msg += `. Received type ${typeof actual} (${inspected})`;
1037-
}
1038-
return msg;
1039-
}, TypeError);
1042+
E('ERR_INVALID_ARG_TYPE', (name, expected, actual) =>
1043+
invalidValueTypeError(name, expected, actual),
1044+
TypeError);
10401045
E('ERR_INVALID_ARG_VALUE', (name, value, reason = 'is invalid') => {
10411046
let inspected = lazyInternalUtilInspect().inspect(value);
10421047
if (inspected.length > 128) {
@@ -1070,6 +1075,9 @@ E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s', TypeError);
10701075
E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent', TypeError);
10711076
E('ERR_INVALID_HTTP_TOKEN', '%s must be a valid HTTP token ["%s"]', TypeError);
10721077
E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s', TypeError);
1078+
E('ERR_INVALID_OPT_TYPE', (name, expected, actual) =>
1079+
invalidValueTypeError(name, expected, actual, 'option'),
1080+
TypeError);
10731081
E('ERR_INVALID_OPT_VALUE', (name, value) =>
10741082
`The value "${String(value)}" is invalid for option "${name}"`,
10751083
TypeError,

0 commit comments

Comments
 (0)