TypeScript vs Dart - Interfaces
Wiley Marques
Posted on July 31, 2021
Nesse artigo falaremos brevemente sobre a utilização de interfaces em TypeScript e como Dart provê a mesma funcionalidade.
TypeScript
Assim como eu muitas linguagens, TypeScript possui suporte a interfaces. Uma funcionalidade muito utilizada para criarmos contratos onde há a comunicação entre dois ou mais componentes de software.
Para exemplificar, vamos criar uma função em TypeScript. Essa função irá receber um nome e um sobrenome. E retornará o nome inteiro:
function joinNames(firstName: string, lastName: string): string {
return `${firstName} ${lastName}`;
}
const fullName = joinNames('Wiley', 'Marques');
console.log(fullName); // 'Wiley Marques'
Agora, para exemplificar o uso de uma interface, vamos alterar o recebimento de parâmetros na função. Ao invés de receber firstName
e lastName
, vamos passar a receber um objeto contendo essas duas propriedades.
Para isso, criaremos a interface Person
com essas propriedades. Alteraremos a função para receber um objeto com o tipo sendo a interface. E alteraremos a forma de executar a função passando um objeto condizente com a interface:
interface Person {
firstName: string;
lastName: string;
}
function joinNames(person: Person): string {
return `${person.firstName} ${person.lastName}`;
}
const fullName = joinNames({
firstName: 'Wiley',
lastName: 'Marques',
});
console.log(fullName); // 'Wiley Marques'
Criamos a interface e fizemos a função joinNames
recebê-la como parâmetro. Mas interfaces não são usadas apenas para definir as assinaturas de funções. Também são usadas para definir um contrato a ser seguido por classes em um software. Então vamos modificar o exemplo acima para demonstrar essa utilização.
Criaremos uma classe de nome Wiley
para realizar a criação de um objeto. Como o objeto será usado como parâmetro para a função joinNames
, ele deverá respeitar o contrato (ou interface) Person
. Portanto teremos de implementar a interface Person
na classe Wiley
e definir propriedades correspondentes às esperadas pela interface. Tendo o seguinte código:
interface Person {
firstName: string;
lastName: string;
}
class Wiley implements Person { // Implementando a interface `Person`
// Definindo as propriedades requeridas na interface
public firstName: string = 'Wiley';
public lastName: string = 'Marques';
// Podemos adicionar qualquer outra propriedade
public hasChildren: boolean = true;
}
function joinNames(person: Person): string {
return `${person.firstName} ${person.lastName}`;
}
const wiley = new Wiley(); // Criando um objeto `Wiley`
const fullName = joinNames(wiley); // Passando `wiley` para a função
console.log(fullName);
Como podemos ver, apesar de o objeto wiley
ser do tipo Wiley
, podemos utilizá-lo como parâmetro para a função joinNames
. Isso se deve ao fato de a classe Wiley
implementar a interface Person
e a função joinNames
estar preparada para receber qualquer objeto que implemente Person
.
Dart
Agora vejamos como Dart funciona nesse quesito.
Dart possui uma extensa documentação sobre todas as funcionalidades disponíveis na linguagem. Essa documentação pode ser vista em: A tour of the Dart language. Lá podemos ver que em Dart não existe uma forma explícita para criação interfaces. A alternativa ofertada pela linguagem é a utilização de Classes Abstratas.
Em Dart, existe um conceito chamado de Interface Implícita. Basicamente, qualquer classe também é implicitamente uma interface! Portanto, qualquer classe (abstrata ou não) pode ser implementada por outra quando necessário.
Indo ao código e seguindo a recomendação da documentação, a classe abstrata Person
ficaria assim:
abstract class Person {
String get firstName;
String get lastName;
}
Um ponto a se notar aqui é que, além de usarmos uma classe abstrata, foi necessário um
getter
ao invés de uma simples propriedade.
Agora, continuando o exercício, vamos converter o último exemplo da sessão anterior para Dart. Ficando com o seguinte código:
abstract class Person {
String get firstName;
String get lastName;
}
class Wiley implements Person { // Classe `Wiley` implementando classe `Person`
String firstName = 'Wiley';
String lastName = 'Marques';
bool hasChildren = true;
}
String joinNames(Person person) {
return '${person.firstName} ${person.lastName}';
}
void main() {
final wiley = new Wiley();
final fullName = joinNames(wiley);
print(fullName);
}
Assim temos uma Classe Abstrata sendo utilizada como uma definição de contrato.
Bônus
Vamos acrescentar uma curiosidade interessante. Em TypeScript, as classes também possuem interfaces implícitas! Com isso, também é possível definir uma classe abstrata e a utilizarmos como um contrato, semelhante a como fizemos em Dart.
Em código, a classe abstrata ficaria assim:
abstract class Person {
abstract get firstName(): string;
abstract get lastName(): string;
}
E o exemplo inteiro seria:
abstract class Person {
abstract get firstName(): string;
abstract get lastName(): string;
}
class Wiley implements Person {
public firstName: string = 'Wiley';
public lastName: string = 'Marques';
public hasChildren: boolean = true;
}
function joinNames(person: Person): string {
return `${person.firstName} ${person.lastName}`;
}
const wiley = new Wiley();
const fullName = joinNames(wiley);
console.log(fullName);
Ou seja, TypeScript dá suporte às duas formas!
Conclusão
Vimos como implementar interfaces em TypeScript e qual a alternativa proposta por Dart. Além de vermos como TypeScript oferece o mesmo suporte a interfaces implícitas presentes em Dart.
Nos próximos artigos, veremos outras comparações. Obrigado. E até logo!
Posted on July 31, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.