Seção 25 - Expressão Regular
Aula 351 a 355- Introdução / Introdução parte 2 / Introdução parte 3 / Quantificadores e classes de caracteres / intervalos
Regex são padrões utilizados para encontrar,
modificar ou validar determinados padrões em strings.
Temos alguns sites para testar expressões regulares. Um
deles é Regexr. Para
declararmos as regex devemos utilizar as
'/expressão_a_procurar/flag'. Entre alguns
caracteres temos o '.' que funciona como coringa, e o
'[]' que cria um "array" de caracteres que podem ser
combinados com a expressão em sequencia (funciona como
um 'ou'), '\' é o caractere de escape. Entre as flags
temos:
g: global; i: case insensitive; m: multiline; s: single
line; u: unicode; y: sticky.
Podemos fazer seleções de caracteres utilizando vários
grupos de caracteres '[]' para cada ocorrencia. Podemos
repetir essa seleção por quantas vezes for necessário
passando o numero de caracters a verificar entre
'{}'(quantificador). Para selecionar um limite de
seleção podemos colocar no quantificador um valor
inicial e um final separado por ','.
Podemos selecionar qualquer número com a opção '\d'.
Quando fazemos uma seleção podemos quantificar com os
seguinte sinais: '+' seleciona o valor 1 ou mais vezes;
'*' seleciona o valor para 0 ou mais vezes; ? seleciona
o valor para 0 ou 1 vez.
Para selecionar todas as letras maiúsculas podemos
utilizar a opção '[A-Z]' e as minúsculas '[a-z]' ou
'[A-z]' para maiúsculas e minúsculas. A opção '\w'
(word) seleciona todos os caracteres alfanuméricos, e o
underscore.
Um exemplo até aqui:
/[A-Z]{3}-?\d{4}/g
Quando queremos que a string inicie com uma expressão
utilizamos '^'. Qaundo queremos que acabe finalizamos
com '$'. Para que essas opções funcionem em todas as
linhas utilizamos a flag '/m'.
O '|' funciona como 'ou'.
A opção '\b' limita o início o fim de uma expressão.
A expressão abaixo seleciona todas as tags do tipo
<h>, seguidas ou não de textos (id, class, etc) com
texto interno que pode ou não ser precedido por espaços
(\s) e que termina com </h > (usamos o escape (\)
para a '/').
/<th.*?>[\s?\w]+\/h.?>/g
Podemos selecionar grupos de dados para verificar se
eles são repetidos em outra ocorrencia. Fazemos isso
selecionando a expressão com '()' e apontando a
repetição com '\1'. Podemos criar vários grupos e
seleciona-los modificando o número no retorno.
A regex é por natureza "gananciosa" ou seja
tenta pegar tudo o que estiver após um quantificador
('.', '+', '*'). Para torna-la "preguiçosa" utilizamos o
'?' após os quantificadores.
Os caracteres especiais (ç, á, à, é, ã) não são
reconhecidos pelas opções '\w' nas
regex portanto devem ser declarados.
Para negarmos um conjunto de dados utilizamos o '^' no
início do grupo de dados('[]'). Para selecionarmos o
inverso dos caracteres utilizamos o operador maiúsculo
por exemplo '\W', '\D' e '\S'.
Podemos utilizar Expressões Regulares no
Javascript de duas formas:
De forma literal com '/RegEx/', ou
Com o construtor new RegExp().
Temos alguns métodos para trabalhar com RegEx:
.test(str), .exec(str)
e com objetos String:
.replace(regex|str, newStr|fn),
.match(regex) e .search(regex).
Neste exemplo vamos fazer uma lógica com
RegEx para testar um número de CEP. Para isso
utilizamos no Javascript a expressão:
/^(\d{5}-?\d{3})$/.
Com essa expressão fazemos o teste em um função com o
método test() para receber como resultado um
booleano.
Não devemos utilizar a flag 'g' quando utilizamos
RegEx no JS pois ele tem um comportamento
errado. Quando passamos essa flag, o JS tende a
intercalar true e false para expressões verdadeiras.
Resolução do desafio de teste de CPF ou e-mail.
Aula 365 - Resolução DesafioA resolução o professor ficou igual a minha. Só não lembrei que o endereço de e-mail pode ter '.' ou '-', que foi incluido no grupo de caracteres.
Aula 366 - Datas por extenso
Neste exemplo vamos capturar datas em diversos formatos.
Foi criada a seguinte RegEx:
/\d{1,2}\/\d{1,2}(?:\/\d{2,4})?/
Essa expressão seleciona 1 a 2 digitos para dia e mes,
separado por '/', e seleciona um grupo de 2 a 4 digitos
que pode existir ou não (? no final do grupo) e que
ignora o valor do grupo (?: no começo do grupo). Esse
'?:' significa que não será capturado o valor do grupo.
Quando não utilizamos esse sinal o valor do grupo fica
armazenado e quando não existe retorna como
undefined.
Quando criamos os grupos '()' o RegEx recupera
seus valores individualmente.
Para tratarmos esses valores vamos utilizar o método
replace(). Esse método recebe uma
RegEx e executa uma função que recebe diversos
valores como parametro para cada ocorrencia que seja
compatível com a expressão. No nosso exemplo ele
apresenta os seguintes valores:
1: valor recuperado pela RegEx;
2: valor do primeiro grupo;
3: valor do segundio grupo;
4: valor do terceiro grupo;
5: posição anterior a localização da RegEx;
6: toda a string verificada.
Com esses valores recuperados podemos apenas selecionar
os valores e retorna-los pela função.
O método search() recebe uma RegEx e
retorna a posição da primeira ocorrencia (mesmo com a
flag 'g')(OBS: nesse caso não tivemos o erro da flag).
Quando o search() não encontra nenhum resultado
ele retorna '-1'.
O método match() retorna um array com diversas
informações da RegEx semelhante a função do
método replace() porém apenas quando não
utilizamos a flag 'g'. Quando utilizamos a flag 'g' ele
retorna em cada posição do array o objeto encontrado
pela RegEx. O método match() é muito
custoso em recursos do sistema e deve ser evitado.
O método exec() quando executado em modo normal retorna um array com os dados da mesma forma que o método match(). Já com a flag 'g' ele retorna os arrays conforme a execução um após o outro. Isso ocorre pois temos no regex um atributo lastIndex que armazena a posição da última expressão encontrada até que o retorno seja 'null' e o lastIndex receba o valor '0'.
Aula 369 - new RegExp()
Quando precisamos passar um valor de consulta em uma
RegEx que não seja fixo não conseguimos fazer através do
modo literal pois ele não permite a passagem de
variáveis. Nesse caso podemos utilizar o método
RegExp('expressão', 'flag').
Para passarmos as opções com o
RegExp() precisamos adicionar uma '\' aos
operadores (\\d, \\w, etc).
Minha resolução para o desafio de criar um objeto com as opções de uma URI de busca do google separando cada opção como um para chave/valor.
Aula 371 - Resolução Desafio: URINa resolução o professor fez basicamente a mesma ciosa que eu, porém ele facilitou a origem de dados (onde eu quebrei a cabeça para entender). Note que dentro dos grupos da RegEx ('[]') não precisamos escapar os caracteres.
Aula 373 - Exercício proposto: Destacar textoConsegui resolver o desafio. O primeiro problema foi lembrar o método split() passando a RegEx e depois utilizando o join() para remontar o texto. O outro problema foi que estava utilizando o innerHTML e o aplicativo só funcionou depois que passei a utilizar o innerText. Atenção para a seleção dos elementos. Primeiro utilizei o querrySelectorAll porém ele gera uma NodeList que não é manipulável. Mudei para getElementsByTagName() que gera uma HTMLCollection.
Aula 374 - Resolução: Destacar texto
Na resolução dom desafio foi utilizado o método que eu
estava tentando lembrar Array.from() combinado
com o map(). Outro ponto foi a utilização do
innerText ao invés do textContent. No
mais foi utilizado o método replace() em vez do
exec() como eu utilizei.
Todo esse processo entretanto tem ainda um problema.
Quando temos tags HTML no meio do texto pesquisado elas
são sobrepostas pelo valor que retornamos, ou seja,
remove a tag HTML original que estava no meio do texto.
Para ajustar isso vamos utilizar o innerHTML ao
invés do innerText, em seguida criamos um array
para manter o texto original e fazer as mudanças, como
eu tinha pensado em fazer na minha resolução.
Seção 26 - Módulos no node e tooling
Aula 375 - CommonJS
Inicialmente vamos inicializar um projeto npm com o
comando:
$ npm init -y
Após iniciado o projeto é criado o arquivo
package.json que contém as configurações do
projeto nodeJS. Podemos incluir novos frameworks com o
comando:
$ npm install aplication --save-dev
A flag --save-dev (ou -D) instala o módulo apenas
no modo de desenvimento e não será incluido no modo de
produção.
O nodeJS tem alguns módulos nativos. Para utiliza-los
precisamos declara-los em uma variável com o parametro
require('nome_do_modulo').
Um dos módulos é o fs (File System), que
permite a manipulação de arquivos.
Com o módulo fs declarado podemos por exemplo
criar arquivos com o comando:
fs.writeFile('nome_do_arquivo', 'dados_a_gravar',
função_de_callback.
Quando trabalhamos com commonJS utilizamos o
require sempre que formos importar
dependencias.
Para esse projeto instalamos a dependencia
node-emoji.
Podemos criar variáveis e funções em módulos e
exportá-las com o método module.exports. Esse
método exporta um objeto com os dados no formato chave:
valor, ou seja podemos acessar o valor através do nome
da variável criada para importar o módulo seguida de '.'
e o nome da propriedade a ser acessada (que é o nome da
variável que exportamos).
Quando exportamos uma variável com a sintaxe:
module.exports.variável = valor
estamos incluindo em um objeto vazio o par chave/valor.
Esse valor pode ser acessado tanto por
module.exports quanto por exports (os
dois fazem referencia ao mesmo ponto na memória).
Já quando atribuimos ao module.exports um
objeto:
moduel.exports = {variável:
atributo}
teremos o acesso por module.exports, entretanto
o exports terá como valor um objeto vazio '{}'.
Quando passamos uma função através do
module.exports o valor que vamos passar será o
valor retornado pela função do module.exports, e não o
valor executado da função.
Quando utilizamos o CommonJS não temos
problemas com colizão de nomes pois as variáveis de cada
módulo ficam isoladas no escopo local, e os módulos que
são exportados podem ser atribuidos a nomes que serão
acessados apenas no escopo do arquivo que faz a
importação, e seus valores são nomes de atributos
independentes para cada import.
A partir do NodeJs versão 14 é possível trabalhar com
ES15.
Para usar ESM (EcmaScriptModule) no NodeJs precisamos
informar que estamos utilizando esse padrão caso
contrário receberemos a seguinte mensagem de erro:
Warning: To load an ES module, set "type": "module"
in the package.json or use the .mjs extension.
Portanto podemos fazer isso de duas maneiras alterando a
extensão do arquivo ou incluindo o 'type' no
package.json.
Um dos problemas em usar ESM é em relação ao acesso a
duas variáveis do sistema NodeJs que não funcionam nesse
modo. São elas a __dirname e __filename.
Quando temos duas variáveis exportadas que tem o mesmo
nome podemos importa-las com nosmes diferentes, bastando
para isso indicar no momento da importação da seguinte
forma:
import { nome_da_variavel_importada as
nome_da_variavel_local } from
'arquivo_de_origem'
Entendendo a utilização do Babel para fazer o ajuste e a
conversão de código para utilização em plataformas mais
antigas. Fazendo testes no try out do site
Babel.
Após iniciar o projeto Node vamos fazer as instalações
dos pacotes do Babel. Vamos instalar 3 módulos na
versão definida para evitar incompatibilidades. O
comando de instação é o seguinte:
$ npm i --save-dev @babel/[email protected]
@babel/[email protected] @babel/[email protected]
Em seguida criamos dois diretórios src e
dist sendo que o primeiro irá armazenar o código
que nós iremos criar e o segundo o código pronto para
distribuição, ou seja, transpilado com o Babel.
Para fazer a conversão dos arquivos utilizamos o
comando:
$ npx babel origem -d destino
Com esse comando as mudanças ficam estáticas, ou seja
precisamos refazer a operação sempre que alterarmos o
arquivo de origem. Para evitar isso podemos utilizar a
flag -w ou --watch que faz a atualização
automática do código de saida.
Para que o Babel faça as conversões do código digitado
precisamos setar a 'env'. Podemos fazer isso com as pré
definições do Babel através do comando:
$ npx babel src -d dist --presets=@babel/env
Outra forma é criando um arquivo
babel.config.json de configuração onde
podemos especificar quais os navegadores que desejamos
utilizar ou ainda o percentual de compatibilidade que
desejamos. Um exemplo desse arquivo é o que segue:
{
"presets": [
[ "@babel/preset-env",
{ "targets": {
"chrome": "58",
"ie": 11 }
} ] ] }
Em seguida criamos uma rotina mais elaborada no JS com
async/await, fetch e
try/catch. Nesse caso tivemos alguns erros na
criação do arquivo transpilado pois precisamos de outras
configurações no arquivo babel.config.json.
Inserimos essas tres linhas:
"useBuiltIns": "usage",
"modules": false,
"corejs": 3
Essa alteração é para que o código convertido importe as
bibliotecas necessárias para executar as funções criadas
pelo Babel. O modules é para que o código
trabalhe com o import ao invés do
require.
Mesmo assim, com isso ainda não resolvemos o problema de
compatibilidade de navegadores antigos pois como ficou o
código após o ajuste do Babel ainda teremos
incompatibilidades como por exemplo a necessidade de
utilização do module. Para resolver isso vamos
utilizar o WebPack que é um empacotador, que
agrega ao código, tudo que é necessário para que ele
seja executado, sem a necessidade de utilizar arquivos
externos através de require ou import
O Webpack faz uma
organização dos arquivos do projeto e suas dependencias
de modo que tenhamos apenas 1 arquivo de cada tipo,
evitando que na aplicação que vai para produção,
tenhamos diversos arquivos para cada função.
Para instalar o webpack digitamos o seguinte
comando:
$ npm install --save-dev [email protected]
[email protected]
Além da instalação do webpack precisamos
instalar o babel-loader que vai ajudar na
conversão feita pelo webpack. Instalamos com:
$ npm install --save-dev [email protected]
Em seguida criamos o arquivo de configuração do
webpack. A criação desse arquivo é bastante
complicada e precisa ser estudada conforme a
necessidade. Como acho que não vou precisar disso no
futuro não vale a pena descrever aqui. O arquivo a ser
criado é webpack.config.js e ele utiliza entre
outra coisas a propriedade __dirname mencionada
acima, além de RegEx para selecionar arquivos a serem
verificados ou excluidos.
Após isso executamos o webpack com o comando:
$ npx webpack
Com os ajustes feitos no arquivo de configuração ainda
verificamos que faltou instalar outro componente do
babel:
$ npm install
@babel/[email protected]
--save-dev
Após isso uma nova mudança no arquivo de configuração e
novamente executamos o webpack.
A próxima instalação é do core-js que pode ser
visto no import do arquivo gerado no Babel.
$ npm install [email protected]
Em seguida importamos o core-js no arquivo
original. O fetch também não é (ou era) compatível
portanto intalamos uma nova biblioteca axios.
npm install [email protected]
O
axios é
um cliente HTTP baseado em promise e substitui o
fetch e as outras formas de requests HTTP.
Com o arquivo gerado pelo webpack podemos
remover os eval colocando uma nova configuração
no webpack.config que é sugerida no arquivo
gerado: devtool: false. Após isso temos um novo
arquivo sem os métodos eval. Desse modo ao
verificarmos a aplicação no DevTools do Chrome somos
direcionados para a linha de código do arquivo gerado
pelo webpack. Entretanto para fins de depuração
não conseguimos localizar o local da rotina no código
bruto. Para podermos localizar as instruções no código
podemos setar a opção devtool: "source-map".
Esse procedimento gera um arquivo '.map' que mapeia os
arquivos de origem.
PAra reduzi um pouco o tamanho do arquivo gerado pelo
webpack podemos fazer o import do
core-js com a opção '/stable'.
Outra alteração é alterar a configuração do
webpack de mode: "development" para
mode: "production".
Seção 27: Typescript: introdução
Aula 385 - IntroduçãoTypescript é uma linguagem que adiciona tipagem estática ao javascript. Adiciona novas features no javascript que nos permite definir tipos para variáveis, parâmetros, retornos de funções, etc. Seu principal objetivo é evitar mudanças inesperadas no código por alteração no tipo.
Aula 386 / Aula 387 - Preparação e instalação / configuração
Para iniciar um projeto em
Typescript primeiramente inicializamos um
projeto npm. Em seguida instalamos o
typescript com o comando:
$ npm install [email protected] -D
Os arquivos em typescript tem aa extensão
.ts e não saõ intepretados nativamente pelos
motores JS (pode ser executado nativamente pelo
deno).
Para transpilar os arquivos .ts utilizamos
o comando:
$ npx tsc nome_do_arquivo
Caso o typescript esteja instalado globalmente
podemos executar o tsc diretamente sem o
npx.
Quando fazemos alguma ação ilegal no
typescript recebemos uma mensagem de erro e não
conseguimos concluir a transpilação do arquivo.
Podemos ver as opções do tsc com o comando:
npx tsc --help
Precisamos configurar alguma opções para o
typescript e fazemos isso com um arquivo de
configuração. Para criar o aruivo de configuração padrão
do typescript utilizamos o comando:
$ npx tsc --init
Essa opção cria uma arquivo tsconfig.json onde
podemos ajustar as opções do tsc.
Entre os atributos que podemos configurar nesse arquivo
temos o outDir que especifica o diretório de
saída do arquivo .js gerado e rootDir que
especifica a origem do arquivo .ts.
Mesmo com essa configuração do rootDir se
criarmos arquivos .ts fora do diretório
especificado ela fará a conversão. Para especificar
quais os diretórios que devem ser observados para
conversão devemos criar no arquivo de configuração uma
propriedade "include": [diretórios_a_observar].
Podemos declarar os tipos de variável em TS descrevendo
com :tipo_de_variável. Essa declaração não é
obrigatória mas faz parte da sintaxe. Os tipos podem
ser:
string, number, boolean.
O mesmo podemos fazer com arrays declarando
nome_da_variável:tipo_de_variável[].
Podemos criar arrays no modo generic com a
sintaxe
nome_da_variavel: Array<tipo_da_variavel>
Criamos objetos definindo seus atributos e o tipo
esperado para cada um, da seguinte forma:
let nome_do_objeto: {
atributo1: tipo,
atributo2: tipo,
...
atributo_n: tipo
}
Uma vez criados os atributos do objeto, não podemos
fugir da estrutura definida, ou seja, não podemos criar
novos atributos que não tenham sido declarados na
criação do objeto, bem como não podemos criar o objeto
sem as propriedades definidas. Para que um atributo não
seja obrigatório podemos incluir o sinal '?' após o nome
do atributo. Isso não muda o tipo do valor. A ordem que
as propriedades são declaradas não importam.
Podemos criar objetos vazios sem definir nenhum atributo
que aceitaram qualquer valor, porém isso deixa de ser
interessante para a utilização do TS.
As tuplas não existem no JS. As tuplas são
arrays com o numero e tipo de valores definidos, ou
seja, não aceitam mais valores que o especificado, nem
tipos diferentes dos definidos para cada indice.
O enum permite que criemos variáveis fixas no
sistema que podem ser acessadas como propriedades do
objeto criado.
Quando declaramos parametros em uma função sem
especificar o seu tipo, por padrão o TS irá retornar um
erro dizendo que o tipo implícito é any.
Podemos evitar essa mensagem de erro alterando as
configurações do tsconfig.json passando
false no parametro:
"noImplicitAny": false
Nesse caso o tipo dos parametros passará a ser
any (ou seja qualquer coisa). Isso deve ser
evitado pois uma vez que estamos utilizando TS não
queremos esse tipo de situação.
Quando isso acontece, podemos passar para os parametros
o tipo unknown. Nesse caso o erro não será mais
nos parametros mas sim no retorno da função pois ela
espera uma definição do tipo antes do return. Verifique
que o erro é sanado quando fazemos um teste lógico de
typeof nos parametros.
O tipo void é utilizado em funções para indicar
que elas não retornam nenhum valor. Essa função se
executada vai retornar undefined. Podemos
indicar isso na sintaxe da função:
function nome_da_função(param: tipo, param: tipo) :
void { corpo_da_função }
Nesse caso ela não vai aceitar retorno.
Já a função never indica que ela nunca vai
retornar nada (nem undefined).
Temos ainda dois tipos null e
undefined.
Nos union types podemos ter dois tipos em uma
variável, por exemplo number | undefined (quando
declaramos uma variável que é opcional com o sinal de
?). Podemos declarar que uma mesma variável pode ter
dois tipos utilizando o sinal de union '|'. Se,
por exemplo declaramos que determinada variável deve ser
de um tipo | null, ela não pode ter seu valor omitido.
Ela deve ser ou do tipo definido ou necessariamente
null.
Alias funciona como uma forma de facilitar a
atribuição de tipos a variáveis. Quando temos variáveis
com diversos tipos podemos criar um type com
uma estrutura de tipos que pode ser definida para
quaisquer outras variáveis.
Podemos utilizar o alias também para definição
de atributos e tipos de um objeto.
Podemos também passar valores como se fosse o
enum.
O intersection é representado pelo '&' e indica
a interseção ou união de dois conjuntos de tipos. Ele
permite que tenhamos um objeto que agregue ao mesmo
tempo os atributos de dois conjuntos de alias.
Podemos tipar funções definindo os tipos dos parametros
e do retorno, da seguinte forma:
function add(x: number, y: number): number {
return x + y
}
Podemos declarar os tipos em arrow functions e
podemos declarar uma arrow function com os tipos
que recebe uma arrow function com os valores:
const sub: (x: number, y: number) => number = (x,
y) => x - y
Outro exemplo de tipagem de funções é o apresentado na
aula. Primeiro criamos um alias com uma
arrow function que define o tipo de dados a serem
tratados. No exemplo a função irá tratar todos os
elementos de alias 'User', que faz parte de uma
intersection com 'Admin'. Essa função em
pprincípio retornaria um erro pois o elemento a ser
testado não faz parte do 'alias' 'User'. Para podermos
testar todos os 'User' informamos que o parametro 'user'
deve ser tratado as 'UserAdmin'. Dessa forma a
função vai avaliar todos os 'User' independente de eles
fazerem parte da intersection ou estiverem
apenas no grupo 'User'.
Na aula a função foi tipada declarando que o parametro
esperado pela função seria user: User. Na
atribuição da função foi passado o parametro
user: User novamente o que não é necessário pois
quando tipamos a função já foi declarado o parametro.
Esta operação de definir um tipo ou parametro como parte
de outro se chama assertion.
A assertion é geralmente utilizada em
manipulação do DOM. Quando fazemos referencia a um
elemento do DOM com document o TS vai
automaticamente aplicar o sinalizador '?' para dizer que
seu tipo pode ser null, ou seja ele pode não
receber uma resposta. Quando queremos determinar que
existe certeza na resposta podemos dizer que o elemento
deve ser considerado como as(HTML...) ou seja
que ele deve ser interpretado como um elemento do HTML
(as opções são demonstradas pelo VSCode). Quando
definimos como qual elemento HTML ele vai se comportar o
valor do evento monitorado já passa automaticamente a
ser interpretado como um evento.
Para testar arquivos TS no terminal podemos executar o
transpilador com o comando:
$ npx tsc
e em seguida executar os arquivos '.js' gerados com o
node.
Outra opção é instalar o pacote ts-node:
$ npm i [email protected]
Com o ts-node instalado podemos executar os
arquivos '.ts' diretamente com o comando:
$ npx ts-node arquivo.ts
Esse procedimento é um pouco mais demorado mas evita que
se transpile todos os arquivos.
Para testarmos aplicações escritas em TS no browser, sem
fazer a transpialção podemos utilizar o webpack.
Para isso precisamos instalar os seguinte módulos:
$ npm i webpack webpack-cli ts-loader
Não fizemos essa instalação pois precisariamos fazer
todas as configurações do webpack. Ao invés disso
aproveitamos a configuração que já tinhamos do
webpack com o babel e incluimos as
configurações para utilizar o ts-loader.
Instalamos naquele projeto o typescript e
alteramos as denominações dos arquivos para serem
carregados.
Minha resolução para conversor polegadas para centimetro.
Aula 404 / Aula 405- preparação do ambiente / resolução: converter polegadas para centimetros
Explicação sobre o projeto. Informações sobre o SASS, e
inicialização do projeto com npm install e
npm run build.
Quando trabalhamos com TS temos que verificar como os
parametros são passados e qual sua tipagem. Quando
monitoramos o evento diretamente no listener o
this é o elemento monitorado. Entretanto quando
criamos uma função externa o this deixa de fazer
referencia ao elemento e o valor recebido deixa de ser
um event, ambos são interpretados com o tipo
any.
Para que o evento passe a ser considerado como tal,
podemos criar uma alias para a função
especificando que ela recebe como parametro um evento.
Isso não resolve o problema do this. Para
resolver esse problema passamos também no
alias a informação que o this é o
elemento desejado (no caso HTMLInputElement).
Feito isso é apenas a manipulação de string para numero
e numero para string e as funções de fórmulas.
Seção 28: Typescript: Classes
Aula 406 a Aula 411 - Preparação do ambiente / public, readonly e private / extends e protected / classes abstratas / getters e setters / lista de objetos
Nessa aula iniciamos o nodeJS e instalamos os pacotes TS
e ts-node-dev:
$ npm init
$ npm install [email protected] [email protected]
--save-dev
Para executar o ts-node-dev com modo de
atualização automática, utilizamos a flag
--respawn.
No TS basicamente a declaração de classes segue o mesmo
padrão do JS, com a diferença que no TS declaramos os
tipos dos atributos.
Podemos criar as classes do modo mais longo declarando o
tipo do atributo, e passando o valor em parametro no
construtor para o atributo ou podemos passar o atributo
diretamente no parametro do construtor com o
modificador de acesso public nos
parametros do constructor().
Quando declaramos o modificador public podemos
alterar os valores do atributo de qualquer local do
programa. Para impedir que alteremos o valor dos
atributos podemos utilizar o modificador
readonly que só permite ler os valores.
Já o modificador private não permite que
acessemos os atributos nem os modifiquemos fora da
classe. Do mesmo modo o protected não permite o
acesso as propriedades, mas difere do
private quando utilizamos as heranças.
Para visualizar os atributos no modificador
private precisamos criar métodos para que
possamos acessa-los (getters/setters?).
Podemos trabalhar com heranças entre as classes com o
extends. Quando utilizamos o
extends precisamos criar o método
super() dentro do constructor().
Quando temos classes herdadas de classes com o
modificador de acesso private, não temos
acesso a essa propriedade mesmo que na classe que a
extende. Para podermos passar o acesso as classes filhas
devemos alterar o modificador para
protected. Com o modificador
protected, as classes filhas tem acesso aos
atributos da classe pai. Porém quando fazemos isso temos
acesso irrestrito aos atributos da classe pai pela
classe filha, inclusive de alterar seu valor.
Para proteger um atributo do tipo protected de
ser editado pela classe filha podemos indicar a
propriedade readonly junto com a
protected (o modificador
protected deve vir antes do readonly).
Podemos passar esses modificadores também na construção
de classes de modo mais curto.
Podemos definir na criação da classe a propriedade
abstract. Com essa propriedade a classe deixa
de poder ser instanciada e pode apenas ser extendida por
outra classes.
Podemos manipular os atributos de classes
private e protected com os
getters/setters. Essas funções são métodos
criados na classe que permitem a alteração de forma
protegida dos dados da classe. Para isso utilizamos os
operadores get e set para criação dos
métodos.
Podemos criar listas de objetos bastando para tal criar
a propriedade e passar o objeto como argumento. Quando
fazemos isso passando outra classe como argumento o TS
na verdade não espera receber os elementos que pertencem
aquela classe, mas sim objetos que tenham as mesmas
propriedades e métodos da classe base. Dessa forma se
passarmos um objeto novo que possua essas mesmas
características o código será aceito.
Quando criamos arrays(listas) de objetos sem alterar o
modificador podemos alterar seu conteudo simplesmente
atribuindo um novo valor. Podemo evitar isso alterando o
modificador para readonly entretanto ainda
podemos mudar isso atribuindo por exemplo um valor '0'
para o objeto.array_objetos.length. Nessa
situação o array vai passar a ser vazio pois seu tamanho
é '0'. Lembrando que arrays são apontamentos em memória
e não valores, portanto a manipulação de propriedades e
métodos de array podem modificar os dados mesmo com o
modificador readonly.
Para evitar esse tipo de manipulação podemos alterar
essa propriedade para private.
Quando criamos atributos private é comum colocarmos
um '_' na frente do nome do atributo. Alterado o modificador para private caso
criemos um getter no modo convencional esse novo
método vai permitir que façamos as mesmas alterações que
sem o private. Para corrigir isso podemos
passar um spread operator no retorno do
getter que vai retornar uma cópia do array e
não o array original. Entretanto cada vez que ele for
executado será gerada uma nova cópia o que pode
compremeter o uso de memória. Nesse caso criamos um novo
atributo temporário que irá armazenar a cópia dos
valores do array original mas que irá zerar o seu valor
a cada chamada, de modo que não gere diversas cópias do
array em memória. A desvantagem é que teremos dois
arrays no objeto principal (o original e o temporário)
Quando utilizamos o extends em uma classe e
utilizamos o método super() esse método é uma
referencia direta a classe pai. Portanto podemos, por
exemplo, utilizar o comando
super.método_da_classe_pai. Quando criamos uma classe abstract ela não
pode ser instanciada e as classes que extends a
classe pai recebem os seus atributos e métodos. Podemos
criar métodos ou atributos abstratos, e esses métodos
devem ser construidos nas classes filhas.
Quando fazemos a conversão de uma arquivo TS para JS, o
abstract deixa de ser válido, ou seja, uma classe
abstrata no TS pode ser instanciada no JS.
Podemos criar também ao invés de classes
abstract padrões do tipo inteface. O
interface á um 'contrato' que determina para a
classe quais elementos ou estrutura ele deve ter. As
interface não utilizam o extends mas o
implements para serem utilizadas pelas classes
filhas. Esse tipo de elemento não precisa do método
super(), bastanto que se passem os atributos e
métodos na classe implementada.
Podemos ainda utilizar os objetos criados com o
implement para criar objetos sem ser
instanciados com o new.
Podemos implemetar mais de uma classe dom tipo
interface, o que não é possível com o
extends.
Quando geramos o arquivo JS não teremos mais as
interface pois elas só existem em tempo de
compilação e não em runtime. Resumindo as
classes, abstratas ou não são levadas para o código
final que será utilizado em produção. Já as interfaces
não. As interfaces não aceitam modificadores de acesso e
não aceitam implementação de métodos, apenas podemos
declarar que a classe que implementar a interface deverá
implementar também os métodos.
Por fim as interface se assemelham aos
type alias. Se substituirmos o
interface por type teremos o mesmo
resultado. As principais diferenças entre interface e
type alias é que a primeira permite que se criem mais de
uma classe com o mesmo nome, já o type acusa duplicidade
de identificador.
Outra característica é que a interface não
permite criarmos tipos primitivos (declarar o tipo da
variável), e também não aceita a utilização de '|'
(union) ou '&' (intersection). Outra diferença é na
sintaxe de declaração de funções conforme exemplo.