Menu

instanceof em TypeScript: narrowing de classes e erros

O instanceof verifica em tempo de execução se um objeto foi criado por uma classe, e o TypeScript estreita a variável para essa classe. Como ele funciona com suas próprias classes e com subclasses de Error, por que não consegue verificar interfaces nem type aliases e onde dá respostas surpreendentes.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

value instanceof SomeClass é uma verificação do JavaScript que roda em tempo de execução: é true quando SomeClass.prototype está na cadeia de protótipos do objeto, o que significa que o objeto foi criado com new SomeClass (ou com uma subclasse). O TypeScript estreita value para SomeClass dentro da verificação.

Use instanceof para objetos criados a partir de classes, e typeof para primitivos. Aqui os dois são necessários: o instanceof separa o Date, e depois o typeof divide o que sobra.

Narrowing das suas próprias classes

O instanceof estreita para a classe no ramo true e a remove no ramo false, então uma union de classes pode ser tratada um membro por vez.

Uma instância de subclasse também passa na verificação da classe pai: se Square extends Rect, então new Square(2) instanceof Rect é true. Teste primeiro a classe mais específica quando os ramos são diferentes.

Subclasses de Error no catch

O uso mais comum do instanceof é no catch. Com strict, o valor capturado é unknown (qualquer coisa pode ser lançada), e o instanceof é como você volta a ter um erro tipado.

Saída:

404: /missing.txt
TypeError: path must be absolute

class X extends Error funciona com instanceof em todo target que o TypeScript 7 suporta (ES2015 e posteriores). O conselho antigo de chamar Object.setPrototypeOf(this, X.prototype) no construtor valia para código compilado para ES5, um target que o TypeScript 7 removeu.

instanceof não funciona com interfaces nem types

Interfaces e type aliases existem só para o compilador. Depois da compilação não há nenhum valor User para comparar, então o TypeScript recusa a verificação:

O compilador mostra index.ts(8,24): error TS2693: 'User' only refers to a type, but is being used as a value here. Há duas soluções. Verifique você mesmo o formato com um type guard, uma função que retorna value is User:

Ou, se é o seu código que cria esses objetos, transforme User em uma classe e crie-os com new; aí o instanceof funciona. A página sobre type guards trata de predicates e assertion functions em detalhe.

O tipo certo não é a instância certa

O TypeScript compara tipos pela estrutura: um objeto literal com os mesmos membros de uma classe pode ser atribuído ao tipo da classe. O instanceof não olha a estrutura. Ele percorre a cadeia de protótipos, e um objeto que nunca foi criado com new falha na verificação mesmo quando o compilador o aceita como aquele tipo.

A última linha importa. structuredClone, JSON.parse(JSON.stringify(...)) e mensagens entre workers retornam objetos simples sem o protótipo da classe, mesmo que o tipo estático deles ainda diga Point. Quando instâncias de classes atravessam uma fronteira dessas, reconstrua-as (new Point(copy.x, copy.y)) antes de depender do instanceof ou dos métodos.

instanceof e primitivos

Primitivos (string, number, boolean...) não são objetos e não têm cadeia de protótipos, então "hi" instanceof String é false. O TypeScript aponta o erro quando consegue: com um valor tipado como string do lado esquerdo, o instanceof gera o erro TS2358, The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter. Use typeof para primitivos.

ValorVerificação instanceofResultado
new Date()instanceof Datetrue
[1, 2]instanceof Arraytrue (mas prefira Array.isArray)
new TypeError("x")instanceof Errortrue (subclasse)
{ x: 1, y: 2 }instanceof Pointfalse (nunca foi construído)
Object.create(null)instanceof Objectfalse (sem protótipo)
"hi"instanceof Stringfalse (primitivo)

Valores de outro realm

O instanceof compara com um objeto construtor específico. Código rodando em outro realm (um iframe, ou um contexto vm do Node) tem o próprio Array, Error e Date, então um array criado lá falha em instanceof Array aqui. O mesmo acontece quando duas cópias de um mesmo pacote npm acabam no node_modules: cada cópia tem a própria classe, e uma instância de uma falha no instanceof contra a outra. Para arrays, Array.isArray funciona entre realms. Para os seus próprios tipos, verificar uma propriedade (um type guard, ou um campo kind) evita o problema por completo.

Perguntas frequentes

Como verificar se um objeto é instância de uma classe no TypeScript?

Use value instanceof ClassName. É uma verificação em tempo de execução (JavaScript puro), e o TypeScript estreita value para ClassName dentro do if. Funciona com classes nativas como Date, Map e Error e também com as suas.

Posso usar instanceof com uma interface no TypeScript?

Não. Interfaces e type aliases são apagados quando o TypeScript compila para JavaScript, então não há nada contra o que comparar em tempo de execução. x instanceof User com uma interface User gera o erro TS2693, "'User' only refers to a type, but is being used as a value here." Verifique as propriedades com uma função type guard, ou transforme User em uma classe se você mesmo cria os objetos.

Por que instanceof retorna false para um objeto do tipo certo?

Os tipos do TypeScript são estruturais: um objeto literal { x: 1, y: 2 } pode ser atribuído a um tipo de classe Point se tiver os mesmos membros. Mas o instanceof verifica a cadeia de protótipos, e o literal nunca foi criado com new Point, então o resultado é false. O mesmo acontece com instâncias de classes que passaram por JSON, structuredClone ou um canal de mensagens, que voltam como objetos simples.

O instanceof funciona com classes de Error personalizadas no TypeScript?

Sim, com qualquer target moderno. class NotFound extends Error {} e depois err instanceof NotFound é true. O antigo problema em que ele retornava false só afetava a saída compilada para ES5, e o TypeScript 7 não suporta mais o target ES5.

Por que "hello" instanceof String é false?

Uma string literal é um primitivo, não um objeto, então não tem cadeia de protótipos para verificar. instanceof String só é verdadeiro para objetos wrapper criados com new String(). O TypeScript rejeita instanceof em um valor tipado como string (TS2358); use typeof value === "string" para primitivos.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR