Este post é o quarto de uma série de 6 posts sobre TypeScript. Veja os detalhes dos posts aqui.
Post anterior: TypeScript – Interfaces
O TypeScript da condição de trabalhar com classes. Uma classe é uma representação de um tipo de objeto. Uma estrutura que descreve o objeto. Assim com uma planta pode ser usada para criar diversas construções, uma única classe pode ser usada para criar quantos objetos forem necessários. Uma classe é composta de atributos (características) e métodos (ações). Vamos criar um exemplo básico, para entender a estrutura básica de uma classe no TypeScript.
class People { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } walk(): void { console.log(this.name + " (" + this.age + ") is walking"); } } window.onload = () => { var peopleJonh: People = new People("Jonh", 20); peopleJonh.walk(); };
No exemplo acima, temos uma classe chamada “People”, que possui dois atributos, “name” do tipo “string”, e “age” do tipo “number”.
Temos um construtor, que pode sua vez, tem dois argumentos, com os nomes e tipos dos atributos. O que diferencia entre as variáveis locais (no contexto do construtor) e os atributos da classe, é a palavra reservada “this”. Ao usar “this.name” por exemplo, estamos informando que a variável local “name” deve passado uma referencia para o atributo de classe “name”. O mesmo vale para variável e atributo “age”.
Também temos um método chamado “walk”, que não tem retorno algum (repare que está denotado como “void”). Já sua implementação, temos um simples “console.log”, para “imprimir” o nome da pessoa, juntamente com a idade no console do navegador.
E por fim, fazemos uso da classe “People”, atribuindo a uma instancia da classe, para variável “peopleJonh”, que corresponde ao mesmo tipo. E invocamos o método “walk”.
Herança entre classes
A Herança possibilita que as classes compartilhem seus atributos e métodos. A herança ajuda a reaproveitar implementações.
Gostaria de recomendar a leitura desse post Programe voltado para a interface e não para a implementação.
Vamos criar um exemplo prático.
class Product { name: string; price: number constructor(name: string, price: number) { this.name = name; this.price = price; } getPriceWithDiscount(): number { var date: Date = new Date(); var discount: number = 0.10; if (date.getDay() == 1 || date.getDay() == 6) discount = 0.12; return this.price - (this.price * discount); } } enum Color { blue, white, red, black }; class Car extends Product { color: Color; constructor(name: string, price: number, color: Color) { super(name, price); this.color = color; } getPriceWithDiscount(): number { return this.price - (this.price * 0.2); } } window.onload = () => { var product: Product = new Product("Table", 100.10); console.log("Preço da mesa", product.getPriceWithDiscount()); var car: Car = new Car("Table", 1000.99, Color.black); console.log("Preço do carro", car.getPriceWithDiscount()); };
No exemplo acima, temos algumas coisas novas. Primeiro vamos ver a classe “Product”. Ela possui dois atributos. “name” do tipo “string”. E “price” do tipo “number”. Tem declarado um construtor, que exige dois argumentos, com os mesmos nome e tipos de atributos da classe.
E possui um método chamado “getPriceWithDiscount”, que deve retornar um “number”. Neste método possui uma implementação simples. O desconto padrão é de 0.10, porem se for sábado ou domingo, o desconto deve ser de 0.12. E por fim, o método que calcula e retorna o preço com desconto.
Logo abaixo, declaramos um enum, chamado “Color”, ele sera utilizado como parâmetro.
Abaixo temos uma classe chamada “Car”, ela por sua vez, é herdeira de “Product”, perceba que utilizamos a palavra chave “extends” seguida da classe “Product”. Esta mesma classe possui um atributo que só faz sentido pra ela “color”, que é do tipo “Color” (nosso enum).
Esta classe também possui um construtor, parecido com o construtor da classe “Product”, com a diferença que tem um parâmetro a mais, chamado “color” do tipo “Color”. Outra novidade é o uso de “super”, este serve para utilizar o construtor da classe base (no caso, a classe “Product”), assim os atributos “name” e “price” ainda serão utilizados. E por fim, no construtor, estamos definindo o valor do atributo “color”.
Ainda na classe “Car”, estamos sobrecarregando o método “getPriceWithDiscount”, ou seja, uma instancia da classe “Car”, não terá a mesma regra definida na classe “Product”. O desconto sera de 0.2, em qualquer dia.
Logo abaixo, fazemos uso das classes “Product” e “Car”. Repare que a assinatura do construtor da classe “Product” é diferente do construtor da classe “Car”. E o calculo de desconto tem diferença, no resultado final.
Visibilidade – public/private
Por padrão as propriedades de uma classe no TypeScript tem o modificador de acesso “public”. Um membro “public” pode ser acessado de qualquer local. Já um membro “private”, é acessível apenas para a própria classe.
class Product { private price: number; constructor(price: number) { this.price = price; } public priceWithDiscount() { return this.price - (this.price * 0.10); } } window.onload = () => { var product: Product = new Product(100.10); console.log("Preço do produto com desconto", product.priceWithDiscount()); };
No exemplo acima, temos uma classe chamada “Product”, que possui apenas uma propriedade “price” do tipo “number”, repare que ela é “private”, ou seja, não pode ser acessada de fora, somente é visível na própria classe. Também temos um construtor que pede um argumento, para definir o valor de “price”. E por fim temos um método “public” chamado “priceWithDiscount”, ele apenas retorna o preço com desconto de 0.10. Se tentarmos acessar a propriedade “price”, uma exceção será lançada. E por fim, fazemos uso da classe “Product” e “imprimimos” o valor do produto com desconto, utilizando o método “priceWithDiscount”.
Static
Um membro estático em uma classe (propriedade ou método) só pode ser acessado em nível de classe, e não em nível de instancia. Vamos modificar o exemplo anterior.
class Product { private price: number; public static discount: number = 0.10; constructor(price: number) { this.price = price; } public priceWithDiscount() { return this.price - (this.price * Product.discount); } } window.onload = () => { var product: Product = new Product(100.10); console.log("Preço do produto com desconto (" + Product.discount + ")", product.priceWithDiscount()); };
No exemplo acima, adicionamos uma propriedade estática, com visibilidade “public”, chamada “discount”, do tipo “number”, que possui o valor 0.10. E acessamos esta propriedade para calcular o desconto, no método “priceWithDiscount” (acessando Product.discount).
Get/Set
O TypeScript permite definir “get” (obter) e “set” (atribuir). Com esses recursos, podemos manipular o valor de uma propriedade, e encapsular / centralizar as regras para obter um valor e definir um valor. Vamos fazer um exemplo bem simples.
class Product { private _name: string; public get name(): string { return this._name; } public set name(name: string) { this._name = name; } } window.onload = () => { var product: Product = new Product(); product.name = "Table"; console.log("Nome do produto", product.name); };
No exemplo acima, temos uma propriedade privada, chamada “_name”, do tipo “string”. E temos dois métodos, um para “get” outro para “set”. O “get” apenas retorna o valor da propriedade “_name”. E o o “set” apenas definir o valor que foi passado como argumento. Repare que ambos tem o mesmo nome. Em ambos, poderíamos ter definimo alguma regra, assim quem utilizasse o get/set estaria sujeito da respeitar.
Continue lendo em TypeScript – Módulos.
Espero ter ajudado!
Até a próxima!