Atualizado: 21/06/2025

Este conteúdo é original e não foi gerado por inteligência artificial.

Conversão de Tipos em TypeScript

Anteriormente, discutimos o tema da asserção de tipos (type assertion), onde mencionamos conversões de tipos. Podemos aplicar os mesmos métodos de conversão ao trabalhar com classes e interfaces.

Assim, consideremos a seguinte hierarquia de tipos:

class Person {
  name: string;
  constructor(userName: string) {
    this.name = userName;
  }
}

class Employee extends Person {
  company: string;
  constructor(userName: string, company: string) {
    super(userName);
    this.company = company;
  }
}

Aqui, a classe Employee herda da classe Person. Como objetos do tipo Employee também são objetos do tipo Person, ao definir objetos podemos escrever:

let tom: Person = new Employee("Tom", "Microsoft");

Dessa forma, em qualquer lugar onde uma função recebe um objeto do tipo Person como parâmetro ou retorna um objeto do tipo Person, podemos passar um objeto do tipo Employee:

class Person {
  name: string;
  constructor(userName: string) {
    this.name = userName;
  }
}

class Employee extends Person {
  company: string;
  constructor(userName: string, company: string) {
    super(userName);
    this.company = company;
  }
}

function printPerson(user: Person): void {
  console.log(`Person ${user.name}`);
}

function personFactory(userName: string): Person {
  return new Employee(userName, "not specified");
}

let tom: Person = new Employee("Tom", "Microsoft");
printPerson(tom);

let bob = personFactory("Bob");
printPerson(bob);

Aqui são demonstradas conversões ascendentes, ou seja, conversões de um tipo mais específico para um mais geral, do tipo derivado Employee para o tipo base Person. Elas são realizadas implicitamente, sem a necessidade de escrever código adicional.

Mas há outro tipo de conversão, descendente ou de um tipo mais geral para um mais específico. Por exemplo:

class Person {
  name: string;
  constructor(userName: string) {
    this.name = userName;
  }
}

class Employee extends Person {
  company: string;
  constructor(userName: string, company: string) {
    super(userName);
    this.company = company;
  }
}

let tom: Person = new Employee("Tom", "Microsoft");
console.log(tom.company); // erro - a classe Person não possui a propriedade company

Aqui, a variável tom é do tipo Person, mas na realidade aponta para um objeto do tipo Employee, já que usamos o construtor de Employee para inicializá-la, que define a propriedade company. No entanto, tentar acessar a propriedade company do objeto tom resultará em um erro, pois tom ainda é uma variável do tipo Person, que não possui a propriedade company.

Para resolver essa situação, precisamos converter explicitamente o objeto tom para o tipo Employee:

let tom: Person = new Employee("Tom", "Microsoft");

let tomEmployee: Employee = <Employee>tom; // conversão para o tipo Employee
console.log(tomEmployee.company);

// ou assim
console.log((<Employee>tom).company);

A expressão <Tipo> variável permite converter uma variável para o tipo especificado entre os sinais de menor e maior.

Outra maneira de realizar a conversão explícita de tipos é usando o operador as:

let tom: Person = new Employee("Tom", "Microsoft");

let tomEmployee: Employee = tom as Employee; // conversão para o tipo Employee
console.log(tomEmployee.company);

// ou assim
console.log((tom as Employee).company);

Interfaces em Conversões de Tipos

Tudo o que foi dito sobre a conversão de classes também é válido para a conversão de interfaces. No entanto, há algumas particularidades. Suponha que temos uma interface IPerson, que não está relacionada com as classes Person e Employee e não é implementada por elas:

interface IPerson {
  name: string;
}
class Person {
  name: string;
  constructor(userName: string) {
    this.name = userName;
  }
}

class Employee extends Person {
  company: string;
  constructor(userName: string, company: string) {
    super(userName);
    this.company = company;
  }
}
function printPerson(user: IPerson): void {
  console.log(`IPerson ${user.name}`);
}

let tom: Person = new Employee("Tom", "Microsoft");
printPerson(tom);

printPerson({ name: "Sam" });
// printPerson({ name: "Bob", company: "Microsoft" }); // erro

A função printPerson recebe um objeto da interface IPerson como parâmetro:

let tom: Person = new Employee("Tom", "Microsoft");
printPerson(tom);

printPerson({ name: "Sam" });
// printPerson({ name: "Bob", company: "Microsoft" }); // erro

Nem a classe Person nem a classe Employee implementam a interface IPerson, mas podemos usá-las, pois possuem todas as propriedades que a interface IPerson define (neste caso, apenas a propriedade name).

O objeto { name: "Sam" } também é compatível com a interface, pois possui a propriedade name. No entanto, ao passar o objeto { name: "Bob", company: "Microsoft" }, recebemos um erro, já que ele expande as capacidades de IPerson ao adicionar a propriedade company e não corresponde diretamente à interface IPerson. Mas mesmo nesse caso, podemos utilizá-lo aplicando a conversão de tipos:

printPerson({ name: "Bob", company: "Microsoft" } as IPerson);

O Operador instanceof

O operador instanceof permite verificar se um objeto pertence a uma classe específica:

let tom = new Employee("Tom", "Microsoft");
if (tom instanceof Person) {
  console.log("Tom is a Person");
} else {
  console.log("Tom is not a Person");
}