Кажется, я не могу утверждать экземпляры string
в Deno:
import {
assertInstanceOf
} from "https://deno.land/[email protected]/testing/asserts.ts";
assertInstanceOf( "foo", string );
Броски:
error: TS2693 [ERROR]: 'string' only refers to a type, but is being used as a value here.
assertInstanceOf( "foo", string );
~~~~~~
at file:///home/jmerelo/Code/my-stackoverflow-examples/js/string-assert.ts:6:26
Достаточно справедливо, давайте попробуем это
assertInstanceOf( "foo", String );
Теперь я в замешательстве:
Uncaught error from ./string-assert.ts FAILED
ERRORS
./string-assert.ts (uncaught error)
error: AssertionError: Expected object to be an instance of "String" but was "string".
Любая идея о том, что было бы правильным типом здесь?
Здесь явно есть обходной путь, чтобы использовать typeof
. Но я хотел бы знать, как решить эту Уловку-22.
Проверка документации не говорит, что вы можете утверждать примитивы. В нем очень явно указано: «Чтобы проверить, является ли объект экземпляром определенного конструктора», который, опять же, примитивы не являются экземплярами конструктора. Явно не выполняет проверку уровня типа TS во время выполнения.
@VLAZ, я думаю, это обходной путь, верно?
В документации также показано, что вы можете создавать свои собственные утверждения. Так что вы, вероятно, можете просто сделать один для примитивных типов.
Связано: Почему instanceof возвращает false для некоторых литералов?
Здесь явно есть обходной путь, чтобы использовать
typeof
. Но я хотел бы знать, как решить эту Уловку-22.
Это не Уловка-22 , а ложная предпосылка . В JavaScript: хотя примитивы в некоторых аспектах выглядят как объекты , они не являются объектами (см. типы данных и структуры данных JavaScript ) — поэтому они не являются полезными операндами для использования с instanceof , потому что оценка всегда будет false
(см. спецификацию):
instanceof
Оператор instanceof проверяет, появляется ли свойство прототипа конструктора где-либо в цепочке прототипов объекта.
Для строк это объясняется далее в статье MDN String
в разделе Строковые примитивы и объекты String.
Ниже приведен пример кода, демонстрирующий, как различать и утверждать, является ли значение строковым литералом или экземпляром объекта String , используя стандартную библиотеку тестирования Deno и определяемые пользователем функции защиты типа.
module.ts
:
import { assert } from "https://deno.land/[email protected]/testing/asserts.ts";
function isStringLiteral(actual: unknown): actual is string {
return typeof actual === "string";
}
function isStringInstance(actual: unknown): actual is String {
return typeof actual === "object" && actual instanceof String;
}
function isAnyString(actual: unknown): actual is string | String {
return isStringLiteral(actual) || isStringInstance(actual);
}
const testCases: [name: string, value: unknown][] = [
["string literal", "foo"],
["string instance", new String("foo")],
["number literal", 42],
["number instance", new Number(42)],
];
console.info("Testing for string types...");
for (const [name, value] of testCases) {
try {
assert(isAnyString(value));
console.info("✅", name);
} catch {
console.error("❌", name);
continue;
}
try {
assert(isStringLiteral(value));
console.info("type:", "literal");
} catch {
assert(isStringInstance(value));
console.info("type:", "instance");
}
}
Выход:
% deno --version
deno 1.30.0 (release, x86_64-apple-darwin)
v8 10.9.194.5
typescript 4.9.4
% deno check module.ts
% echo $?
0
% deno run module.ts
Testing for string types...
✅ string literal
type: literal
✅ string instance
type: instance
❌ number literal
❌ number instance
Скомпилированный JavaScript со встроенным импортом:
// import { assert } from "https://deno.land/[email protected]/testing/asserts.ts";
// ---> Begin inlined imports
// https://deno.land/[email protected]/testing/asserts.ts?source#L19
class AssertionError extends Error {
name = "AssertionError";
constructor(message) {
super(message);
}
}
// https://deno.land/[email protected]/testing/asserts.ts?source#L138
/** Make an assertion, error will be thrown if `expr` does not have truthy value. */
function assert(expr, msg = "") {
if (!expr) {
throw new AssertionError(msg);
}
}
// <--- End inlined imports
function isStringLiteral(actual) {
return typeof actual === "string";
}
function isStringInstance(actual) {
return typeof actual === "object" && actual instanceof String;
}
function isAnyString(actual) {
return isStringLiteral(actual) || isStringInstance(actual);
}
const testCases = [["string literal", "foo"], ["string instance", new String("foo")], ["number literal", 42], ["number instance", new Number(42)]];
console.info("Testing for string types...");
for (const [name, value] of testCases) {
try {
assert(isAnyString(value));
console.info("✅", name);
} catch {
console.error("❌", name);
continue;
}
try {
assert(isStringLiteral(value));
console.info("type:", "literal");
} catch {
assert(isStringInstance(value));
console.info("type:", "instance");
}
}
Разве это не должна быть
"string"
строка с содержимым «строка»? Идентификаторstring
, набранный строчными буквами, не должен ни на что ссылаться во время выполнения. Если в настройках нет чего-то, что делает внешний контекст доступным во время выполнения?String
в верхнем регистре будет относиться к строковому конструктору. И примитивные строки не являются его экземплярами:"hello" instanceof String
естьfalse
.