2019-02-04 21:56:24 +02:00
# clean - code - typescript [ ! [ Tweet ] ( https : //img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Clean%20Code%20Typescript&url=https://github.com/labs42io/clean-code-typescript)
2019-01-26 10:52:23 +02:00
2019-02-13 11:18:45 -03:00
Conceitos de _Código Limpo_ adaptados para TypeScript .
Inspirado em [ clean - code - javascript ] ( https : //github.com/ryanmcdermott/clean-code-javascript)
# # Conteúdos
2019-02-13 11:22:04 -03:00
1 . [ Introdução ] ( # introdução )
2 . [ Variáveis ] ( # variáveis )
3 . [ Funções ] ( # funções )
4 . [ Objetos e Estruturas de dados ] ( # objetos - e - estruturas - de - dados )
2019-02-13 11:18:45 -03:00
5 . [ Classes ] ( # classes )
6 . [ SOLID ] ( # solid )
2019-02-13 11:22:04 -03:00
7 . [ Testando ] ( # testando )
8 . [ Concorrência ] ( # concorrência )
9 . [ Tratamento de erros ] ( # tratamento - de - erros )
10 . [ Formatação ] ( # formatação )
11 . [ Comentários ] ( # comentários )
2019-02-13 11:18:45 -03:00
# # Introdução
2019-01-29 09:44:59 +02:00
2019-01-26 10:52:23 +02:00
! [ Humorous image of software quality estimation as a count of how many expletives
2019-02-05 13:18:08 +00:00
you shout when reading code ] ( https : //www.osnews.com/images/comics/wtfm.jpg)
2019-01-26 10:52:23 +02:00
2019-02-13 11:18:45 -03:00
Principios da engenharia de software , do livro de Robert C . Martin
[ _Código Limpo_ ] ( https : //www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), para TypeScript. Isto não é um style guide.
É um guia para desenvolver software [ legível , reutilizavel , e refatorável ] ( https : //github.com/ryanmcdermott/3rs-of-software-architecture) em TypeScript.
2019-01-26 10:52:23 +02:00
2019-02-13 11:18:45 -03:00
Nem todos principios contidos aqui tem de ser seguidos estritamente ,
e muito menos irão ser universalmente aprovados .
Estes são apenas guias e nada mais , mas que foram codificados durante muito
anos por experiências coletivas dos autores de _Código Limpo_ .
2019-01-26 10:52:23 +02:00
2019-02-13 11:18:45 -03:00
Nosso trabalho de engenharia de software tem aproximadamente 50 anos de idade ,
e ainda estamos aprendendo muito . Quando arquitetura de software for
algo antigo como uma arquitetura em si , talvez então teremos regras mais
rígidas para serem seguidas . Por enquanto , deixe esses guias servirem como
referências pelo qual podemos avaliar a qualidade do código JavaScript que
você e seu time produz .
2019-01-26 10:52:23 +02:00
2019-02-13 11:18:45 -03:00
Mais uma coisa : Saber de tudo isso não vai te tornar um melhor desenvolvedor
imediatamente , e trabalhar com isso por muitos anos não significa que você
não irá cometer erros . Todo pedaço de código começa como um rascunho , como
argila se moldando para sua forma final . E finalmente , cortamos fora as
imperfeições quando revemos isso com nossos parceiros . Não se deixe abalar
pelos primeiros rascunhos que precisam de melhorias . Mande ver no seu código ao
invés disso !
2019-01-26 10:52:23 +02:00
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - content ) * *
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
# # Variáveis
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
# # # Use nomes significantes em suas variáveis
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
Diferencie os nomes de tal forma que o leitor saiba as diferença entre eles
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
function between < T > ( a1 : T , a2 : T , a3 : T ) {
2019-01-28 17:55:17 +02:00
return a2 <= a1 && a1 <= a3 ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
function between < T > ( value : T , left : T , right : T ) {
2019-01-28 17:55:17 +02:00
return left <= value && value <= right ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
# # # Use nomes pronunciáveis
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
Se você não consegue pronunciar sua variável , você não consegue argumentar sem
parecer um idiota .
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
class DtaRcrd102 {
private genymdhms : Date ;
private modymdhms : Date ;
private pszqint = '102' ;
2019-01-28 17:55:17 +02:00
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
class Customer {
private generationTimestamp : Date ;
private modificationTimestamp : Date ;
private recordId = '102' ;
2019-01-28 17:55:17 +02:00
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 18:18:27 +02:00
2019-02-13 11:18:45 -03:00
# # # Use o mesmo vocabulário para o mesmo tipo de variável
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
function getUserInfo ( ) : User ;
function getUserDetails ( ) : User ;
function getUserData ( ) : User ;
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
function getUser ( ) : User ;
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
# # # Use nomes fáceis de pesquisar
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
Nós vamos ler mais código do que escrever . É importante que o código que escrevemos seja legível e fácil de achar . Ao não nomear variáveis que acabam sendo inúteis para entender nosso programa , machucamos nossos leitores . Faça seus nomes pesquisáveis . Ferramentas como [ TSLint ] ( https : //palantir.github.io/tslint/rules/no-magic-numbers/) podem te ajudar a identificar constantes sem nome.
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
// What the heck is 86400000 for?
setTimeout ( restart , 86400000 ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
// Declare them as capitalized named constants.
const MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000 ;
setTimeout ( restart , MILLISECONDS_IN_A_DAY ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
# # # Use variáveis explicativas
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
2019-02-06 09:57:52 +02:00
declare const users : Map < string , User > ;
2019-01-28 17:55:17 +02:00
for ( const keyValue of users ) {
// iterate through users map
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
2019-02-06 09:57:52 +02:00
declare const users : Map < string , User > ;
2019-01-28 17:55:17 +02:00
for ( const [ id , user ] of users ) {
// iterate through users map
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
# # # Evite mapear mentalmente
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
Explicito é melhor que implito .
_Clareza é um Rei . _
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
const u = getUser ( ) ;
const s = getSubscription ( ) ;
const t = charge ( u , s ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
const user = getUser ( ) ;
const subscription = getSubscription ( ) ;
const transaction = charge ( user , subscription ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
# # # Não adicione contextos desnecessários
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
If your class / object name tells you something , don ' t repeat that in your variable name .
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
Se o nome da sua classe / objeto expressa algo , não repita isso no nome da variável .
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
2019-02-05 14:22:02 +02:00
type Car = {
2019-01-28 17:55:17 +02:00
carMake : string ;
carModel : string ;
carColor : string ;
2019-02-13 11:18:45 -03:00
} ;
2019-01-28 17:55:17 +02:00
2019-02-05 14:22:02 +02:00
function print ( car : Car ) : void {
2019-02-13 11:18:45 -03:00
console . log ( ` ${ this . carMake } ${ this . carModel } ( ${ this . carColor } ) ` ) ;
2019-01-28 17:55:17 +02:00
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
2019-02-05 14:22:02 +02:00
type Car = {
2019-01-28 17:55:17 +02:00
make : string ;
model : string ;
color : string ;
2019-02-13 11:18:45 -03:00
} ;
2019-01-28 17:55:17 +02:00
2019-02-05 14:22:02 +02:00
function print ( car : Car ) : void {
2019-02-13 11:18:45 -03:00
console . log ( ` ${ this . make } ${ this . model } ( ${ this . color } ) ` ) ;
2019-01-28 17:55:17 +02:00
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
# # # Use argumentos padrões ao invés de encadear condicionais
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
Argumentos padrões são normalmente mais limpos que condicionais .
2019-01-28 17:55:17 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
2019-02-05 17:21:31 +02:00
function loadPages ( count? : number ) {
2019-02-05 14:22:02 +02:00
const loadCount = count !== undefined ? count : 10 ;
// ...
2019-01-28 17:55:17 +02:00
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
function loadPages ( count : number = 10 ) {
2019-02-05 14:22:02 +02:00
// ...
2019-01-28 17:55:17 +02:00
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-28 17:56:21 +02:00
2019-02-13 11:18:45 -03:00
# # Funções
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Argumentos de funções ( 2 ou menos , idealmente )
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Limitar a quantidade de parametros de uma função é incrivelmente importantante
porque isso torna sua função fácil de testar .
Ter mais de três de leva em uma explosão onde você tem que testar vários
casos diferentes , com argumentos separados .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Um ou dois argumentos é o caso ideal , e três deve ser evitado se possível .
Algo além disso deve ser deixado de lado .
Usualmente , se você tem mais de dois argumentos , suas funções estão tentando fazer
coisas demais .
Nos casos que não estão , na maior parte do tempo um objeto irá ser o suficiente como argumento .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Considere usar objetos caso sinta necessidade de enviar muitos argumentos .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Para deixar explicitos quais propriedades suas funções esperam , você pode usar
[ desestruturação ] ( https : //basarat.gitbooks.io/typescript/docs/destructuring.html).
Aqui vão algumas vantagens :
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
1 . Quando alguém olhar a assinatura da função , imediatamente será claro quais propriedades estão sendo usadas .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
2 . Desestruturação também clone os valores primitivos especificados do objeto passado como argumento para a função . Isso ajuda a evitar efeitos colaterais . Nota : Objetos e Arrays que são desestruturados do objeto _argument_ não são clonados .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
3 . TypeScript irá te avisar quando haver propriedades não utilizadas , o que seria impossivel sem usar desestruturação .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
function createMenu (
title : string ,
body : string ,
buttonText : string ,
cancellable : boolean
) {
2019-01-31 10:18:41 +02:00
// ...
}
createMenu ( 'Foo' , 'Bar' , 'Baz' , true ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
function createMenu ( options : {
title : string ;
body : string ;
buttonText : string ;
cancellable : boolean ;
} ) {
2019-01-31 10:18:41 +02:00
// ...
}
createMenu ( {
title : 'Foo' ,
body : 'Bar' ,
buttonText : 'Baz' ,
cancellable : true
} ) ;
` ` `
2019-02-13 11:18:45 -03:00
Você ainda pode aumentar a legibilidade ao utilizar os [ type aliases ] ( https : //www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases) do TypeScript.
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
type MenuOptions = {
title : string ;
body : string ;
buttonText : string ;
cancellable : boolean ;
} ;
2019-01-31 10:18:41 +02:00
function createMenu ( options : MenuOptions ) {
// ...
}
createMenu ( {
title : 'Foo' ,
body : 'Bar' ,
buttonText : 'Baz' ,
cancellable : true
} ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Funções devem fazer somente uma coisa
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Esta é , de longe , a regra mais importante da engenharia de software . Quando funções fazem mais de uma coisa , elas são mais difíceis de compor , testar e pensar sobre . Quando você consegue isolar a função para apenas uma ação , elas podem ser refatoradas fácilmente e teu código será fácilmente lido . Se você ignorar todo o resto deste guia além dessa dica , você já estará a frente de vários outros desenvolvedores .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
function emailClients ( clients : Client ) {
2019-02-13 11:18:45 -03:00
clients . forEach ( client = > {
2019-01-31 10:18:41 +02:00
const clientRecord = database . lookup ( client ) ;
if ( clientRecord . isActive ( ) ) {
email ( client ) ;
}
} ) ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
function emailClients ( clients : Client ) {
clients . filter ( isActiveClient ) . forEach ( email ) ;
}
function isActiveClient ( client : Client ) {
const clientRecord = database . lookup ( client ) ;
return clientRecord . isActive ( ) ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Nomes das funções devem dizer o que elas fazem
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
function addToDate ( date : Date , month : number ) : Date {
// ...
}
const date = new Date ( ) ;
// It's hard to tell from the function name what is added
addToDate ( date , 1 ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
function addMonthToDate ( date : Date , month : number ) : Date {
// ...
}
const date = new Date ( ) ;
addMonthToDate ( date , 1 ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Funções devem estar em apenas um nível de abstração
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Quando você tem mais de um nível de abstração possívelmente sua função está fazendo coisa demais . Dividir suas funções desencadeia em código reusável e fácil de testar .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-06 09:57:52 +02:00
function parseCode ( code : string ) {
2019-02-13 11:18:45 -03:00
const REGEXES = [
/* ... */
] ;
2019-01-31 10:18:41 +02:00
const statements = code . split ( ' ' ) ;
const tokens = [ ] ;
2019-02-13 11:18:45 -03:00
REGEXES . forEach ( regex = > {
statements . forEach ( statement = > {
2019-01-31 10:18:41 +02:00
// ...
} ) ;
} ) ;
const ast = [ ] ;
2019-02-13 11:18:45 -03:00
tokens . forEach ( token = > {
2019-01-31 10:18:41 +02:00
// lex...
} ) ;
2019-02-13 11:18:45 -03:00
ast . forEach ( node = > {
2019-01-31 10:18:41 +02:00
// parse...
} ) ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
const REGEXES = [
/* ... */
] ;
2019-01-31 10:18:41 +02:00
2019-02-06 09:57:52 +02:00
function parseCode ( code : string ) {
2019-01-31 10:18:41 +02:00
const tokens = tokenize ( code ) ;
const syntaxTree = parse ( tokens ) ;
2019-02-13 11:18:45 -03:00
syntaxTree . forEach ( node = > {
2019-01-31 10:18:41 +02:00
// parse...
} ) ;
}
2019-02-06 09:57:52 +02:00
function tokenize ( code : string ) : Token [ ] {
2019-01-31 10:18:41 +02:00
const statements = code . split ( ' ' ) ;
2019-02-06 09:57:52 +02:00
const tokens : Token [ ] = [ ] ;
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
REGEXES . forEach ( regex = > {
statements . forEach ( statement = > {
tokens . push ( /* ... */ ) ;
2019-01-31 10:18:41 +02:00
} ) ;
} ) ;
return tokens ;
}
function parse ( tokens : Token [ ] ) : SyntaxTree {
2019-02-06 09:57:52 +02:00
const syntaxTree : SyntaxTree [ ] = [ ] ;
2019-02-13 11:18:45 -03:00
tokens . forEach ( token = > {
syntaxTree . push ( /* ... */ ) ;
2019-01-31 10:18:41 +02:00
} ) ;
return syntaxTree ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Remove código duplicado
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Faça o seu melhor para evitar código duplicado .
Código duplicado é ruim pois significa que há mais de um lugar para ser alterado se houver alguma mudança na lógica .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Imagine que você tem um restaurante que mantém uma lista do seu inventário : todos seus tomates , cebolas , alho , pimentas , etc .
Se você tem multiplas listas que contém esses dados , então todas irão ser modificadas quando você servir um prato com tomates , por exemplo .
Se você tem apenas uma lista , então este será o ú nico lugar a ser alterado .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Algumas vezes você tem códigos duplicados porque há duas ou mais coisas diferentes , mas que compartilham muito em comum , mas suas diferenças os forçam a ter duas ou mais funções separadas que fazem muito das mesmas coisas . Remover código duplicado significa criar uma abstração que pode lidar com essas diferenças com apenas uma função / módulo / classe .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Ter sua abstração do jeito certo é algo crítico , por isso você deve seguir os principios [ SOLID ] ( # solid ) . Más abstrações podem ser pior que código duplicado , então tome cuidado ! Com isto dito , se você pode fazer uma boa abstração , faça ! Não repita você mesmo , ou então você se encontrará atualizando vários lugares toda vez que desejar alterar uma coisa .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
function showDeveloperList ( developers : Developer [ ] ) {
2019-02-13 11:18:45 -03:00
developers . forEach ( developer = > {
2019-01-31 10:18:41 +02:00
const expectedSalary = developer . calculateExpectedSalary ( ) ;
const experience = developer . getExperience ( ) ;
const githubLink = developer . getGithubLink ( ) ;
const data = {
expectedSalary ,
experience ,
githubLink
} ;
render ( data ) ;
} ) ;
}
function showManagerList ( managers : Manager [ ] ) {
2019-02-13 11:18:45 -03:00
managers . forEach ( manager = > {
2019-01-31 10:18:41 +02:00
const expectedSalary = manager . calculateExpectedSalary ( ) ;
const experience = manager . getExperience ( ) ;
const portfolio = manager . getMBAProjects ( ) ;
const data = {
expectedSalary ,
experience ,
portfolio
} ;
render ( data ) ;
} ) ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
class Developer {
// ...
getExtraDetails() {
return {
2019-02-13 11:18:45 -03:00
githubLink : this.githubLink
} ;
2019-01-31 10:18:41 +02:00
}
}
class Manager {
// ...
getExtraDetails() {
return {
2019-02-13 11:18:45 -03:00
portfolio : this.portfolio
} ;
2019-01-31 10:18:41 +02:00
}
}
function showEmployeeList ( employee : Developer | Manager ) {
2019-02-13 11:18:45 -03:00
employee . forEach ( employee = > {
const expectedSalary = developer . calculateExpectedSalary ( ) ;
const experience = developer . getExperience ( ) ;
2019-01-31 10:18:41 +02:00
const extra = employee . getExtraDetails ( ) ;
const data = {
expectedSalary ,
experience ,
2019-02-13 11:18:45 -03:00
extra
2019-01-31 10:18:41 +02:00
} ;
render ( data ) ;
} ) ;
}
` ` `
2019-02-13 11:18:45 -03:00
Você deve ser duro quando o assunto for código duplicado . As vezes há uma troca entre código duplicado e complexidade aumentada quando introduz abstrações desnecessárias . Quando duas implementações de módulos diferentes se parecem bastante mas vivem em diferentes lugares , código duplicado pode ser aceitável e preferível à extrair para um código comum entre os lugares . Nesse caso , o código que seria extraido iria criar uma dependência indireta entre os dois módulos .
2019-02-05 14:22:02 +02:00
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Define objetos padrões utilizando Object . assign ou desestruturação
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
type MenuConfig = {
title? : string ;
body? : string ;
buttonText? : string ;
cancellable? : boolean ;
} ;
2019-01-31 10:18:41 +02:00
function createMenu ( config : MenuConfig ) {
config . title = config . title || 'Foo' ;
config . body = config . body || 'Bar' ;
config . buttonText = config . buttonText || 'Baz' ;
2019-02-13 11:18:45 -03:00
config . cancellable =
config . cancellable !== undefined ? config.cancellable : true ;
2019-02-06 09:57:52 +02:00
}
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
const menuConfig = {
title : null ,
body : 'Bar' ,
buttonText : null ,
cancellable : true
} ;
createMenu ( menuConfig ) ;
2019-01-31 10:18:41 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
type MenuConfig = {
title? : string ;
body? : string ;
buttonText? : string ;
cancellable? : boolean ;
} ;
2019-01-31 10:18:41 +02:00
function createMenu ( config : MenuConfig ) {
2019-02-13 11:18:45 -03:00
const menuConfig = Object . assign (
{
title : 'Foo' ,
body : 'Bar' ,
buttonText : 'Baz' ,
cancellable : true
} ,
config
) ;
2019-01-31 10:18:41 +02:00
}
createMenu ( { body : 'Bar' } ) ;
` ` `
2019-02-13 11:18:45 -03:00
Alternativamente , você pode usar desestruturação com valores predefinidos :
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
type MenuConfig = {
title? : string ;
body? : string ;
buttonText? : string ;
cancellable? : boolean ;
} ;
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
function createMenu ( {
title = 'Foo' ,
body = 'Bar' ,
buttonText = 'Baz' ,
cancellable = true
} : MenuConfig ) {
2019-01-31 10:18:41 +02:00
// ...
}
createMenu ( { body : 'Bar' } ) ;
` ` `
2019-02-13 11:18:45 -03:00
Para evitar efeitos colaterais e comportamentos indesejados ao passar explicitamente ` undefined ` ou ` null ` , você pode dizer ao compilador TypeScript para não permitir isso . Veja mais em [ ` --strictNullChecks ` ] ( https : //www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#--strictnullchecks).
2019-02-05 17:21:31 +02:00
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Não use flags como parâmetros de funções
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Flags indicam ao seu usuário que a função faz mais de uma coisa .
Funções devem fazer apenas uma coisa . Divida sua função se ela segue diferentes caminhos baseados em uma condição .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-06 10:43:37 +02:00
function createFile ( name : string , temp : boolean ) {
2019-01-31 10:18:41 +02:00
if ( temp ) {
fs . create ( ` ./temp/ ${ name } ` ) ;
} else {
fs . create ( name ) ;
}
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-07 23:42:21 +02:00
function createFile ( name : string ) {
fs . create ( name ) ;
2019-01-31 10:18:41 +02:00
}
2019-02-13 11:18:45 -03:00
function createTempFile ( name : string ) {
fs . create ( ` ./temp/ ${ name } ` ) ;
}
2019-01-31 10:18:41 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Evite efeitos colaterais ( parte 1 )
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Uma função produz efeitos colaterais se ela faz algo além de receber um valor e retornar outro valor ou valores .
Efeitos colaterais poderia ser escrever em um arquivo , modificar alguma variável global , ou acidentamente transferir todo seu dinheiro para um estranho .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Agora , você precisa ter efeitos colaterais em algumas ocasiões . Como no exemplo anterior , onde você precisa escrever em um arquivo .
O que você deseja fazer é centralizar onde você está fazendo isto , ao invés de ter várias funções e classes que escrevem em um só arquivo .
Tenha um serviço que faça isso . Um , e apenas um .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
O ponto principal é evitar alguns vacilos como compartilhar o estado entre dois objetos sem nenhuma estrutura , usando tipo de dados mutáveis que podem ser escritos por qualquer coisas , e não centralizar onde seus efeitos colaterais vão ocorrer . Se você pode fazer isso , você será mais feliz que a maioria dos outros programadores .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
// Global variable referenced by following function.
2019-02-13 11:18:45 -03:00
// If we had another function that used this name, now it'd be an array and it could break it.
2019-01-31 10:18:41 +02:00
let name = 'Robert C. Martin' ;
function toBase64() {
name = btoa ( name ) ;
}
2019-02-13 11:18:45 -03:00
toBase64 ( ) ; // produces side effects to `name` variable
2019-02-07 21:53:16 +02:00
2019-01-31 10:18:41 +02:00
console . log ( name ) ; // expected to print 'Robert C. Martin' but instead 'Um9iZXJ0IEMuIE1hcnRpbg=='
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
// Global variable referenced by following function.
// If we had another function that used this name, now it'd be an array and it could break it.
2019-01-31 10:18:41 +02:00
const name = 'Robert C. Martin' ;
2019-02-06 09:57:52 +02:00
function toBase64 ( text : string ) : string {
2019-01-31 10:18:41 +02:00
return btoa ( text ) ;
}
2019-02-05 10:33:23 +01:00
const encodedName = toBase64 ( name ) ;
2019-02-13 11:18:45 -03:00
2019-01-31 10:18:41 +02:00
console . log ( name ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Evite efeitos colaterais ( parte 2 )
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
No JavaScript , primitivos são passados por valores e objetos / arrays são passados por referência . No caso dos objetos e arrays , se sua função faz uma mudança em um array de carrinho de loja , por exemplo , adicionando um item à compra , então todas outras funções que usam esse array serão afetadas por esta adição . Isso pode ser bom , mas pode ser ruim também . Vamos imaginar um cenário ruim :
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
O usuário clica em "Comprar" , botão que chama uma função de compra , que envia uma requisição com o array de carrinho de compras ao servidor . Por conta de uma conexão ruim , a função de comprar precisa ficar tentando novamente a requisição . Agora , e se o usuário , neste meio tempo , clicar no botão "Adicionar ao carrinho" , em um item que ele não quer , antes da requisição começar ? Se isso acontecer e a requisição começar , a função de compra irá enviar o item adicionado acidentalmente , pois este tem a referência do mesmo array anterior e que a função _addItemToCart_ modificou , ao adicionar um item novo .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Uma ó tima solução seria a função _addItemToCart_ sempre clonar o carrinho , editar , e retornar o clone . Isso assegura que nenhuma outra função que tem a referência do carrinho será afetada pelas mudanças .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Dois avisos ao mencionar essa abordagem :
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
1 . Podem haver casos onde você quer modificar o objeto inputado , mas quando você adota esse prática você verá que esses casos são bem raros . A maiorias das coisas podem ser refatoradas para não terem efeitos colaterais ! ( veja [ funções puras ] ( https : //en.wikipedia.org/wiki/Pure_function))
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
2 . Clonar grandes objetos pode ser bem caro em termos de performance . Com sorte , isso não é um grande problema na prática pois há ó timas bibliotecas que permitem esse tipo de abordagem serem rápidas e não tão intensivas no consumo de memória , como seria em clonar os objetos manualmente .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-06 09:57:52 +02:00
function addItemToCart ( cart : CartItem [ ] , item : Item ) : void {
2019-01-31 10:18:41 +02:00
cart . push ( { item , date : Date.now ( ) } ) ;
2019-02-13 11:18:45 -03:00
}
2019-01-31 10:18:41 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-06 09:57:52 +02:00
function addItemToCart ( cart : CartItem [ ] , item : Item ) : CartItem [ ] {
2019-02-05 12:56:25 +00:00
return [ . . . cart , { item , date : Date.now ( ) } ] ;
2019-02-13 11:18:45 -03:00
}
2019-01-31 10:18:41 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Não escreva em funções globais
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Poluir escopos globais é uma má prática em JavaScript pois você pode colidir com código de outra biblioteca , e o usuário da sua API não será esclarecido até ele receber uma erro em produção . Vamos pensar no exemplo a seguir : E se você quiser extender o código nativo do Array em JavaScript , para ter uma função diff que pode mostrar a diferença entre dois arrays ? Você pode escrever sua nova função em ` Array.prototype ` , mas isso iria colidir com código de outra biblioteca que tenta fazer a mesma coisa . E se essa biblioteca usa o método ` diff ` para achar a diferença entre o primeiro e o ú ltimo elemento de um array ? Por isso seria muito melhor usar classes e simplesmente extender o ` Array ` global .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
declare global {
interface Array < T > {
diff ( other : T [ ] ) : Array < T > ;
}
}
2019-02-06 10:43:37 +02:00
if ( ! Array . prototype . diff ) {
2019-02-13 11:18:45 -03:00
Array . prototype . diff = function < T > ( other : T [ ] ) : T [ ] {
2019-01-31 10:18:41 +02:00
const hash = new Set ( other ) ;
return this . filter ( elem = > ! hash . has ( elem ) ) ;
} ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
class MyArray < T > extends Array < T > {
diff ( other : T [ ] ) : T [ ] {
const hash = new Set ( other ) ;
return this . filter ( elem = > ! hash . has ( elem ) ) ;
2019-02-13 11:18:45 -03:00
}
2019-01-31 10:18:41 +02:00
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Priorize programação funcional à programação imperativa
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Use esse tipo de paradigma quando puder .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
const contributions = [
{
name : 'Uncle Bobby' ,
linesOfCode : 500
2019-02-13 11:18:45 -03:00
} ,
{
2019-01-31 10:18:41 +02:00
name : 'Suzie Q' ,
linesOfCode : 1500
2019-02-13 11:18:45 -03:00
} ,
{
2019-01-31 10:18:41 +02:00
name : 'Jimmy Gosling' ,
linesOfCode : 150
2019-02-13 11:18:45 -03:00
} ,
{
2019-01-31 10:18:41 +02:00
name : 'Gracie Hopper' ,
linesOfCode : 1000
}
] ;
let totalOutput = 0 ;
for ( let i = 0 ; i < contributions . length ; i ++ ) {
totalOutput += contributions [ i ] . linesOfCode ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
const contributions = [
{
name : 'Uncle Bobby' ,
linesOfCode : 500
2019-02-13 11:18:45 -03:00
} ,
{
2019-01-31 10:18:41 +02:00
name : 'Suzie Q' ,
linesOfCode : 1500
2019-02-13 11:18:45 -03:00
} ,
{
2019-01-31 10:18:41 +02:00
name : 'Jimmy Gosling' ,
linesOfCode : 150
2019-02-13 11:18:45 -03:00
} ,
{
2019-01-31 10:18:41 +02:00
name : 'Gracie Hopper' ,
linesOfCode : 1000
}
] ;
2019-02-13 11:18:45 -03:00
const totalOutput = contributions . reduce (
( totalLines , output ) = > totalLines + output . linesOfCode ,
0
) ;
2019-01-31 10:18:41 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Encapsular condicionais
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
if ( subscription . isTrial || account . balance > 0 ) {
// ...
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
function canActivateService ( subscription : Subscription , account : Account ) {
2019-02-13 11:18:45 -03:00
return subscription . isTrial || account . balance > 0 ;
2019-01-31 10:18:41 +02:00
}
if ( canActivateService ( subscription , account ) ) {
// ...
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Evite condicionais negativas
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
function isEmailNotUsed ( email : string ) {
2019-01-31 10:18:41 +02:00
// ...
}
if ( isEmailNotUsed ( email ) ) {
// ...
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
function isEmailUsed ( email ) {
2019-01-31 10:18:41 +02:00
// ...
}
if ( ! isEmailUsed ( node ) ) {
// ...
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ Ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Evite condicionais
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Isso parece uma tarefa impossivel . Quando se escuta isso pela primeira vez , a maioria das pessoas dizem , "Como eu posso fazer qualquer coisa sem declarar um `if`?" A resposta é que você pode usar polimorfismo para alcançar o mesmo objetivo em muitos casos . A segunda pergunta normalmente é , _ "Bom isso é otimo, mas por que eu iria querer fazer isso?" _ A resposta é um conceito anterior de codigo limpo que aprendemos antes : Uma função deve fazer apenas uma coisa . Quando você tem classes e funções com ` if ` declarados , você está falando para seu usuario que sua função faz mais que uma coisa . Lembre - se apenas faça uma coisa .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
class Airplane {
private type : string ;
2019-02-07 21:53:16 +02:00
2019-02-13 11:18:45 -03:00
// ...
2019-01-31 10:18:41 +02:00
getCruisingAltitude() {
switch ( this . type ) {
case '777' :
return this . getMaxAltitude ( ) - this . getPassengerCount ( ) ;
case 'Air Force One' :
return this . getMaxAltitude ( ) ;
case 'Cessna' :
return this . getMaxAltitude ( ) - this . getFuelExpenditure ( ) ;
default :
throw new Error ( 'Unknown airplane type.' ) ;
}
}
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
class Airplane {
2019-01-31 10:18:41 +02:00
// ...
}
class Boeing777 extends Airplane {
// ...
getCruisingAltitude() {
return this . getMaxAltitude ( ) - this . getPassengerCount ( ) ;
}
}
class AirForceOne extends Airplane {
// ...
getCruisingAltitude() {
return this . getMaxAltitude ( ) ;
}
}
class Cessna extends Airplane {
// ...
getCruisingAltitude() {
return this . getMaxAltitude ( ) - this . getFuelExpenditure ( ) ;
}
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Evite verificação de tipo
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
TypeScript é um superconjunto sintático estrito de JavaScript e adciona verificação de tipo estático para a linguagem .
Prefira sempre especificar tipos de variáveis , parâmetros e retornar valores para aproveitar todo o potencial dos recursos do TypeScript .
Isso torna refatoração mais fácil .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
function travelToTexas ( vehicle : Bicycle | Car ) {
if ( vehicle instanceof Bicycle ) {
2019-02-13 11:18:45 -03:00
vehicle . pedal ( this . currentLocation , new Location ( 'texas' ) ) ;
2019-01-31 10:18:41 +02:00
} else if ( vehicle instanceof Car ) {
2019-02-13 11:18:45 -03:00
vehicle . drive ( this . currentLocation , new Location ( 'texas' ) ) ;
2019-01-31 10:18:41 +02:00
}
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
type Vehicle = Bicycle | Car ;
function travelToTexas ( vehicle : Vehicle ) {
2019-02-13 11:18:45 -03:00
vehicle . move ( this . currentLocation , new Location ( 'texas' ) ) ;
2019-01-31 10:18:41 +02:00
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Não otimize demais
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Navegadores modernos fazem muita otimização por baixo dos panos na hora da execução . Muitas vezes , se você está otimizando então , você está apenas perdendo seu tempo . Há bons
[ recursos ] ( https : //github.com/petkaantonov/bluebird/wiki/Optimization-killers) para ver aonde está faltando otimização. Procure esses, até que eles sejam corrigidos caso possam ser.
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
// On old browsers, each iteration with uncached `list.length` would be costly
// because of `list.length` recomputation. In modern browsers, this is optimized.
for ( let i = 0 , len = list . length ; i < len ; i ++ ) {
// ...
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
for ( let i = 0 ; i < list . length ; i ++ ) {
// ...
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # # Remover código morto
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
Código morto é tão ruim quanto código duplicado . Não há razão alguma para mantê - lo na sua base de códigos .
Se não está sendo chamado , livre - se dele ! Ainda continuará seguro no seu histórico de versões se você ainda precisar .
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
function oldRequestModule ( url : string ) {
// ...
}
function requestModule ( url : string ) {
// ...
}
const req = requestModule ;
inventoryTracker ( 'apples' , req , 'www.inventory-awesome.io' ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-05 08:51:37 +02:00
function requestModule ( url : string ) {
2019-01-31 10:18:41 +02:00
// ...
}
2019-02-05 08:51:37 +02:00
const req = requestModule ;
inventoryTracker ( 'apples' , req , 'www.inventory-awesome.io' ) ;
2019-01-31 10:18:41 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
# # Objetos e estruturas de dados
2019-02-09 23:28:17 +02:00
2019-02-13 11:18:45 -03:00
# # # Use getters e setters
2019-02-12 08:40:50 +02:00
2019-02-13 11:18:45 -03:00
TypeScript suporta sintaxe de getter / setter .
Usar getters e setters para acessar dados de objetos que encapsulam comportamento pode ser melhor do que simplesmente procurar por uma propriedade em um objeto .
"Por que?" você pode perguntar . Bom , aqui está uma lista de razões :
2019-02-09 23:28:17 +02:00
2019-02-13 11:18:45 -03:00
- Quando você quer fazer mais além de pegar uma propriedade de um objeto , você não precisa olhar e mudar todos os acessores na sua base de códigos .
- Faz adicionar validação simples quando está setando .
- Encapsula a representação interna ;
- Fácil de adicionar logging e tratamento de erros quando usar get e set .
- Você pode carregar preguiçosamente as propriedades do seu objeto , vamos dizer pegar de um servido .
2019-02-09 23:28:17 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-09 23:28:17 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
class BankAccount {
balance : number = 0 ;
2019-01-29 09:44:59 +02:00
// ...
}
const value = 100 ;
2019-02-13 11:18:45 -03:00
const account = new BankAccount ( ) ;
2019-01-29 09:44:59 +02:00
if ( value < 0 ) {
throw new Error ( 'Cannot set negative balance.' ) ;
}
account . balance = value ;
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-29 09:44:59 +02:00
` ` ` ts
class BankAccount {
private accountBalance : number = 0 ;
get balance ( ) : number {
return this . accountBalance ;
}
set balance ( value : number ) {
if ( value < 0 ) {
throw new Error ( 'Cannot set negative balance.' ) ;
}
this . accountBalance = value ;
}
// ...
}
const account = new BankAccount ( ) ;
account . balance = 100 ;
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ Ir para o topo ] ( # table - of - contents ) * *
2019-01-29 09:44:59 +02:00
2019-02-13 11:18:45 -03:00
# # # Fazer objetos ter membros privados / protegidos
2019-01-29 09:44:59 +02:00
2019-02-13 11:18:45 -03:00
TypeScript suporta acessores ` Publico ` _ ( Padrão ) _ , ` Protegido ` e ` Privado ` em membros das classes .
2019-01-29 09:44:59 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-29 09:44:59 +02:00
` ` ` ts
class Circle {
radius : number ;
2019-02-13 11:18:45 -03:00
2019-01-29 09:44:59 +02:00
constructor ( radius : number ) {
this . radius = radius ;
}
2019-02-06 09:57:52 +02:00
perimeter() {
2019-01-29 09:44:59 +02:00
return 2 * Math . PI * this . radius ;
}
2019-02-06 09:57:52 +02:00
surface() {
2019-01-29 09:44:59 +02:00
return Math . PI * this . radius * this . radius ;
}
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-29 09:44:59 +02:00
` ` ` ts
class Circle {
2019-02-13 11:18:45 -03:00
constructor ( private readonly radius : number ) { }
2019-01-29 09:44:59 +02:00
2019-02-06 09:57:52 +02:00
perimeter() {
2019-01-29 09:44:59 +02:00
return 2 * Math . PI * this . radius ;
}
2019-02-06 09:57:52 +02:00
surface() {
2019-01-29 09:44:59 +02:00
return Math . PI * this . radius * this . radius ;
}
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-29 09:44:59 +02:00
2019-02-13 11:18:45 -03:00
# # # Prefira propriedades de apenas leitura
2019-01-29 09:44:59 +02:00
2019-02-13 11:18:45 -03:00
TypeScript ' s type system allows you to mark individual properties on an interface / class as readonly . This allows you to work in a functional way ( unexpected mutation is bad ) .
2019-01-29 09:44:59 +02:00
For more advanced scenarios there is a built - in type ` Readonly ` that takes a type ` T ` and marks all of its properties as readonly using mapped types ( see [ mapped types ] ( https : //www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types)).
2019-02-13 11:18:45 -03:00
O sistema de tipagem do TypeScript permite que você marque propriedades individuais em uma interface / classe como de apenas leitura . Isso permite que você trabalhe de uma maneira funcional ( mutações inesperadas são ruins ) .
Para cenários mais avançados há um tipo integrado ` Readonly ` que recebe um tipo ` T ` e marca todas suas propriedades como de apenas leitura , usando tipos mapeados ( mapped types ) ( veja [ mapped types ] ( https : //www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types)).
* * Ruim : * *
2019-01-29 09:44:59 +02:00
` ` ` ts
interface Config {
host : string ;
port : string ;
db : string ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-29 09:44:59 +02:00
` ` ` ts
interface Config {
readonly host : string ;
readonly port : string ;
readonly db : string ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-11 13:39:31 +02:00
2019-02-04 18:09:04 +02:00
# # Classes
2019-02-13 11:18:45 -03:00
# # # Classes devem ser pequenas
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
O tamanho de uma clase é mensurado por sua responsabilidade . Seguindo o princípio de ú nica responsabilidade , uma classe deve ser pequena .
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
class Dashboard {
2019-02-13 11:18:45 -03:00
getLanguage ( ) : string {
/* ... */
}
setLanguage ( language : string ) : void {
/* ... */
}
showProgress ( ) : void {
/* ... */
}
hideProgress ( ) : void {
/* ... */
}
isDirty ( ) : boolean {
/* ... */
}
disable ( ) : void {
/* ... */
}
enable ( ) : void {
/* ... */
}
addSubscription ( subscription : Subscription ) : void {
/* ... */
}
removeSubscription ( subscription : Subscription ) : void {
/* ... */
}
addUser ( user : User ) : void {
/* ... */
}
removeUser ( user : User ) : void {
/* ... */
}
goToHomePage ( ) : void {
/* ... */
}
updateProfile ( details : UserDetails ) : void {
/* ... */
}
getVersion ( ) : string {
/* ... */
}
2019-02-04 18:09:04 +02:00
// ...
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
class Dashboard {
2019-02-13 11:18:45 -03:00
disable ( ) : void {
/* ... */
}
enable ( ) : void {
/* ... */
}
getVersion ( ) : string {
/* ... */
}
2019-02-04 18:09:04 +02:00
}
2019-02-13 11:18:45 -03:00
// divida as responsábilidades movendo os métodos restantes para outras classes
2019-02-04 18:09:04 +02:00
// ...
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
# # # Classes coesas e desacopladas
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
Coesão define o grau de parentesco que um membro de uma classe tem com os outros . Idealmente , cada campo de uma classe deve ser usado por cada um dos métodos .
Nós dizemos , então , que uma classe é super coesa . Na prática , nem sempre isso é possível , e nem recomendado em alguns casos . Você deve preferir , entretanto , classes com alta coesão .
2019-02-04 18:18:27 +02:00
2019-02-13 11:18:45 -03:00
Acoplamento se refere ao quanto duas classes são relacionadas ou dependentes umas das outras . Classes são pouco acopladas / desacopladas quando mudanças em uma não afeta a outra .
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
Um bom design de software tem * * coesão * * e * * desacoplamento * *
* * Ruim : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
class UserManager {
// Bad: each private variable is used by one or another group of methods.
// It makes clear evidence that the class is holding more than a single responsibility.
// If I need only to create the service to get the transactions for a user,
2019-02-13 11:18:45 -03:00
// I'm still forced to pass and instance of emailSender.
2019-02-04 18:09:04 +02:00
constructor (
private readonly db : Database ,
2019-02-13 11:18:45 -03:00
private readonly emailSender : EmailSender
) { }
2019-02-04 18:09:04 +02:00
async getUser ( id : number ) : Promise < User > {
2019-02-06 10:43:37 +02:00
return await db . users . findOne ( { id } ) ;
2019-02-04 18:09:04 +02:00
}
async getTransactions ( userId : number ) : Promise < Transaction [ ] > {
2019-02-06 10:43:37 +02:00
return await db . transactions . find ( { userId } ) ;
2019-02-04 18:09:04 +02:00
}
async sendGreeting ( ) : Promise < void > {
await emailSender . send ( 'Welcome!' ) ;
}
async sendNotification ( text : string ) : Promise < void > {
await emailSender . send ( text ) ;
}
async sendNewsletter ( ) : Promise < void > {
// ...
}
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
class UserService {
2019-02-13 11:18:45 -03:00
constructor ( private readonly db : Database ) { }
2019-02-04 18:09:04 +02:00
async getUser ( id : number ) : Promise < User > {
2019-02-13 11:18:45 -03:00
return await db . users . findOne ( { id } ) ;
2019-02-04 18:09:04 +02:00
}
async getTransactions ( userId : number ) : Promise < Transaction [ ] > {
2019-02-13 11:18:45 -03:00
return await db . transactions . find ( { userId } ) ;
2019-02-04 18:09:04 +02:00
}
}
class UserNotifier {
2019-02-13 11:18:45 -03:00
constructor ( private readonly emailSender : EmailSender ) { }
2019-02-04 18:09:04 +02:00
async sendGreeting ( ) : Promise < void > {
2019-02-13 11:18:45 -03:00
await emailSender . send ( 'Welcome!' ) ;
2019-02-04 18:09:04 +02:00
}
async sendNotification ( text : string ) : Promise < void > {
2019-02-13 11:18:45 -03:00
await emailSender . send ( text ) ;
2019-02-04 18:09:04 +02:00
}
async sendNewsletter ( ) : Promise < void > {
// ...
}
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
# # # Prefira composição à herança
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
Como foi muito bem pontuado em [ Design Patterns ] ( https : //en.wikipedia.org/wiki/Design_Patterns), pela _Gang of Four_, você deve preferir composição à herança quando puder. Há ótimos motivos para usar herança, e ótimos motivos para usar composição. O ponto aqui é, se sua mente instintivamente pensa em heranças, tente pensar em como composições poderiam resolver seu problema de forma melhor. Em muitos casos isso é possível.
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
Você pode estar pensando , "quando devo usar herança?" E isso depende do seu problema , mas aqui vai uma lista de quando usar herança faz mais sentido de que composição :
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
1 . Sua herança representa uma relação de "é um..." e não "tem um..." ( Humano - > Animal vs Usuário - > Detalhes ) .
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
2 . Você pode reutilizar código das classes base ( Humanos podem se mover como animais ) .
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
3 . Você deseja fazer mudanças globais ao alterar a classe base . ( Alterar o gasto de calorias de todos os animais quando se movimentam ) .
* * Ruim : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
class Employee {
2019-02-13 11:18:45 -03:00
constructor ( private readonly name : string , private readonly email : string ) { }
2019-02-04 18:09:04 +02:00
// ...
}
2019-02-13 11:18:45 -03:00
// Ruim pois Employees "tem" taxas. EmployeeTaxData não é um tipo de Employee
2019-02-04 18:09:04 +02:00
class EmployeeTaxData extends Employee {
constructor (
2019-02-06 09:57:52 +02:00
name : string ,
2019-02-06 10:43:37 +02:00
email : string ,
2019-02-06 09:57:52 +02:00
private readonly ssn : string ,
2019-02-13 11:18:45 -03:00
private readonly salary : number
) {
2019-02-04 18:09:04 +02:00
super ( name , email ) ;
}
// ...
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
class Employee {
private taxData : EmployeeTaxData ;
2019-02-13 11:18:45 -03:00
constructor ( private readonly name : string , private readonly email : string ) { }
2019-02-04 18:09:04 +02:00
setTaxData ( ssn : string , salary : number ) : Employee {
this . taxData = new EmployeeTaxData ( ssn , salary ) ;
return this ;
}
// ...
}
class EmployeeTaxData {
2019-02-13 11:18:45 -03:00
constructor ( public readonly ssn : string , public readonly salary : number ) { }
2019-02-04 18:09:04 +02:00
// ...
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
# # # Use cadeia de métodos
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
Esse padrão é bem ú til e usado normalmente em muitas bibliotecas . Seu uso permite que seu código seja mais expressivo , e menos verboso . Por esse motivo , use cadeia de métodos e olhe depois como seu código irá estar mais limpo .
2019-02-04 18:09:04 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
2019-02-05 08:51:37 +02:00
class QueryBuilder {
2019-02-04 18:09:04 +02:00
private collection : string ;
private pageNumber : number = 1 ;
private itemsPerPage : number = 100 ;
private orderByFields : string [ ] = [ ] ;
from ( collection : string ) : void {
this . collection = collection ;
}
page ( number : number , itemsPerPage : number = 100 ) : void {
this . pageNumber = number ;
this . itemsPerPage = itemsPerPage ;
}
orderBy ( . . . fields : string [ ] ) : void {
this . orderByFields = fields ;
}
2019-02-05 08:51:37 +02:00
build ( ) : Query {
2019-02-04 18:09:04 +02:00
// ...
}
}
// ...
2019-02-13 11:18:45 -03:00
const query = new QueryBuilder ( ) ;
query . from ( 'users' ) ;
query . page ( 1 , 100 ) ;
query . orderBy ( 'firstName' , 'lastName' ) ;
2019-02-04 18:09:04 +02:00
2019-02-05 08:51:37 +02:00
const query = queryBuilder . build ( ) ;
2019-02-04 18:09:04 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
2019-02-05 08:51:37 +02:00
class QueryBuilder {
2019-02-04 18:09:04 +02:00
private collection : string ;
private pageNumber : number = 1 ;
private itemsPerPage : number = 100 ;
private orderByFields : string [ ] = [ ] ;
2019-02-05 08:51:37 +02:00
from ( collection : string ) : this {
2019-02-04 18:09:04 +02:00
this . collection = collection ;
return this ;
}
2019-02-05 08:51:37 +02:00
page ( number : number , itemsPerPage : number = 100 ) : this {
2019-02-04 18:09:04 +02:00
this . pageNumber = number ;
this . itemsPerPage = itemsPerPage ;
return this ;
}
2019-02-05 08:51:37 +02:00
orderBy ( . . . fields : string [ ] ) : this {
2019-02-04 18:09:04 +02:00
this . orderByFields = fields ;
return this ;
}
2019-02-05 08:51:37 +02:00
build ( ) : Query {
2019-02-04 18:09:04 +02:00
// ...
}
}
// ...
2019-02-05 08:51:37 +02:00
const query = new QueryBuilder ( )
2019-02-04 18:09:04 +02:00
. from ( 'users' )
. page ( 1 , 100 )
. orderBy ( 'firstName' , 'lastName' )
2019-02-05 08:51:37 +02:00
. build ( ) ;
2019-02-04 18:09:04 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 18:09:04 +02:00
2019-02-04 18:08:52 +02:00
# # SOLID
2019-02-13 11:18:45 -03:00
# # # Principio da ú nica responsabilidade ( Single Responsabiliy Principle )
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
Como dito em Código Limpo , "Não deve haver mais de um motivo para alterar uma classe" . É tentador lotar uma classe com várias funcionalidades , como se você só pudesse carregar uma mala em uma viagem . O problema disso é que sua classe não será conceitualmente coesiva e posteriormente te trará vários motivos para mudar . Reduzir a quantidade de vezes que você precisa mudar uma classe é importante . É importante pois se muitas funcionalidades estão contidas em uma classe e você altera um pedaço disso , pode ser difícil entender como isso irá afetar os módulos dependentes da sua classe / do seu código .
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
class UserSettings {
2019-02-13 11:18:45 -03:00
constructor ( private readonly user : User ) { }
2019-02-04 18:08:52 +02:00
changeSettings ( settings : UserSettings ) {
if ( this . verifyCredentials ( ) ) {
// ...
}
}
verifyCredentials() {
// ...
}
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
class UserAuth {
2019-02-13 11:18:45 -03:00
constructor ( private readonly user : User ) { }
2019-02-04 18:08:52 +02:00
verifyCredentials() {
// ...
}
}
class UserSettings {
private readonly auth : UserAuth ;
constructor ( private readonly user : User ) {
this . auth = new UserAuth ( user ) ;
}
changeSettings ( settings : UserSettings ) {
if ( this . auth . verifyCredentials ( ) ) {
// ...
}
}
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
# # # Prinpio do Aberto / Fechado ( Open / Closed Principle )
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
Como dito por Bertrand Meyer , "entidades em software (classes, módulos, funções, etc.) devem ser abertas para extenções, mas fechadas para modificações." Mas o que isso significa ? Este princípio diz , basicamente , que você deve permitir que seus usuários adicionem novas funcionalidades sem alterar código já existente .
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
class AjaxAdapter extends Adapter {
constructor ( ) {
super ( ) ;
}
// ...
}
class NodeAdapter extends Adapter {
constructor ( ) {
super ( ) ;
}
// ...
}
class HttpRequester {
2019-02-13 11:18:45 -03:00
constructor ( private readonly adapter : Adapter ) { }
2019-02-04 18:08:52 +02:00
async fetch < T > ( url : string ) : Promise < T > {
if ( this . adapter instanceof AjaxAdapter ) {
const response = await makeAjaxCall < T > ( url ) ;
2019-02-13 11:18:45 -03:00
// transforma a resposta e retorna
2019-02-04 18:08:52 +02:00
} else if ( this . adapter instanceof NodeAdapter ) {
const response = await makeHttpCall < T > ( url ) ;
2019-02-13 11:18:45 -03:00
// transforma a resposta e retorna
2019-02-04 18:08:52 +02:00
}
}
}
function makeAjaxCall < T > ( url : string ) : Promise < T > {
2019-02-13 11:18:45 -03:00
// faz a requisição e retorna uma Promise
2019-02-04 18:08:52 +02:00
}
function makeHttpCall < T > ( url : string ) : Promise < T > {
2019-02-13 11:18:45 -03:00
// faz a requisição e retorna uma Promise
2019-02-04 18:08:52 +02:00
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
abstract class Adapter {
abstract async request < T > ( url : string ) : Promise < T > ;
}
class AjaxAdapter extends Adapter {
constructor ( ) {
super ( ) ;
}
2019-02-13 11:18:45 -03:00
async request < T > ( url : string ) : Promise < T > {
// faz requisição e retorna uma Promise
2019-02-04 18:08:52 +02:00
}
// ...
}
class NodeAdapter extends Adapter {
constructor ( ) {
super ( ) ;
}
2019-02-13 11:18:45 -03:00
async request < T > ( url : string ) : Promise < T > {
// faz requisição e retorna uma Promise
2019-02-04 18:08:52 +02:00
}
// ...
}
class HttpRequester {
2019-02-13 11:18:45 -03:00
constructor ( private readonly adapter : Adapter ) { }
2019-02-04 18:08:52 +02:00
async fetch < T > ( url : string ) : Promise < T > {
const response = await this . adapter . request < T > ( url ) ;
2019-02-13 11:18:45 -03:00
// transforma a resposta e retorna
2019-02-04 18:08:52 +02:00
}
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
# # # Principios da Substituição de Liskov ( Liskov Substitution Principle )
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
Este é um termo muito assustador para um conceito bem simples . É formalmente definido como "Se S é um subtipo de T, então os objetos do tipo T podem ser substituidos com objetos do tipo S(ou seja, objetos do tipo S podem substituir objetos do tipo T) sem alterar nenhuma propriedade desejáveis daquele programa (correção, tarefa executada, etc.)." E essa é uma definição ainda mais assustadora .
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
A melhor explicação para isso é se você tem uma classe pi e uma classe filho , então a classe base e a classe filho podem ser usada sem ocorrer resultados incorretos . Isso pode ainda estar sendo confuso , então vamos dar uma olhada no exemplo clássico Quadrado - Retângulo . Matemáticamente , o quadrado é um retângulo , mas se você modelar o quadrado usando o relacionamento "é-um" via herança , você terá problemas .
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
class Rectangle {
2019-02-13 11:18:45 -03:00
constructor ( protected width : number = 0 , protected height : number = 0 ) { }
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
setColor ( color : string ) {
2019-02-04 18:08:52 +02:00
// ...
}
render ( area : number ) {
// ...
}
2019-02-13 11:18:45 -03:00
setWidth ( width : number ) {
2019-02-04 18:08:52 +02:00
this . width = width ;
}
2019-02-13 11:18:45 -03:00
setHeight ( height : number ) {
2019-02-04 18:08:52 +02:00
this . height = height ;
}
getArea ( ) : number {
return this . width * this . height ;
}
}
class Square extends Rectangle {
2019-02-13 11:18:45 -03:00
setWidth ( width : number ) {
2019-02-04 18:08:52 +02:00
this . width = width ;
this . height = width ;
}
2019-02-13 11:18:45 -03:00
setHeight ( height : number ) {
2019-02-04 18:08:52 +02:00
this . width = height ;
this . height = height ;
}
}
function renderLargeRectangles ( rectangles : Rectangle [ ] ) {
2019-02-13 11:18:45 -03:00
rectangles . forEach ( rectangle = > {
rectangle . setWidth ( 4 ) ;
rectangle . setHeight ( 5 ) ;
const area = rectangle . getArea ( ) ; // BAD: Returns 25 for Square. Should be 20.
2019-02-04 18:08:52 +02:00
rectangle . render ( area ) ;
} ) ;
}
const rectangles = [ new Rectangle ( ) , new Rectangle ( ) , new Square ( ) ] ;
renderLargeRectangles ( rectangles ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
abstract class Shape {
2019-02-13 11:18:45 -03:00
setColor ( color : string ) {
2019-02-04 18:08:52 +02:00
// ...
}
render ( area : number ) {
// ...
}
abstract getArea ( ) : number ;
}
class Rectangle extends Shape {
2019-02-13 11:18:45 -03:00
constructor ( private readonly width = 0 , private readonly height = 0 ) {
2019-02-04 18:08:52 +02:00
super ( ) ;
}
getArea ( ) : number {
return this . width * this . height ;
}
}
class Square extends Shape {
constructor ( private readonly length : number ) {
super ( ) ;
}
getArea ( ) : number {
return this . length * this . length ;
}
}
function renderLargeShapes ( shapes : Shape [ ] ) {
2019-02-13 11:18:45 -03:00
shapes . forEach ( shape = > {
2019-02-04 18:08:52 +02:00
const area = shape . getArea ( ) ;
shape . render ( area ) ;
} ) ;
}
const shapes = [ new Rectangle ( 4 , 5 ) , new Rectangle ( 4 , 5 ) , new Square ( 5 ) ] ;
renderLargeShapes ( shapes ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
# # # Principio da Segragação de Interface ( PSI )
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
PSI afima que "Clientes não deveriam ser forçados a serem dependentes de interfaces que eles não usam" . Esse princípio é muito relacionado ao Princípio da ú nica responsabilidade .
O que isso realmente significa é que você deve sempre projetar suas abstrações de uma maneira que os clientes que estão usando os métodos expostos não obtenham a "a torta inteira" . Isto também inclui aos clientes o dever implementar metódos que eles , na realidade , não precisam .
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
interface ISmartPrinter {
2019-02-04 18:08:52 +02:00
print ( ) ;
2019-02-13 11:18:45 -03:00
2019-02-04 18:08:52 +02:00
fax ( ) ;
2019-02-13 11:18:45 -03:00
2019-02-04 18:08:52 +02:00
scan ( ) ;
}
2019-02-13 11:18:45 -03:00
class AllInOnePrinter implements ISmartPrinter {
2019-02-04 18:08:52 +02:00
print() {
// ...
2019-02-13 11:18:45 -03:00
}
2019-02-04 18:08:52 +02:00
fax() {
// ...
}
scan() {
// ...
}
}
2019-02-13 11:18:45 -03:00
class EconomicPrinter implements ISmartPrinter {
2019-02-04 18:08:52 +02:00
print() {
// ...
2019-02-13 11:18:45 -03:00
}
2019-02-04 18:08:52 +02:00
fax() {
throw new Error ( 'Fax not supported.' ) ;
}
scan() {
throw new Error ( 'Scan not supported.' ) ;
}
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
interface IPrinter {
2019-02-04 18:08:52 +02:00
print ( ) ;
}
2019-02-13 11:18:45 -03:00
interface IFax {
2019-02-04 18:08:52 +02:00
fax ( ) ;
}
2019-02-13 11:18:45 -03:00
interface IScanner {
2019-02-04 18:08:52 +02:00
scan ( ) ;
}
2019-02-13 11:18:45 -03:00
class AllInOnePrinter implements IPrinter , IFax , IScanner {
2019-02-04 18:08:52 +02:00
print() {
// ...
2019-02-13 11:18:45 -03:00
}
2019-02-04 18:08:52 +02:00
fax() {
// ...
}
scan() {
// ...
}
}
2019-02-13 11:18:45 -03:00
class EconomicPrinter implements IPrinter {
2019-02-04 18:08:52 +02:00
print() {
// ...
}
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
# # # Princípio da Inversão de Dependência ( DIP ) ( Dependency Inversion Principle )
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
Esse princípio afirma duas coisas essenciais :
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
1 . Modulos de alto nível não deveriam ser dependentes de módulos de baixo nível . Ambos devem depender de abstrações .
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
2 . Abstrações não deveriam ser dependentes de detalhes . Detalhes devem depender de abstrações .
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
Isso pode ser dificil de entender de primeira , porém se você já trabalhou com Angular , você já viu a implementação desse principio na forma de Injeção de Dependência ( Dependency Injection ) . Entretanto , não são conceitos idênticos , DIP mantém modulos de alto nível conhecendo os detalhes dos módulos de baixo nível e os configura . Isso pode ser feito através da Injeção de Depedencia . Um grande benficio disso é que o acoplamento entre módulos é reduzido . Acoplamento é um padrão muito ruim de desenvolvimento porque faz o seu código ser dificil de refatorar .
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
DIP é normalmente alcançado através do uso de um container de controle de inversão ( IoC ) . Um exemplo de um container IoC poderoso para o TypeScript é o [ InversifyJs ] ( https : //www.npmjs.com/package/inversify)
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
import { readFile as readFileCb } from 'fs' ;
import { promisify } from 'util' ;
const readFile = promisify ( readFileCb ) ;
type ReportData = {
// ..
}
class XmlFormatter {
parse < T > ( content : string ) : T {
// Converts an XML string to an object T
}
}
class ReportReader {
// BAD: We have created a dependency on a specific request implementation.
// We should just have ReportReader depend on a parse method: `parse`
private readonly formatter = new XmlFormatter ( ) ;
async read ( path : string ) : Promise < ReportData > {
const text = await readFile ( path , 'UTF8' ) ;
return this . formatter . parse < ReportData > ( text ) ;
}
}
// ...
const reader = new ReportReader ( ) ;
await report = await reader . read ( 'report.xml' ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
import { readFile as readFileCb } from 'fs' ;
import { promisify } from 'util' ;
const readFile = promisify ( readFileCb ) ;
type ReportData = {
// ..
}
interface Formatter {
parse < T > ( content : string ) : T ;
}
class XmlFormatter implements Formatter {
parse < T > ( content : string ) : T {
// Converts an XML string to an object T
}
}
class JsonFormatter implements Formatter {
parse < T > ( content : string ) : T {
// Converts a JSON string to an object T
}
}
class ReportReader {
2019-02-13 11:18:45 -03:00
constructor ( private readonly formatter : Formatter ) {
2019-02-04 18:08:52 +02:00
}
async read ( path : string ) : Promise < ReportData > {
const text = await readFile ( path , 'UTF8' ) ;
return this . formatter . parse < ReportData > ( text ) ;
}
}
// ...
const reader = new ReportReader ( new XmlFormatter ( ) ) ;
await report = await reader . read ( 'report.xml' ) ;
2019-02-13 11:18:45 -03:00
// or if we had to read a json report:
2019-02-04 18:08:52 +02:00
const reader = new ReportReader ( new JsonFormatter ( ) ) ;
await report = await reader . read ( 'report.json' ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 18:08:52 +02:00
2019-02-13 11:18:45 -03:00
# # Testar
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
Testar é mais importante do que lançar o software . Se você não tem nenhum teste , ou poucos testes , todas as vezes que você lançar um software você não vai ter certeza de que não quebrou nada .
Decidir o que seria uma boa quantidade de testes é de responsabilidade do seu time , mas ter uma cobertura ampla das funcionalidades do seu código é como você atinge confiança e certa paz de espirito . Isso implica que , além de uma boa ferramente para fazer seus testes , precisa de algo para promover essa total cobertura .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
Não há desculpas para não escrever testes . Há uma vasta leva de frameworks para testar JavaScript com suporte a tipos com TypeScript , então ache um que seu time prefira . Quando achar um que funcione pro seu time , escreva testes para toda funcionalidade ou módulo que você introduzir . Se seu método preferido é o TDD ( Test Driven Development - Desenvolvimento guiado à testes ) , isso é ó timo , mas o ponto é você ter certeza do código que está lançando , antes de lança - lo , ou refatorar códigos antigos .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
# # # As três leis do TDD
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
1 . Você não pode escrever nenhum código de produção a não ser que seja para fazer um teste unitário quebrado , passar .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
2 . Você não pode escrever mais de um teste que falhe ; e erros de compilação são erros .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
3 . Você não pode esrever mais de um código de produção do que o suficiente para passar o teste unitário que falhou .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
# # # Regras F . I . R . S . T
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
Testes limpos devem seguir essas regras :
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
- * * Fast - Rápido * * testes devem ser rápidos pois queremos roda - los frequentemente .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
- * * Independent - Independente * * testes não devem depender um dos outros . Eles devem retornar algo , seja ele rodado sozinho ou com outros testes .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
- * * Repeatable - Repetitivos * * testes devem ser repetitivos em qualquer ambiente e não devem haver motivos para eles falharem .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
- * * Self - Validating - Auto - validado * * um teste devem responder com _Passed_ ou _Failed_ . Você não tem que comparar com arquivos de log para saber se passaram ou não .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
- * * Timely - Pontuais * * testes unitários devem ser escritos antes do código de produção . se você escrever depois , pode parar muito complicado escrever testes .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
# # # Ú nico conceito por teste
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
Testes devem seguir também o principio da ú nica responsabilidade . Faça apenas uma asserção por teste unitário .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
import { assert } from 'chai' ;
describe ( 'AwesomeDate' , ( ) = > {
it ( 'handles date boundaries' , ( ) = > {
let date : AwesomeDate ;
date = new AwesomeDate ( '1/1/2015' ) ;
2019-02-13 11:18:45 -03:00
date . addDays ( 30 ) ;
assert . equal ( '1/31/2015' , date ) ;
2019-02-04 09:18:53 +02:00
date = new AwesomeDate ( '2/1/2016' ) ;
2019-02-13 11:18:45 -03:00
date . addDays ( 28 ) ;
assert . equal ( '02/29/2016' , date ) ;
2019-02-04 09:18:53 +02:00
date = new AwesomeDate ( '2/1/2015' ) ;
2019-02-13 11:18:45 -03:00
date . addDays ( 28 ) ;
assert . equal ( '03/01/2015' , date ) ;
2019-02-04 09:18:53 +02:00
} ) ;
} ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
import { assert } from 'chai' ;
describe ( 'AwesomeDate' , ( ) = > {
it ( 'handles 30-day months' , ( ) = > {
const date = new AwesomeDate ( '1/1/2015' ) ;
2019-02-13 11:18:45 -03:00
date . addDays ( 30 ) ;
assert . equal ( '1/31/2015' , date ) ;
2019-02-04 09:18:53 +02:00
} ) ;
it ( 'handles leap year' , ( ) = > {
const date = new AwesomeDate ( '2/1/2016' ) ;
2019-02-13 11:18:45 -03:00
date . addDays ( 28 ) ;
assert . equal ( '02/29/2016' , date ) ;
2019-02-04 09:18:53 +02:00
} ) ;
it ( 'handles non-leap year' , ( ) = > {
const date = new AwesomeDate ( '2/1/2015' ) ;
2019-02-13 11:18:45 -03:00
date . addDays ( 28 ) ;
assert . equal ( '03/01/2015' , date ) ;
2019-02-04 09:18:53 +02:00
} ) ;
} ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
# # # O nome do teste deve revelar sua intenção
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
Quando um teste falha , o seu nome é a primeira indicação do que deu errado .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
describe ( 'Calendar' , ( ) = > {
it ( '2/29/2020' , ( ) = > {
// ...
} ) ;
it ( 'throws' , ( ) = > {
// ...
} ) ;
} ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
describe ( 'Calendar' , ( ) = > {
it ( 'should handle leap year' , ( ) = > {
// ...
} ) ;
it ( 'should throw when format is invalid' , ( ) = > {
// ...
} ) ;
} ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
# # Concorrência
2019-01-29 17:25:00 +02:00
2019-02-13 11:18:45 -03:00
# # # Prefira promises à callbacks
2019-01-29 17:25:00 +02:00
2019-02-13 11:18:45 -03:00
Callbacks não são claros , e eles causam uma quantidade desnecessária de agrupamento _ ( callback hell ) _ .
Há utilitários que transformam funções existentes que usam callbacks para uma versão que retorna promises
( Em Node . js há [ ` util.promisify ` ] ( https : //nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original), para propositos gerais, veja [pify](https://www.npmjs.com/package/pify), [es6-promisify](https://www.npmjs.com/package/es6-promisify))
2019-01-29 17:25:00 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-29 17:25:00 +02:00
` ` ` ts
import { get } from 'request' ;
import { writeFile } from 'fs' ;
2019-02-13 11:18:45 -03:00
function downloadPage (
url : string ,
saveTo : string ,
callback : ( error : Error , content? : string ) = > void
) {
2019-01-29 17:25:00 +02:00
get ( url , ( error , response ) = > {
if ( error ) {
callback ( error ) ;
} else {
2019-02-13 11:18:45 -03:00
writeFile ( saveTo , response . body , error = > {
2019-01-29 17:25:00 +02:00
if ( error ) {
callback ( error ) ;
} else {
callback ( null , response . body ) ;
}
} ) ;
}
2019-02-06 10:43:37 +02:00
} ) ;
2019-01-29 17:25:00 +02:00
}
2019-02-13 11:18:45 -03:00
downloadPage (
'https://en.wikipedia.org/wiki/Robert_Cecil_Martin' ,
'article.html' ,
( error , content ) = > {
if ( error ) {
console . error ( error ) ;
} else {
console . log ( content ) ;
}
2019-01-29 17:25:00 +02:00
}
2019-02-13 11:18:45 -03:00
) ;
2019-01-29 17:25:00 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-29 17:25:00 +02:00
` ` ` ts
import { get } from 'request' ;
import { writeFile } from 'fs' ;
import { promisify } from 'util' ;
const write = promisify ( writeFile ) ;
function downloadPage ( url : string , saveTo : string ) : Promise < string > {
2019-02-13 11:18:45 -03:00
return get ( url ) . then ( response = > write ( saveTo , response ) ) ;
2019-01-29 17:25:00 +02:00
}
2019-02-13 11:18:45 -03:00
downloadPage (
'https://en.wikipedia.org/wiki/Robert_Cecil_Martin' ,
'article.html'
)
2019-01-29 17:25:00 +02:00
. then ( content = > console . log ( content ) )
2019-02-13 11:18:45 -03:00
. catch ( error = > console . error ( error ) ) ;
2019-01-29 17:25:00 +02:00
` ` `
2019-02-13 11:18:45 -03:00
Promises suportam alguns padrões que podem ser ú teis em alguns casos :
2019-01-31 10:18:41 +02:00
2019-02-13 11:18:45 -03:00
| Padrão | Descrição |
| -- -- -- -- -- -- -- -- -- -- -- -- | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - |
| ` Promise.resolve(value) ` | Converte um valor para uma promise resolvida . |
| ` Promise.reject(error) ` | Converte um erro para uma promise rejeitada . |
| ` Promise.all(promises) ` | Retorna uma nova promise , que é preenchida com um array of fulfillment values for the passed promises or rejects with the reason of the first promise that rejects . |
| ` Promise.race(promises) ` | Returns a new promise which is fulfilled / rejected with the result / error of the first settled promise from the array of passed promises . |
2019-01-31 10:18:41 +02:00
` Promise.all ` is especially useful when there is a need to run tasks in parallel . ` Promise.race ` makes it easier to implement things like timeouts for promises .
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-29 17:25:00 +02:00
2019-02-13 11:18:45 -03:00
# # # Async / Await são ainda mais claros do que Promises
2019-01-29 17:25:00 +02:00
2019-02-13 11:18:45 -03:00
Com a sintaxe Async / Await você pode escrever um código muito mais claro e compreensível do que com promises . Dentro de uma função prefixada com ` async ` você tem uma maneira de dizer ao tempo de execução do JavaScript pausar a execução do código quando utilizado o prefixo ` await ` ( quando usando em um promise ) .
2019-01-29 17:25:00 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-29 17:25:00 +02:00
` ` ` ts
import { get } from 'request' ;
import { writeFile } from 'fs' ;
import { promisify } from 'util' ;
const write = util . promisify ( writeFile ) ;
function downloadPage ( url : string , saveTo : string ) : Promise < string > {
2019-02-06 10:48:34 +02:00
return get ( url ) . then ( response = > write ( saveTo , response ) ) ;
2019-01-29 17:25:00 +02:00
}
2019-02-13 11:18:45 -03:00
downloadPage (
'https://en.wikipedia.org/wiki/Robert_Cecil_Martin' ,
'article.html'
)
2019-01-29 17:25:00 +02:00
. then ( content = > console . log ( content ) )
2019-02-13 11:18:45 -03:00
. catch ( error = > console . error ( error ) ) ;
2019-01-29 17:25:00 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-29 17:25:00 +02:00
` ` ` ts
import { get } from 'request' ;
import { writeFile } from 'fs' ;
import { promisify } from 'util' ;
const write = promisify ( writeFile ) ;
async function downloadPage ( url : string , saveTo : string ) : Promise < string > {
const response = await get ( url ) ;
await write ( saveTo , response ) ;
return response ;
}
2019-02-13 11:18:45 -03:00
// somewhere in an async function (Alguma parte de uma função async)
2019-01-29 17:25:00 +02:00
try {
2019-02-13 11:18:45 -03:00
const content = await downloadPage (
'https://en.wikipedia.org/wiki/Robert_Cecil_Martin' ,
'article.html'
) ;
2019-01-29 17:25:00 +02:00
console . log ( content ) ;
} catch ( error ) {
console . error ( error ) ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 09:18:53 +02:00
2019-02-13 11:22:04 -03:00
# # Tratamento de erros
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
Erros lançados são uma coisa boa ! Eles significam que o tempo de execução identificou com sucesso quando alguma coisa no seu programa deu errado e está deixando você saber parando a execução da função atual , matando o processo ( em Node ) , e notificando você no console .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
# # # Sempre use Erro para jogar ( throwing ) ou rejeitar ( rejecting )
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
JavaScript assim como o TypeScript te permite jogar ( ` throw ` ) qualquer objeto . Um promise também ser rejeitado com qualquer objeto de razão .
É aconselhavel usar a sintaxe jogar ( ` throw ` ) com um tipo Erro ( ` Error ` ) . Isto é porque seu erro pode ser pego em um código de alto nível com uma sintaxe pegar ( ` catch ` ) .
Seria muito confuso pegar uma mensagem string la e faria
[ debugar mais doloroso ] ( https : //basarat.gitbooks.io/typescript/docs/types/exceptions.html#always-use-error).
Pela mesma razão você deveria rejeitar promises com tipo de erro ( ` Error ` ) .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
function calculateTotal ( items : Item [ ] ) : number {
throw 'Not implemented.' ;
}
function get ( ) : Promise < Item [ ] > {
return Promise . reject ( 'Not implemented.' ) ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
function calculateTotal ( items : Item [ ] ) : number {
throw new Error ( 'Not implemented.' ) ;
}
function get ( ) : Promise < Item [ ] > {
return Promise . reject ( new Error ( 'Not implemented.' ) ) ;
}
2019-02-13 11:18:45 -03:00
// Ou equivalente a:
2019-02-04 09:18:53 +02:00
async function get ( ) : Promise < Item [ ] > {
throw new Error ( 'Not implemented.' ) ;
}
` ` `
2019-02-13 11:18:45 -03:00
O benefício de user o tipo ` Error ` é que este é suportado por ` try/catch/finally ` e implicitamente todos os erros tem a propriedade ` stack ` , que é uma ferramenta poderosa para debug .
Há também outras alternativas , não usar ` throw ` e , ao invés disso , sempre retornar objetos de erro .
TypeScript deixa isso ainda mais fácil . Considere o exemplo abaixo :
2019-02-05 14:22:02 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
type Result < R > = { isError : false ; value : R } ;
type Failure < E > = { isError : true ; error : E } ;
2019-02-05 17:21:31 +02:00
type Failable < R , E > = Result < R > | Failure < E > ;
2019-02-05 14:22:02 +02:00
function calculateTotal ( items : Item [ ] ) : Failable < number , 'empty' > {
if ( items . length === 0 ) {
return { isError : true , error : 'empty' } ;
}
// ...
return { isError : false , value : 42 } ;
}
` ` `
2019-02-13 11:18:45 -03:00
Para entender mais disso , leia a [ publicação original ] ( https : //medium.com/@dhruvrajvanshi/making-exceptions-type-safe-in-typescript-c4d200ee78e9).
2019-02-05 14:22:02 +02:00
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
# # # Não ignore erros capturados
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
Não fazer nada com um erro capturado não te da a habilidade para consertar ou reagir ao erro . Mostrar o erro no console ( ` console.log ` ) não é muito bom , já que fácilmente pode ser perdido no meio de tanta coisa mostrada no console . Se você coloca todo pedaço de código em um ` try/catch ` , significa que você acha que um erro pode acontecer , então você deve ter um plano , ou criar uma saída , pra quando acontecer .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
try {
functionThatMightThrow ( ) ;
} catch ( error ) {
console . log ( error ) ;
}
2019-02-13 11:18:45 -03:00
// ou ainda pior
2019-02-04 09:18:53 +02:00
try {
functionThatMightThrow ( ) ;
} catch ( error ) {
2019-02-13 11:18:45 -03:00
// ignorar o erro
2019-02-04 09:18:53 +02:00
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
import { logger } from './logging' ;
2019-02-04 09:18:53 +02:00
try {
functionThatMightThrow ( ) ;
} catch ( error ) {
logger . log ( error ) ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
# # # Não ignore promises rejeitadas
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
Pelo mesmo motivo que você não deve ignorar erros que vem do ` try/catch ` .
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
getUser ( )
. then ( ( user : User ) = > {
return sendEmail ( user . email , 'Welcome!' ) ;
} )
2019-02-13 11:18:45 -03:00
. catch ( error = > {
2019-02-04 09:18:53 +02:00
console . log ( error ) ;
} ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
import { logger } from './logging' ;
2019-02-04 09:18:53 +02:00
getUser ( )
. then ( ( user : User ) = > {
return sendEmail ( user . email , 'Welcome!' ) ;
} )
2019-02-13 11:18:45 -03:00
. catch ( error = > {
2019-02-04 09:18:53 +02:00
logger . log ( error ) ;
} ) ;
2019-02-13 11:18:45 -03:00
// ou usando async/await:
2019-02-04 09:18:53 +02:00
try {
const user = await getUser ( ) ;
await sendEmail ( user . email , 'Welcome!' ) ;
} catch ( error ) {
logger . log ( error ) ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
# # Formatação
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
Formatação é subjetivo . Como todas outras regras aqui , não há uma mais rápida ou mais difícil que você deva seguir . O ponto principal é _NÃO ARGUMENTE_ formatação do código . Há várias ferramentas que automatizam isso . Escolha uma ! É uma perda de tempo e dinheiro para engenheiros ( de software ) argumentar em cima de formatação de código . A regra geral é _seguir e manter consistente as regras de formatação_ .
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
Em TypeScript , tem uma ó tima ferramenta chamada [ TSLint ] ( https : //palantir.github.io/tslint/). É uma ferramenta de análise estática que pode te ajudar a melhorar drásticamente a legibilidade e manutenibilidade do seu código. Há algumas configurações prontas para serem utilizadas com TSLint.
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
- [ TSLint Config Standard ] ( https : //www.npmjs.com/package/tslint-config-standard) - Configurações padrões
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
- [ TSLint Config Airbnb ] ( https : //www.npmjs.com/package/tslint-config-airbnb) - Configurações utilizadas no Airbnb
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
- [ TSLint Clean Code ] ( https : //www.npmjs.com/package/tslint-clean-code) - Regras inspiradas no livro [Clean Code: A Handbook of Agile Software Craftsmanship](https://www.amazon.ca/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882)
2019-02-04 09:22:16 +02:00
2019-02-13 11:18:45 -03:00
- [ TSLint react ] ( https : //www.npmjs.com/package/tslint-react) - Regras para React e JSX
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
- [ TSLint + Prettier ] ( https : //www.npmjs.com/package/tslint-config-prettier) - Regras para serem usadas com o [Prettier](https://github.com/prettier/prettier).
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
- [ ESLint rules for TSLint ] ( https : //www.npmjs.com/package/tslint-eslint-rules) - Regras do ESLint p/ TypeScript
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
- [ Immutable ] ( https : //www.npmjs.com/package/tslint-immutable) - Regras para desabilitar mutações no TypeScript
2019-02-04 09:18:53 +02:00
2019-02-13 11:18:45 -03:00
Vale mencionar também este ó timo artigo [ TypeScript StyleGuide and Coding Conventions ] ( https : //basarat.gitbooks.io/typescript/docs/styleguide/styleguide.html).
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
# # # Capitalize de forma consistente
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
Capitalização diz muito sobre suas variáveis , funções , etc . Essas regras são subjetivas , seu time pode escolher o que eles quiserem . Indepentente da forma que escolherem , _sejam consistentes_ .
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-04 09:16:01 +02:00
` ` ` ts
const DAYS_IN_WEEK = 7 ;
const daysInMonth = 30 ;
const songs = [ 'Back In Black' , 'Stairway to Heaven' , 'Hey Jude' ] ;
const Artists = [ 'ACDC' , 'Led Zeppelin' , 'The Beatles' ] ;
function eraseDatabase() { }
function restore_database() { }
2019-02-13 11:18:45 -03:00
class animal { }
class Container { }
2019-02-04 09:16:01 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 09:16:01 +02:00
` ` ` ts
const DAYS_IN_WEEK = 7 ;
const DAYS_IN_MONTH = 30 ;
const SONGS = [ 'Back In Black' , 'Stairway to Heaven' , 'Hey Jude' ] ;
const ARTISTS = [ 'ACDC' , 'Led Zeppelin' , 'The Beatles' ] ;
function eraseDatabase() { }
function restoreDatabase() { }
2019-02-13 11:18:45 -03:00
class Animal { }
class Container { }
2019-02-04 09:16:01 +02:00
` ` `
2019-02-13 11:18:45 -03:00
Use ` PascalCase ` para classes , interfaces , tipos e namespaces .
Use ` camelCase ` para variáveis , funções e classes .
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 09:16:01 +02:00
# # # Function callers and callees should be close
2019-02-13 11:18:45 -03:00
# # # Funções que chamam outras funções , ou que são chamadas , devem estar próximas uma das outras .
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
Se uma função chama outra , mantenha essas funções verticalmente perto no seu arquivo . Idealmente , mantenha a função que chama a outra , logo acima da função chamada .
Tendemos a ler código de cima para baixo , como um jornal . Por conta disso , faça seu código ser fácil de ler dessa maneira .
* * Ruim : * *
2019-02-04 09:16:01 +02:00
` ` ` ts
class PerformanceReview {
2019-02-13 11:18:45 -03:00
constructor ( private readonly employee : Employee ) { }
2019-02-04 09:16:01 +02:00
private lookupPeers() {
return db . lookup ( this . employee . id , 'peers' ) ;
}
private lookupManager() {
return db . lookup ( this . employee , 'manager' ) ;
}
private getPeerReviews() {
const peers = this . lookupPeers ( ) ;
// ...
}
review() {
this . getPeerReviews ( ) ;
this . getManagerReview ( ) ;
this . getSelfReview ( ) ;
// ...
}
private getManagerReview() {
const manager = this . lookupManager ( ) ;
}
private getSelfReview() {
// ...
}
}
const review = new PerformanceReview ( employee ) ;
review . review ( ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-04 09:16:01 +02:00
` ` ` ts
class PerformanceReview {
2019-02-13 11:18:45 -03:00
constructor ( private readonly employee : Employee ) { }
2019-02-04 09:16:01 +02:00
review() {
this . getPeerReviews ( ) ;
this . getManagerReview ( ) ;
this . getSelfReview ( ) ;
// ...
}
private getPeerReviews() {
const peers = this . lookupPeers ( ) ;
// ...
}
private lookupPeers() {
return db . lookup ( this . employee . id , 'peers' ) ;
}
private getManagerReview() {
const manager = this . lookupManager ( ) ;
}
private lookupManager() {
return db . lookup ( this . employee , 'manager' ) ;
2019-02-12 08:40:50 +02:00
}
2019-02-04 09:16:01 +02:00
private getSelfReview() {
// ...
}
}
const review = new PerformanceReview ( employee ) ;
review . review ( ) ;
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 09:16:01 +02:00
2019-02-13 11:18:45 -03:00
# # # type vs . interface
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
Use ` type ` quando você precisar de uma união ou interseção . Use interface quando precisar usar ` extends ` ou ` implements ` . Não há uma regra a ser seguida , entretanto . Use aquela que funciona para você .
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
Dê uma olhada nesta [ explicação ] ( https : //stackoverflow.com/questions/37233735/typescript-interfaces-vs-types/54101543#54101543) sobre as diferenças entre `type` e `interface`.
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-02-11 10:47:27 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
interface EmailConfig {
// ...
}
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
interface DbConfig {
// ...
}
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
interface Config {
// ...
}
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
//...
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
type Shape {
// ...
}
2019-02-11 10:47:27 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
` ` ` ts
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
type EmailConfig {
// ...
}
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
type DbConfig {
// ...
}
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
type Config = EmailConfig | DbConfig ;
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
// ...
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
interface Shape {
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
}
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
class Circle implements Shape {
// ...
}
class Square implements Shape {
// ...
}
2019-02-11 10:47:27 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-13 19:13:20 -03:00
# # # Organize imports
Com declarações de import fáceis e limpas de ler , você consegue rapidamente ver as dependências do seu código . Assegure - se de que está aplicando as boas práticas para fazer imports :
- Imports devem ser em ordem alfabética e agrupados .
- Imports não utilizadas tem de ser removidos .
- Imports com nomes devem estar em ordem alfabética ( ex . : ` import {A, B, C} from 'foo'; ` ) .
- As fontes do seu imports devem estar em ordem alfabética dividido em grupos , ex . : ` import * as foo from 'a'; import * as bar from 'b'; `
- Grupos de imports são separados por espaços em branco .
- Grupos devem respeitar a seguinte ordem :
- polyfills ( ex . : ` import 'reflect-metadata'; ` )
- módulos do node ( ex . : ` import fs from 'fs'; ` )
- módulos externos ( ex . : ` import { query } from 'itiriri'; ` )
- módulos internos ( ex . : ` import { UserService } from 'src/services/userService'; ` )
- módulos de um diretório pai ( ex . : ` import foo from '../foo'; import qux from '../../foo/qux'; ` )
- módulos de um mesmo diretório ( ex . : ` import bar from './bar'; import baz from './bar/baz'; ` )
* * Ruim : * *
` ` ` ts
import { TypeDefinition } from '../types/typeDefinition' ;
import { AttributeTypes } from '../model/attribute' ;
import { ApiCredentials , Adapters } from './common/api/authorization' ;
import fs from 'fs' ;
import { ConfigPlugin } from './plugins/config/configPlugin' ;
import { BindingScopeEnum , Container } from 'inversify' ;
import 'reflect-metadata' ;
` ` `
* * Bom : * *
` ` ` ts
import 'reflect-metadata' ;
import fs from 'fs' ;
import { BindingScopeEnum , Container } from 'inversify' ;
import { AttributeTypes } from '../model/attribute' ;
import { TypeDefinition } from '../types/typeDefinition' ;
import { ApiCredentials , Adapters } from './common/api/authorization' ;
import { ConfigPlugin } from './plugins/config/configPlugin' ;
` ` `
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-13 19:15:45 -03:00
# # # Use alias do typescript
Faça imports mais agradáveis definindo caminhos e a propriedade baseUrl na seção compilerOptions em ` tsconfig.json `
Isso irá evitar caminhos relativos longos quando fizer imports .
* * Ruim : * *
` ` ` ts
import { UserService } from '../../../services/UserService' ;
` ` `
* * Bom : * *
` ` ` ts
import { UserService } from '@services/UserService' ;
` ` `
` ` ` js
// tsconfig.json
. . .
"compilerOptions" : {
. . .
"baseUrl" : "src" ,
"paths" : {
"@services" : [ "services/*" ]
}
. . .
}
. . .
` ` `
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-13 11:18:45 -03:00
# # Comentários
2019-02-11 10:47:27 +02:00
2019-02-13 11:18:45 -03:00
O uso de comentários é uma indicação que você falhou ao se expressar sem eles . Seu código deve ser sua ú nica fonte .
2019-01-28 17:56:21 +02:00
2019-02-13 11:18:45 -03:00
> Don ’ t comment bad code — rewrite it . ( Não comente código ruim - Reescreva - o . )
> — _Brian W . Kernighan and P . J . Plaugher_
2019-01-28 17:56:21 +02:00
2019-02-13 11:18:45 -03:00
# # # Use código que se auto - explica ao invés de comentários
2019-01-28 17:56:21 +02:00
2019-02-13 11:18:45 -03:00
Comentários são desculpas , não um requisito . Bom código normalmente se auto - documentam .
2019-01-28 17:56:21 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-28 17:56:21 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
// Checa se subscriptions estão ativas.
if ( subscription . endDate > Date . now ) {
}
2019-01-28 17:56:21 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-28 17:56:21 +02:00
` ` ` ts
2019-01-28 17:58:45 +02:00
const isSubscriptionActive = subscription . endDate > Date . now ;
2019-02-13 11:18:45 -03:00
if ( isSubscriptionActive ) {
/* ... */
}
2019-01-28 17:56:21 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-28 17:56:21 +02:00
2019-02-13 11:18:45 -03:00
# # # Não deixe código comentado na sua base de código ( codebase )
2019-01-28 17:56:21 +02:00
2019-02-13 11:18:45 -03:00
Versionamento de código existe por um motivo . Deixe código antigo no seu histórico .
2019-01-28 17:56:21 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-28 17:56:21 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
class User {
2019-01-28 17:56:21 +02:00
name : string ;
email : string ;
// age: number;
// jobPosition: string;
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-28 17:56:21 +02:00
` ` ` ts
2019-02-13 11:18:45 -03:00
class User {
2019-01-28 17:56:21 +02:00
name : string ;
email : string ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-04 18:18:27 +02:00
2019-02-13 11:18:45 -03:00
# # # Não tenha códigos que marcam datas
2019-01-28 17:56:21 +02:00
2019-02-13 11:18:45 -03:00
Lembre - se , versione seu código ! Não há motivo para manter código morto , comentado , e especialmente , datado . Use ` git log ` para ter o histórico .
2019-01-28 17:56:21 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-28 17:56:21 +02:00
` ` ` ts
/ * *
* 2016 - 12 - 20 : Removed monads , didn ' t understand them ( RM )
* 2016 - 10 - 01 : Improved using special monads ( JP )
* 2016 - 02 - 03 : Added type - checking ( LI )
* 2015 - 03 - 14 : Implemented combine ( JR )
* /
2019-02-06 10:43:37 +02:00
function combine ( a : number , b : number ) : number {
2019-01-28 17:56:21 +02:00
return a + b ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-28 17:56:21 +02:00
` ` ` ts
2019-02-06 10:43:37 +02:00
function combine ( a : number , b : number ) : number {
2019-01-28 17:56:21 +02:00
return a + b ;
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-01-28 17:56:21 +02:00
2019-02-13 11:18:45 -03:00
# # # Evite marcadores posicionados
2019-01-28 17:56:21 +02:00
2019-02-13 11:18:45 -03:00
Normalmente eles só sujam o código . Deixe que os nomes das funções e varáveis , juntamente com uma identação e formatação apropriada , dê uma boa estrutura visual no seu código .
Opcionalmente você pode usar o suporte da sua IDE para _code folding_ ( VSCode [ folding regions ] ( https : //code.visualstudio.com/updates/v1_17#_folding-regions))
2019-01-28 17:56:21 +02:00
2019-02-13 11:18:45 -03:00
* * Ruim : * *
2019-01-28 17:56:21 +02:00
` ` ` ts
////////////////////////////////////////////////////////////////////////////////
// Client class
////////////////////////////////////////////////////////////////////////////////
class Client {
id : number ;
name : string ;
address : Address ;
contact : Contact ;
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
public describe ( ) : string {
// ...
}
////////////////////////////////////////////////////////////////////////////////
// private methods
////////////////////////////////////////////////////////////////////////////////
private describeAddress ( ) : string {
// ...
}
private describeContact ( ) : string {
// ...
}
2019-02-13 11:18:45 -03:00
}
2019-01-28 17:56:21 +02:00
` ` `
2019-02-13 11:18:45 -03:00
* * Bom : * *
2019-01-28 17:56:21 +02:00
` ` ` ts
class Client {
id : number ;
name : string ;
address : Address ;
contact : Contact ;
public describe ( ) : string {
// ...
}
private describeAddress ( ) : string {
// ...
}
private describeContact ( ) : string {
// ...
}
2019-02-09 22:31:00 +02:00
}
` ` `
2019-02-13 11:18:45 -03:00
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-12 08:48:13 +02:00
2019-02-13 19:05:14 -03:00
# # # Comentários de TODO
Quando você reparar que precisa deixar notas no código , para alterar coisas futuramente , faça isso utilizando comentários ` // TODO ` . A maioria das IDEs tem um suporte especial para esse tipo de comentário para que possa voltar nesses comentários mais fácilmente depois .
Tenha em mente que * TODO * s não são uma desculpa para código ruim .
* * Ruim : * *
` ` ` ts
function getActiveSubscriptions ( ) : Promise < Subscription [ ] > {
// garantir que `dueDate` está indexado
return db . subscriptions . find ( { dueDate : { $lte : new Date ( ) } } ) ;
}
` ` `
* * Bom : * *
` ` ` ts
function getActiveSubscriptions ( ) : Promise < Subscription [ ] > {
// TODO: garantir que `dueDate` está indexado.
return db . subscriptions . find ( { dueDate : { $lte : new Date ( ) } } ) ;
}
` ` `
* * [ ⬆ ir para o topo ] ( # table - of - contents ) * *
2019-02-13 11:22:04 -03:00
Um grande obrigado ao meu amigo [ Luís Gustavo ] ( https : //github.com/lgustavogdc) que me deu uma força ao traduzir esse conteúdo maravilhoso!