Atualizado: 02/01/2025

Interfaces - TypeScript

Interfaces de Objetos

Uma interface define as propriedades e métodos que um objeto deve implementar. Em outras palavras, uma interface é a definição de um tipo de dados personalizado, mas sem implementação. Neste caso, as interfaces em TypeScript são semelhantes às interfaces nas linguagens Java e C#. As interfaces são definidas usando a palavra-chave interface. Para começar, vamos definir uma interface simples:

interface IUser {
    id: number;
    name: string;
}

A interface entre chaves define duas propriedades: id, que tem o tipo number, e name, que representa uma string. Agora, vamos usá-la em um programa:

let employee: IUser = {
    id: 1,
    name: "Tom"
};
console.log("id: ", employee.id);
console.log("name: ", employee.name);

Essencialmente, employee é um objeto comum, exceto que ele tem o tipo IUser. Mais precisamente, employee implementa a interface IUser. Essa implementação impõe algumas restrições a employee. Por exemplo, employee deve implementar todas as propriedades e métodos da interface IUser, portanto, ao definir employee, o objeto deve necessariamente incluir as propriedades id e name.

Os parâmetros de métodos e funções também podem representar interfaces:

interface IUser {
    id: number;
    name: string;
}
let employee: IUser = {
    id: 1,
    name: "Alice"
};

function printUser(user: IUser): void {
    console.log("id: ", user.id);
    console.log("name: ", user.name);
}

printUser(employee);

Neste caso, o argumento que é passado para a função deve ser um objeto ou classe que implementa a interface correspondente.

E também é possível retornar objetos de uma interface:

interface IUser {
    id: number;
    name: string;
}
function buildUser(userId: number, userName: string): IUser {
    return { id: userId, name: userName };
}

let newUser = buildUser(2, "Bill");
console.log("id: ", newUser.id);
console.log("name: ", newUser.name);

Propriedades Opcionais e Propriedades Somente Leitura

Ao definir uma interface, podemos especificar algumas propriedades como opcionais usando o ponto de interrogação. Tais propriedades não precisam ser necessariamente implementadas:

interface IUser {
    id: number;
    name: string;
    age?: number;
}
let employee: IUser = {
    id: 1,
    name: "Alice",
    age: 23
};
let manager: IUser = {
    id: 2,
    name: "Tom"
};

A propriedade age está marcada como opcional, portanto, não é necessário incluí-la ao definir um objeto que implementa a interface IUser.

Além disso, uma interface pode conter propriedades somente leitura, cujo valor não pode ser alterado. Tais propriedades são definidas com a palavra-chave readonly:

interface Point {
    readonly x: number;
    readonly y: number;
}
let p: Point = { x: 10, y: 20 };
console.log(p);
// p.x = 5; // Erro - propriedade somente leitura

Definição de Métodos

Além das propriedades, as interfaces podem definir funções:

interface IUser {
    id: number;
    name: string;
    sayWords(words: string): void;
}
let employee: IUser = {
    id: 1,
    name: "Alice",
    sayWords: function(words: string): void {
        console.log(`${this.name} diz "${words}"`);
    }
};

employee.sayWords("Olá, como vai?");
interface IUser {
    id: number;
    name: string;
    sayWords(words: string): void;
}
let employee: IUser = {
    id: 1,
    name: "Alice",
    sayWords: function(words: string): void {
        console.log(`${this.name} diz "${words}"`);
    }
};

employee.sayWords("Olá, como vai?");

Novamente, um objeto que implementa a interface é obrigado a implementar a função definida na interface com o mesmo conjunto de parâmetros e o mesmo tipo de resultado. Neste caso, a função sayWords() recebe uma string como parâmetro e não retorna nada, exibindo uma mensagem no console.

Interfaces de Classes

As interfaces podem ser implementadas não apenas por objetos, mas também por classes. Para isso, utiliza-se a palavra-chave implements:

interface IUser {
    id: number;
    name: string;
    getFullName(surname: string): string;
}

class User implements IUser {
    id: number;
    name: string;
    age: number;
    constructor(userId: number, userName: string, userAge: number) {
        this.id = userId;
        this.name = userName;
        this.age = userAge;
    }
    getFullName(surname: string): string {
        return this.name + " " + surname;
    }
}

let tom = new User(1, "Tom", 23);
console.log(tom.getFullName("Simpson"));

