Почему "экземпляр" в TypeScript дает мне ошибку "Foo" относится только к типу, но используется как значение здесь ".?

Я написал этот код

interface Foo {
    abcdef: number;
}

let x: Foo | string;

if (x instanceof Foo) {
    // ...
}

Но TypeScript дал мне эту ошибку:

'Foo' only refers to a type, but is being used as a value here.

Почему это происходит? Я думал, что instanceof может проверить, имеет ли мое значение данный тип, но TypeScript, похоже, не нравится.

Ответ 1

Что происходит

Проблема в том, что instanceof является конструкцией из JavaScript, а в JavaScript instanceof ожидает значение для правого операнда. В частности, в x instanceof Foo JavaScript выполнит проверку во время выполнения, чтобы увидеть, существует ли Foo.prototype где-нибудь в цепочке прототипов x.

Тем не менее, в TypeScript interface не имеет излучения. Это означает, что ни Foo ни Foo.prototype существуют во время выполнения, поэтому этот код определенно потерпит неудачу.

TypeScript пытается сказать вам, что это никогда не сработает. Foo это просто тип, это не значение вообще!

"Что я могу сделать вместо instanceof?"

Вы можете посмотреть на охрану типов и определяемые пользователем охранники типов.

"Но что, если я просто переключился с interface на class?"

Вы можете испытать желание переключиться с interface на class, но вы должны понимать, что в структурной системе типов TypeScript (где вещи в основном основаны на форме), вы можете создать любой объект, имеющий такую же форму, что и данный класс:

class C {
    a: number = 10;
    b: boolean = true;
    c: string = "hello";
}

let x = new C()
let y = {
    a: 10, b: true, c: "hello",
}

// Works!
x = y;
y = x;

В этом случае у вас есть x и y одинакового типа, но если вы попытаетесь использовать instanceof для одного из них, вы получите противоположный результат для другого. Таким образом, instanceof мало что скажет вам о типе, если вы используете преимущества структурных типов в TypeScript.

Ответ 2

Даниэль Розенвассер мог бы быть прав и моден, но мне хочется внести поправку в его ответ. Можно полностью проверить экземпляр x, см. Фрагмент кода.

Но одинаково легко назначить x = y. Теперь x не будет экземпляром C, поскольку y имеет только форму C.

class C {
a: number = 10;
b: boolean = true;
c: string = "hello";
}

let x = new C()
let y = {
    a: 10, b: true, c: "hello",
}

console.log('x is C? ' + (x instanceof C)) // return true
console.log('y is C? ' + (y instanceof C)) // return false