Typescript: O uso da palavra Super()

Qual é a importância da palavra reservada Super()?

Você já deve ter se deparado com a declaração no construtor de classes que recebem um super(). Por que precisamos? Como o usamos? E que tipo de erros recebemos quando não usamos? A palavra-chave super é usada para se referir aos métodos e propriedades definidos dentro da superclasse, mas calma, vamos entender tudo isso em partes iniciando pela herança!

A herança

É um princípio clássico e muito conhecido da Programação Orientada a Objetos que permite a criação de novas classes a partir de outras previamente criadas. Podemos chamar as novas classe de subclasses, ou classes derivadas; e as classes já existentes, que deram origem às subclasses, são chamadas de superclasses, ou classes base.

Então agora temos uma hierarquia de classes, mas o que isso nos ajuda ao desenvolver? Com a herança temos classes mais amplas e classes mais específicas. Uma subclasse herda métodos e atributos de sua superclasse; apesar disso, a subclasse pode escrevê-los novamente para uma forma mais específica de representar o comportamento do método herdado. E o que a herança tem haver com o super? Com ele podemos acessar atributos que são declarados no construtor e métodos que estão na superclasse e usá-los na subclasse. Vamos entender no próximo tópico.

A palavra reservada super() em JavaScript

Em outras linguagens como Java e Ruby a palavra reservada está presente. Em C# temos algo muito parecido com a palavra base:

// C#
using System;
public class Animal {
   public string repColor = "brown";
}

public class Reptile: Animal {
   string repColor = "green";

   public void display() {
      Console.WriteLine("Color: "+base.repColor); // base equivale a super
      Console.WriteLine("Color: "+repColor);
   }
}

// JAVA

public class Pessoa {
    public String nome;
    public String cpf;
    public Date data_nascimento;

    public Pessoa(String _nome, String _cpf, Date _data) {
        this.nome = _nome;
        this.cpf = _cpf;
        this.data_nascimento = _data;
    }
}

public class Aluno extends Pessoa {
    public Aluno(String _nome, String _cpf, Date _data) {
        super(_nome, _cpf, _data);
    }
    public String matricula;
}

// RUBY
class Pessoa
    def initialize(nome)  
        @nome = validarNome(nome)
    end
    def validarNome(nome)
        ...
    end
end

class PessoaFisica < Pessoa
    def initialize(nome, cpf)  
        super(nome)
        @cpf = validarCpf(cpf)
    end
    def validarCpf(cpf)
        ...
    end
end

O JavaScript introduziu as classes no ECMAScript 6 e junto a palavra reservada super(). Isso abriu um leque de possibilidades para se trabalhar com essa linguagem! E já que o TypeScript é um superset do JavaScript, essas features fazem parte dele. Vamos ver um exemplo simples, mas que mostra muito bem o uso do super() dentro do Typescript :

code.png

A classe Animal representa uma superclasse. Outras classes irão se basear nela para se especializar e utilizar seus métodos e atributos. Note que temos na superclasse o atributo name sendo declarado no construtor. Agora vamos criar uma subclasse:

code2.png

O que acontece se apenas tentarmos herdar a classe base sem a palavra reservada super()?

image.png

Temos três tipos de erro. Precisamos chamar o super() dentro do construtor antes de podermos acessar this e isso ocorre porque o objeto não é criado até super() ser chamado. Isso é uma regra! E segundo a documentação do Typescript você é obrigado a seguir dessa maneira:

" What’s more, before we ever access a property on this in a constructor body, we have to call super(). This is an important rule that TypeScript will enforce. - Typescript Documentation "

Para acessar métodos podemos utilizar o this também. Mas o recomendado sempre é usar a palavra super também. Veja o código corrigido:

3.png

Podemos também chamar o super() fora do construtor, dentro de métodos da subclasse e isso é muito útil. Como vemos na imagem acima, o método move da classe Lion consegue chamar super.move(distanceInMeters) da classe Animal.

Podemos executar o código utilizando a extensão Code Runner e com o ts-node instalado no projeto. Veja o resultado:

class Animal {
  public name: string;
  constructor(theName: string) {
    this.name = theName;
  }

  public move(distanceInMeters: number = 0) {
    console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
}

class Lion extends Animal {
  constructor(name: string) {
    super(name)
  }
  move(distanceInMeters = 35) {
    console.log("Run...");
    super.move(distanceInMeters);
  }
}

let simba: Animal = new Lion("Simba");

simba.move(33)

Resultado no console:

image.png

Mas pode estar se perguntando:

Por que isso não acontece automaticamente? Por que o compilador não pode simplesmente chamar super para mim?

Pode ser que aconteça que alguns casos, é necessário realizar algo antes da chamada do super(). Veja a subclasse Lion:

5.png

Veja que não tivemos nenhum erro. Mas é importante entender que existe casos específicos em que isso pode ocorrer.

Fico por aqui neste post e espero ter ajudado a entender melhor o objetivo dessa palavra reservada. Até o próximo! 🚀

Referencias:

ECMA: 262.ecma-international.org/6.0/#sec-super-k..

Documentação Typescript: typescriptlang.org/docs/handbook/classes.htm