A classe User implementa a interface IUser. Nesse caso, a classe User é obrigada a definir todas as propriedades e métodos que existem em IUser.

Além disso, o objeto tom é tanto um objeto User quanto um objeto IUser:

let tom: IUser = new User(1, "Tom", 23);
// ou
let tom: User = new User(1, "Tom", 23);

Extensão de Interface

O TypeScript permite adicionar novos campos e métodos a uma interface simplesmente declarando a interface com o mesmo nome e definindo nela os campos e métodos necessários. Por exemplo:

interface IUser {
    id: number;
    name: string;
}
interface IUser {
    age: number;
}
let employee: IUser = {
    id: 1,
    name: "Alice",
    age: 31
};

function printUser(user: IUser): void {
    console.log(`id: ${user.id}  name: ${user.name}  age: ${user.age}`);
}

printUser(employee);

Neste caso, a primeira definição da interface IUser contém os campos id e name. A segunda definição da interface inclui a declaração do campo age. Como resultado, um objeto ou classe que implementa essa interface deve definir todos os três campos: id, name e age.

Herança de Interfaces

As interfaces, assim como as classes, podem ser herdadas:

interface IMovable {
    speed: number;
    move(): void;
}
interface ICar extends IMovable {
    fill(): void;
}
class Car implements ICar {
    speed: number;
    move(): void {
        console.log("O carro está se movendo a " + this.speed + " km/h");
    }
    fill(): void {
        console.log("Abastecendo o carro com combustível");
    }
}

let auto = new Car();
auto.speed = 60;
auto.fill();
auto.move();

Após a herança, a interface ICar também terá todas as propriedades e funções definidas em IMovable. Portanto, a classe Car, que implementa a interface ICar, também deve implementar as propriedades e métodos da interface IMovable.

Interfaces de Função

Interfaces de função contêm a definição do tipo de uma função. Elas devem ser implementadas por um objeto que representa uma função desse tipo:

interface FullNameBuilder {
    (name: string, surname: string): string;
}

let simpleBuilder: FullNameBuilder = function (name: string, surname: string): string {
    return "Mr. " + name + " " + surname;
};

let fullName = simpleBuilder("Bob", "Simpson");
console.log(fullName); // Mr. Bob Simpson

Aqui, a interface FullNameBuilder é definida apenas com a assinatura de uma função. Em seguida, é declarada a variável simpleBuilder, que tem o tipo FullNameBuilder e, portanto, deve representar uma função com essa assinatura.

Interfaces de Arrays

Interfaces de arrays descrevem objetos que podem ser acessados por índice, como em arrays:

interface StringArray {
    [index: number]: string;
}

let phones: StringArray;
phones = ["iPhone 7", "HTC 10", "HP Elite x3"];

let myPhone: string = phones[0];
console.log(myPhone);

Aqui, a interface StringArray é definida com a assinatura de um array. Essa assinatura indica que o objeto que implementa StringArray pode ser indexado usando números (tipo number) e deve armazenar elementos do tipo string.

No exemplo acima, o índice é do tipo number, mas também podemos usar o tipo string para indexação:

interface Dictionary {
    [index: string]: string;
}

let colors: Dictionary = {};
colors["red"] = "#ff0000";
colors["green"] = "#00ff00";
colors["blue"] = "#0000ff";

console.log(colors["red"]);

Interfaces Híbridas

As interfaces podem combinar diferentes estilos e podem ser aplicadas tanto à definição de um objeto quanto de uma função:

interface PersonInfo {
    (name: string, surname: string): void;
    fullName: string;
    password: string;
    authenticate(): void;
}

function personBuilder(): PersonInfo {
    let person = <PersonInfo>function (name: string, surname: string): void {
        person.fullName = name + " " + surname;
    };
    person.authenticate = function () {
        console.log(person.fullName + " está se autenticando com a senha " + person.password);
    };
    return person;
}

let tom = personBuilder();
tom("Tom", "Simpson");
tom.password = "qwerty";
tom.authenticate();

O tipo de função definido em uma interface híbrida geralmente atua como um construtor de objetos. Neste caso, esse construtor tem o tipo (name: string, surname: string): void.

A função que representa essa interface (neste exemplo, a função personBuilder) implementa essa função construtora e também pode utilizar outras propriedades e métodos definidos na interface.

Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com