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-12-12 00:17:35 +07:00
К о н ц е п ц и и ч и с т о г о к о д а а д а п т и р о в а н н ы е д л я TypeScript , в д о х н о в л е н н ы е [ clean - code - javascript ] ( https : //github.com/ryanmcdermott/clean-code-javascript).
2019-01-26 10:52:23 +02:00
2019-12-12 00:17:35 +07:00
О р и г и н а л н а а н г л и й с к о м [ clean - code - typescript ] ( https : //github.com/labs42io/clean-code-typescript)
2019-01-29 09:44:59 +02:00
2019-12-28 21:56:47 +07:00
* У п е р е в о д ч и к а н е и д е а л ь н ы е з н а н и я а н г л и й с к о г о , у к а з ы в а й т е п о ж а л у й с т а н а [ о ш и б к и ] ( https : //github.com/Real001/clean-code-typescript/issues/5)!*
2019-12-12 00:17:35 +07:00
# # С о д е р ж а н и е
2019-12-12 23:36:27 +07:00
1 . [ В в е д е н и е ] ( # в в е д е н и е )
2019-12-12 23:43:25 +07:00
2 . [ П е р е м е н н ы е ] ( # п е р е м е н н ы е )
2019-12-25 22:42:36 +07:00
3 . [ Ф у н к ц и и ] ( # ф у н к ц и и )
4 . [ О б ъ е к т ы и с т р у к т у р ы д а н н ы х ] ( # о б ъ е к т ы - и - с т р у к т у р ы - д а н н ы х )
2019-12-28 21:54:44 +07:00
5 . [ К л а с с ы ] ( # к л а с с ы )
2019-02-04 18:08:52 +02:00
6 . [ SOLID ] ( # solid )
2019-12-28 21:54:44 +07:00
7 . [ Т е с т и р о в а н и е ] ( # т е с т и р о в а н и е )
8 . [ А с и н х р о н н о с т ь ] ( # а с и н х р о н н о с т ь )
9 . [ О б р а б о т к а о ш и б о к ] ( # о б р а б о т к а - о ш и б о к )
10 . [ Ф о р м а т и р о в а н и е ] ( # ф о р м а т и р о в а н и е )
11 . [ К о м м е н т а р и и ] ( # к о м м е н т а р и и )
12 . [ П е р е в о д ы ] ( # п е р е в о д ы )
2019-01-26 10:52:23 +02:00
2019-12-12 00:17:35 +07:00
# # В в е д е н и е
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-12-12 00:17:35 +07:00
И н ж е н е р н ы е п р и н ц и п ы П О , и з к н и г и Robert C . Martin '
2019-01-26 10:52:23 +02:00
[ * Clean Code * ] ( https : //www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882),
2019-12-12 00:17:35 +07:00
а д а п т и р о в а н н ы е д л я TypeScript . Э т о н е р у к о в о д с т в о п о с т и л ю . Э т о р у к о в о д с т в о п о н а п и с а н и ю
[ ч и т а е м о г о , п е р е и с п о л ь з у е м о г о и п р и г о д н о г о д л я р е ф а к т о р и н г а ] ( https : //github.com/ryanmcdermott/3rs-of-software-architecture) кода на TypeScript.
Н е к а ж д ы й п р и н ц и п о п и с а н н ы й з д е с ь д о л ж е н с т р о г о с о б л ю д а т ь с я и е щ е м е н ь ш е п о л у ч а т ь
в с е о б щ е г о п р и з н а н и й . Э т о п р и н ц и п ы и н и ч е г о б о л е е , о н о о н и н а к а п л и в а л и с ь в т е ч е н и е
м н о г и х л е т с о п ы т о м к о л л е к т и в а а в т о р о в * Ч и с т о г о К о д а *
Р е м е с л у п о н а п и с а н и ю п р о г р а м м н о г о о б е с п е ч е н и я ч у т ь б о л е е 50 л е т , н о м ы в с е е щ е м н о г о м у у ч и м с я .
К о г д а п р о г р а м м н а я а р х и т е к т у р а п о с т а р е е т д о в о з р а с т р а с а м о й а р х и т е к т у р ы , б ы т ь м о ж е т т о г д а у н а с
п о я в я т с я ж е с т к и е п р а в и л а к о т о р ы м н е о б х о д и м о с л е д о в а т ь . А с е й ч а с п у с т ь э т о с л у ж и т к р и т е р и я м и ,
с п о м о щ ь ю к о т о р о г о в ы б у д е т е о ц е н и в а т ь к а ч е с т в о в а ш е г о TypeScript к о д а и в а ш е й к о м а н д ы .
И е щ е о д н а вещь : знание э т и х п р и н ц и п о в н е д е л а е т в а с с р а з у л у ч ш и м р а з р а б о т ч и к о м П О , а и х и с п о л ь з о в а н и е в
т е ч е н и е м н о г и х л е т н е г а р а н т и р у е т , ч т о в ы н е б у д е т е с о в е р ш а т ь о ш и б к и . К а ж д ы й к у с о к к о д а н а ч и н а е т с я к а к
ч е р н о в и к , к а к м о к р ы й к у с о к г л и н ы к о т о р ы й т о л ь к о п о с т е м е н н о п р и о б р е т а е т с в о ю ф о р м у . Н е у п р е к а й т е с е б я п р и
п е р в ы х н а б р о с к а х к о д а , к о т о р ы е н у ж д а ю т с я в у л у ч ш е н и и . У л у ч ш а й т е к о д в м е с т о э т о г о !
2019-12-12 23:36:27 +07:00
* * [ ⬆ В е р н у т ь с я в н а ч а л о ] ( # с о д е р ж а н и е ) * *
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
# # П е р е м е н н ы е
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
# # # И с п о л ь з у й т е в ы р а з и т е л ь н ы е и м е н а п е р е м е н н ы х
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
Р а з л и ч а й т е и м е н а т а к и м о б р а з о м , ч т о б ы ч и т а т е л ь з н а л ч т о о н и о з н а ч а ю т .
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
* * П л о х о : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
2019-02-06 09:57:52 +02:00
function between < T > ( a1 : T , a2 : T , a3 : T ) : boolean {
2019-01-28 17:55:17 +02:00
return a2 <= a1 && a1 <= a3 ;
}
` ` `
2019-12-12 23:36:27 +07:00
* * Х о р о ш о : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
2019-02-06 09:57:52 +02:00
function between < T > ( value : T , left : T , right : T ) : boolean {
2019-01-28 17:55:17 +02:00
return left <= value && value <= right ;
}
` ` `
2019-12-12 23:43:25 +07:00
* * [ ⬆ В е р н у т ь с я в н а ч а л о ] ( # с о д е р ж а н и е ) * *
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
# # # И с п о л ь з у й т е п р о и з н о с и т е л ь н ы е и м е н а п е р е м е н н ы х
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
Е с л и в ы н е м о ж е т е п р о и з н о с и т ь и х , в ы н е м о ж е т е о б с у ж д а т ь и х н е в ы г л я д я к а к и д и о т .
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
* * П л о х о : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
2019-02-06 09:57:52 +02:00
type DtaRcrd102 = {
genymdhms : Date ;
modymdhms : Date ;
pszqint : number ;
2019-01-28 17:55:17 +02:00
}
` ` `
2019-12-12 23:36:27 +07:00
* * Х о р о ш о : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
2019-02-06 09:57:52 +02:00
type Customer = {
generationTimestamp : Date ;
modificationTimestamp : Date ;
recordId : number ;
2019-01-28 17:55:17 +02:00
}
` ` `
2019-12-12 23:43:25 +07:00
* * [ ⬆ В е р н у т ь с я в н а ч а л о ] ( # с о д е р ж а н и е ) * *
2019-02-04 18:18:27 +02:00
2019-12-12 23:36:27 +07:00
# # # И с п о л ь з у й т е о д и н и т о т ж е с л о в а р ь д л я о д н и х и т е х ж е т и п о в п е р е м е н н ы х
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
* * П л о х о : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
function getUserInfo ( ) : User ;
function getUserDetails ( ) : User ;
function getUserData ( ) : User ;
` ` `
2019-12-12 23:36:27 +07:00
* * Х о р о ш о : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
function getUser ( ) : User ;
` ` `
2019-12-12 23:43:25 +07:00
* * [ ⬆ В е р н у т ь с я в н а ч а л о ] ( # с о д е р ж а н и е ) * *
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
# # # И с п о л ь з у й т е и м е н а , д о с т у п н ы е д л я п о и с к а
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
М ы ч и т а е м б о л ь щ е к о д а , ч е м п и ш е м . Э т о в а ж н о ч т о б ы к о д , к о т о р ы й м ы п и ш е м , б ы л ч и т а е м ы м и д о с т у м н ы м д л я п о и с к а .
Н е н а з ы в а й т е п е р е м е н н ы е , к о т о р ы е в к о н е ч н о м и т о е и м е ю т с м ы с л т о л ь к о д л я н а ш и х п р о г р а м м м ы в р е д и м н а ш и м ч и т а т е л я м .
Д е л а й т е в а ш и и м е н а д о с т у п н ы м и д л я п о и с к а .
Т а к и е и н с т р у м е н т ы , к а к [ TSLint ] ( https : //palantir.github.io/tslint/rules/no-magic-numbers/) и [ESLint](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-magic-numbers.md)
м о г у т п о м о ч ь и д е н т и ф и ц и р о в а т ь н е н а з в а н н ы е к о н с т а н т ы .
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
* * П л о х о : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
// What the heck is 86400000 for?
setTimeout ( restart , 86400000 ) ;
` ` `
2019-12-12 23:36:27 +07:00
* * Х о р о ш о : * *
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-12-12 23:43:25 +07:00
* * [ ⬆ В е р н у т ь с я в н а ч а л о ] ( # с о д е р ж а н и е ) * *
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
# # # И с п о л ь з у й т е о б ъ я с н я ю щ и е п е р е м е н н ы е
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
* * П л о х о : * *
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-12-12 23:36:27 +07:00
* * Х о р о ш о : * *
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-12-12 23:43:25 +07:00
* * [ ⬆ В е р н у т ь с я в н а ч а л о ] ( # с о д е р ж а н и е ) * *
2019-01-28 17:55:17 +02:00
2020-02-05 21:32:28 +07:00
# # # И з б е г а й т е м ы с л е н н о г о с в я з ы в а н и я
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
Я в н о е л у ч ш е , ч е м н е я в н о е .
* Я с н о с т ь - э т о к о р о л ь . *
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
* * П л о х о : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
const u = getUser ( ) ;
const s = getSubscription ( ) ;
const t = charge ( u , s ) ;
` ` `
2019-12-12 23:36:27 +07:00
* * Х о р о ш о : * *
2019-01-28 17:55:17 +02:00
` ` ` ts
const user = getUser ( ) ;
const subscription = getSubscription ( ) ;
const transaction = charge ( user , subscription ) ;
` ` `
2019-12-12 23:43:25 +07:00
* * [ ⬆ В е р н у т ь с я в н а ч а л о ] ( # с о д е р ж а н и е ) * *
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
# # # Н е д о б а в л я й т е н е н у ж н ы й к о н т е к с т
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
Е с л и и м я в а ш е г о к л а с с а / т и п а / о б ъ е к т а г о в о р и т с а м о з а с е б я , н е п о в т о р я й т е е г о в в а ш е м и м е н н и п е р е м е н н о й .
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
* * П л о х о : * *
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-05 14:22:02 +02:00
}
2019-01-28 17:55:17 +02:00
2019-02-05 14:22:02 +02:00
function print ( car : Car ) : void {
2019-02-06 13:05:25 +09:00
console . log ( ` ${ car . carMake } ${ car . carModel } ( ${ car . carColor } ) ` ) ;
2019-01-28 17:55:17 +02:00
}
` ` `
2019-12-12 23:36:27 +07:00
* * Х о р о ш о : * *
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-05 14:22:02 +02:00
}
2019-01-28 17:55:17 +02:00
2019-02-05 14:22:02 +02:00
function print ( car : Car ) : void {
2019-02-06 13:05:25 +09:00
console . log ( ` ${ car . make } ${ car . model } ( ${ car . color } ) ` ) ;
2019-01-28 17:55:17 +02:00
}
` ` `
2019-12-12 23:43:25 +07:00
* * [ ⬆ В е р н у т ь с я в н а ч а л о ] ( # с о д е р ж а н и е ) * *
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
# # # И с п о л ь з у й т е а р г у м е н т ы п о у м о л ч а н и ю в м е с т о з а м ы к а н и й и л и в ы ч и с л е н и й
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
А р г у м е н т ы п о у м о л ч а н и ю ч а с т о ч и щ е , ч е м к о р о т к о е в ы ч и с л е н и е .
2019-01-28 17:55:17 +02:00
2019-12-12 23:36:27 +07:00
* * П л о х о : * *
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-12-12 23:36:27 +07:00
* * Х о р о ш о : * *
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-12-12 23:43:25 +07:00
* * [ ⬆ В е р н у т ь с я в н а ч а л о ] ( # с о д е р ж а н и е ) * *
2019-01-28 17:56:21 +02:00
2019-12-12 23:36:27 +07:00
# # # И с п о л ь з у й т е enum д л д о к у м е н т и р о в а н и я
2019-05-30 14:02:11 +02:00
2020-02-05 21:32:28 +07:00
Enam ' ы м о г у т п о м о ч ь д о к у м е н т и р о в а н и ю в а ш е г о к о д а . Н а п р и м е р к о г д а м ы о б е с п о к о е н ы т е м , ч т о н а ш и п е р е м е н н ы е
2019-12-12 23:36:27 +07:00
о т л и ч а ю т с я о т з н а ч е н и й .
2019-05-30 14:02:11 +02:00
2019-12-12 23:36:27 +07:00
* * П л о х о : * *
2019-05-30 14:02:11 +02:00
` ` ` ts
const GENRE = {
ROMANTIC : 'romantic' ,
DRAMA : 'drama' ,
COMEDY : 'comedy' ,
DOCUMENTARY : 'documentary' ,
}
projector . configureFilm ( GENRE . COMEDY ) ;
class Projector {
// delactation of Projector
configureFilm ( genre ) {
switch ( genre ) {
case GENRE . ROMANTIC :
// some logic to be executed
}
}
}
` ` `
2019-12-12 23:36:27 +07:00
* * Х о р о ш о : * *
2019-05-30 14:02:11 +02:00
` ` ` ts
enum GENRE {
ROMANTIC ,
DRAMA ,
COMEDY ,
DOCUMENTARY ,
}
projector . configureFilm ( GENRE . COMEDY ) ;
class Projector {
// delactation of Projector
configureFilm ( genre ) {
switch ( genre ) {
case GENRE . ROMANTIC :
// some logic to be executed
}
}
}
` ` `
2019-12-12 23:43:25 +07:00
* * [ ⬆ В е р н у т ь с я в н а ч а л о ] ( # с о д е р ж а н и е ) * *
2019-05-30 14:02:11 +02:00
2019-12-12 23:36:27 +07:00
# # Ф у н к ц и и
2019-01-31 10:18:41 +02:00
2019-12-16 23:48:42 +07:00
# # # А р г у м е н т ы ф у н к ц и и ( и д е а л ь н о д в а и л и м е н ь ш е )
2019-01-31 10:18:41 +02:00
2019-12-16 23:48:42 +07:00
О г р а н и ч е н и е к о л л и ч е с т в а п а р а м м е т р о в ф у н к ц и и н е в е р о я т н о в а ж н о , п о т о м у ч т о э т о д е л а е т т е с т и р о в а н и е в а ш и х
ф у н к ц и й п р о щ е . Н а л и ч и е б о л е е 3 - х а р г у м е н т о в п р и в о д и т к к о м б и н а т о р н о м у в з р ы в у , г д е в ы д о л ж н ы п р о т е с т и р о в а т ь
м н о ж е с т в о в а р и а н т о в с к а ж д ы м о т д е л ь н ы м а р г у м е н т о м
2019-01-31 10:18:41 +02:00
2019-12-16 23:48:42 +07:00
О д и н и л и д в а а р г у м е н т а э т о и д е а л ь н ы й с л у ч а й , а т р и и б о л е е с л е д у е т и з б е г а т ь , е с л и э т о в о з м о ж н о .
Б о л ь ш о е к о л л и ч е с т в о а р г у м е н т о в л у ч ш е о б ъ е д е н я т ь . О б ы ч н о е с л и в ы и с п о л ь з у е т е б о л е е д в у х а р г у м е н т о в , т о в а ш а ф у н к ц и я п ы т а е т с я
д е л а т ь с л и ш к о м м н о г о . В с л у ч а я х к о г д а э т о н е т а к , т о л у ч ш е и с п о л ь з о в а т ь о б ъ е к т в е р х н е г о у р о в н я .
2019-01-31 10:18:41 +02:00
2019-12-16 23:48:42 +07:00
П о д у м а й т е о т о м ч т о б ы и с п о л ь з о в а т ь о б ъ е к т н ы е л и т е р а л ы , е с л и в а м н е о б х о д и м о м н о г о а р г у м е н т о в .
2019-01-31 10:18:41 +02:00
2019-12-16 23:48:42 +07:00
Д л я т о г о ч т о б ы в ы з н а л и к а к и е п а р а м е т р ы о ж и д а е т ф у н к ц и я , в ы м о ж е т е и с п о л ь з о в а т ь
[ с и н т а к с и с д е с т р у к т у р и з а ц и и ] ( https : //basarat.gitbooks.io/typescript/docs/destructuring.html).
2019-01-31 10:18:41 +02:00
2019-12-16 23:48:42 +07:00
О н и м е е т н е с к о л ь к о п р е и м у щ е с т в :
2019-01-31 10:18:41 +02:00
2019-12-16 23:48:42 +07:00
1 . К о г д а к т о - т о с м о т р и т н а с и н а т у р у ф у н к ц и и , т о с р а з у с т а н о в и т с я п о н я т к а к а к и е с в о й с т в а о н а и с п о л ь з у е т .
2019-01-31 10:18:41 +02:00
2019-12-16 23:48:42 +07:00
2 . Д е с т р у к т у р и з а ц и я т а к ж е к л о н и р у е т п р и м и т и в н ы е з н а ч е н и я а р г у м е н т а - о б ъ е к т а п е р е д а н н о г о в ф у н к ц и ю .
Э т о п о м о г а е т и з б е ж а т ь с а й д э ф ф е к т а . Заметка : объекты и м а с с и в ы к о т о р ы е д е с т р у к т у р и р о в а н ы и з а р г у м е н т а - о б ъ е к т а н е к л о н и р у ю т с я .
2019-01-31 10:18:41 +02:00
2019-12-16 23:48:42 +07:00
3 . TypeScript п р е д у п р е ж д а е т о н е и с п о л ь з у е м ы х с в о й с т в а х , э т о б ы л о б ы н е в о з м о ж н о б е з д е с т р у к т у р и з а ц и и .
* * П л о х о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
function createMenu ( title : string , body : string , buttonText : string , cancellable : boolean ) {
// ...
}
createMenu ( 'Foo' , 'Bar' , 'Baz' , true ) ;
` ` `
2019-12-16 23:48:42 +07:00
* * Х о р о ш о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-06 09:57:52 +02: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-12-16 23:48:42 +07:00
В ы м о ж е т е е щ е б о л ь ш е п о в ы с и т ь ч и т а е м о с т ь , е с л и б у д е т е и с п о л ь з о в а т ь [ type aliases ] ( https : //www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases):
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-06 09:57:52 +02: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-12-16 23:48:42 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-16 23:48:42 +07:00
# # # Ф у н к ц и и д о л ж н ы в ы п о л н я т ь о д н у з а д а ч у
2019-01-31 10:18:41 +02:00
2019-12-16 23:48:42 +07:00
Э т о о д н о и з с а м ы х в а ж н ы х п р а в и л в р а з р а б о т к е П О . К о г д а ф у н к ц и и р е ш а ю т б о л ь ш е о д н о й з а д а ч и ,
и х т р у д н е е о б ъ е д е н я т ь , т е с т и р о в а т ь . Е с л и в ы с м о ж е т е и з о л и р о в а т ь ф у н к ц и ю т а к ч т о б ы о н а в ы п о л н я л а т о л ь к о о д н у з а д а ч у ,
в д а л ь н е й ш е м о н а м о ж е т б ы т ь л е г к о п е р е р а б о т а н а , а в а ш к о д б у д е т ч и щ е . Е с л и в ы з а п о м н и т е т о л ь к о э т о п р а в и л о и з э т о г о
р у к о в о д с т в а , т о в ы у ж е б у д е т е л у ч щ е м н о г и х р а з р а б о т ч и к о в .
2019-01-31 10:18:41 +02:00
2019-12-16 23:48:42 +07:00
* * П л о х о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-03-02 17:57:05 +01:00
function emailClients ( clients : Client [ ] ) {
2019-01-31 10:18:41 +02:00
clients . forEach ( ( client ) = > {
const clientRecord = database . lookup ( client ) ;
if ( clientRecord . isActive ( ) ) {
email ( client ) ;
}
} ) ;
}
` ` `
2019-12-16 23:48:42 +07:00
* * Х о р о ш о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-03-02 17:57:05 +01:00
function emailClients ( clients : Client [ ] ) {
2019-01-31 10:18:41 +02:00
clients . filter ( isActiveClient ) . forEach ( email ) ;
}
function isActiveClient ( client : Client ) {
const clientRecord = database . lookup ( client ) ;
return clientRecord . isActive ( ) ;
}
` ` `
2019-12-16 23:48:42 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
# # # Н а з в а н и е ф у н к ц и й д о л ж н ы о п и с ы в а т ь ч т о о н и д е л а ю т
2019-01-31 10:18:41 +02:00
2019-12-16 23:48:42 +07:00
* * П л о х о : * *
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-12-16 23:48:42 +07:00
* * Х о р о ш о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
function addMonthToDate ( date : Date , month : number ) : Date {
// ...
}
const date = new Date ( ) ;
addMonthToDate ( date , 1 ) ;
` ` `
2019-12-16 23:48:42 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
# # # Ф у н к ц и и д о л ж н ы и м е т ь о д и н у р о в е н ь а б с т р а к ц и и
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
Е с л и у в а с б о л ь щ е о д н о г о у р о в н я а б с т р а к ц и и , т о о б ы ч н о э т а ф у н к ц и я д е л а е т с л и ш к о м м н о о е . Р а з д е л е н и е ф у н к ц и й д а е т в о з м о ж н о с т ь
п е р е и с п о л ь з о в а н и я и п р о с т о г о т е с т и р о в а н и я .
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
* * П л о х о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-06 09:57:52 +02:00
function parseCode ( code : string ) {
2019-01-31 10:18:41 +02:00
const REGEXES = [ /* ... */ ] ;
const statements = code . split ( ' ' ) ;
const tokens = [ ] ;
REGEXES . forEach ( ( regex ) = > {
statements . forEach ( ( statement ) = > {
// ...
} ) ;
} ) ;
const ast = [ ] ;
tokens . forEach ( ( token ) = > {
// lex...
} ) ;
ast . forEach ( ( node ) = > {
// parse...
} ) ;
}
` ` `
2019-12-19 00:08:19 +07:00
* * Х о р о ш о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
const REGEXES = [ /* ... */ ] ;
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 ) ;
syntaxTree . forEach ( ( node ) = > {
// 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
REGEXES . forEach ( ( regex ) = > {
statements . forEach ( ( statement ) = > {
tokens . push ( /* ... */ ) ;
} ) ;
} ) ;
return tokens ;
}
function parse ( tokens : Token [ ] ) : SyntaxTree {
2019-02-06 09:57:52 +02:00
const syntaxTree : SyntaxTree [ ] = [ ] ;
2019-01-31 10:18:41 +02:00
tokens . forEach ( ( token ) = > {
syntaxTree . push ( /* ... */ ) ;
} ) ;
return syntaxTree ;
}
` ` `
2019-12-19 00:08:19 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
# # # У д а л я й т е д у б л и р о в а н н ы й к о д
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
Д е л а й т е в с е в о з м о ж н о е д л я и з б е ж а н и я д у б л и р о в а н и я к о д а .
Д у б л и р о в а н и е к о д а п л о х о , т е м ч т о е с л и в а м п р и д е т с я п р а в и т ь л о г и к у , е ё п р и д е т с я п р а в и т ь в н е с к о л ь к и х м е с т а х .
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
П р е д с т а в ь т е е с л и в ы о т к р ы л и р е с т о р а н и в е д е т е у ч е т в а ш и х продуктов : всех в а ш и х т о м а т о в , л у к а , ч е с н о к а , с п е ц и й и д . р . .
Е с л и у в а с з а к а ж у т б л ю д а и з т о м а т о в т о в а м п р и д е т с я в н о с и т ь и з м е н е н и я в о в с е в а ш и с п и с к и . Е с л и с п и с о к б у д е т т о л ь к о о д и н
т о и п р а в и т ь н у ж н о б у д е т т о л ь к о е г о .
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
Ч а с т о в ы д у б л и р у е т е к о д и з - з а т о г о ч т о к о г д а в а м т р е б у е т с я р е а л и з о в а т ь д в а и б о л е е н е з н а ч и т е л ь н о р а з л и ч а ю щ и х с я д е й с т в и й ,
к о т о р ы е о ч е н ь п о х о ж и , н о и х р а з л и ч и я з а с т а в л я ю т в а с з а в е с т и н е с к о л ь к о ф у н к ц и й , д е л а ю щ и й п р а к т и ч е с к и о д н о и т о ж е . У д а л е н и е
д у б л и р у ю щ и х с я ч а с т е й к о д а , о з н а ч а е т с о з д а н и е а б с т р а к ц и и , о б р а б а т ы в а ю щ и й р а з н у ю л о г и к у с п о м о щ ь ю в с е г о о д н о й ф у н к ц и и / м о д у л я / к л а с с а .
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
П о л у ч е н и е а б с т р а к ц и и и м е е т в а ж н о е з н а ч е н и е , п о э т о м у в ы д о л ж н ы с л е д о в а т ь п р и н ц и п а м [ SOLID ] ( # solid ) . П л о х и е а б с т р а к ц и и м о г у т
о к а з а т ь с я х у ж е д у б л и р у ю щ е г о к о д а , б у д ь т е о с т о р о ж н ы ! Е с л и в ы м о ж е т е с д е л а т ь х о р о ш у ю а б с т р а к ц и ю д е л а й т е . Н е п о в т о р я й т е с е б я
в п р о т и в н о м с л у ч а е в ы м о ж е т е о б н а р у ж и т ь с е б я в н о с я щ и м и з м е н е н и я в р а з н ы е м е с т а , д л я о д н о й е д и н с т в е н н о й л о г и к и .
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
* * П л о х о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
function showDeveloperList ( developers : Developer [ ] ) {
developers . forEach ( ( developer ) = > {
const expectedSalary = developer . calculateExpectedSalary ( ) ;
const experience = developer . getExperience ( ) ;
const githubLink = developer . getGithubLink ( ) ;
const data = {
expectedSalary ,
experience ,
githubLink
} ;
render ( data ) ;
} ) ;
}
function showManagerList ( managers : Manager [ ] ) {
managers . forEach ( ( manager ) = > {
const expectedSalary = manager . calculateExpectedSalary ( ) ;
const experience = manager . getExperience ( ) ;
const portfolio = manager . getMBAProjects ( ) ;
const data = {
expectedSalary ,
experience ,
portfolio
} ;
render ( data ) ;
} ) ;
}
` ` `
2019-12-19 00:08:19 +07:00
* * Х о р о щ о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
class Developer {
// ...
getExtraDetails() {
return {
githubLink : this.githubLink ,
}
}
}
class Manager {
// ...
getExtraDetails() {
return {
portfolio : this.portfolio ,
}
}
}
function showEmployeeList ( employee : Developer | Manager ) {
employee . forEach ( ( employee ) = > {
2019-02-06 12:25:36 +01:00
const expectedSalary = employee . calculateExpectedSalary ( ) ;
const experience = employee . getExperience ( ) ;
2019-01-31 10:18:41 +02:00
const extra = employee . getExtraDetails ( ) ;
const data = {
expectedSalary ,
experience ,
extra ,
} ;
render ( data ) ;
} ) ;
}
` ` `
2019-12-19 00:08:19 +07:00
В ы д о л ж н ы к р и т и ч е с к и о т н о с и т ь с я к д у б л и р о в а н и ю к о д а . И н о г д а с у щ е с т в у е т к о м п р о м и с с м е ж д у д у б л и р о в а н и е м к о д а и у в е л и ч е н и е м
с л о ж н о с т и , в в о д я н о в у ю а б с т р а к ц и ю . К о г д а д в е р е а л и з а ц и и и з д в у х р а з н ы х м о д у л е й в ы г л я д я т о д и н а к о в о , н о с у щ е с т в у ю т в р а з н ы х
д о м е н а х , д у б л и р о в а н и е м о ж е т б ы т ь п р и е м л е м ы м и п р е д п о ч т и т е л ь н ы м в а р и а н т о м , н е ж е л и о б ъ е д е н е н и й в о б щ и й к о д . П е р е н о с л о г и к и
в о б щ и й к о д , в в о д и т к о с в е н н у ю з а в и с и м о с т ь м е ж д у д в у м я м о д у л я м и .
2019-02-05 14:22:02 +02:00
2019-12-19 00:08:19 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
# # # У с т а н а в л и в а й т е о б ъ е к т ы п о у м о л ч а н и ю с п о м о щ ь ю Object . assign и л и д е с т р у к т у р и з а ц и и
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
* * П л о х о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-05 17:21:31 +02: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' ;
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-06 09:57:52 +02:00
createMenu ( { body : 'Bar' } ) ;
2019-01-31 10:18:41 +02:00
` ` `
2019-12-19 00:08:19 +07:00
* * Х о р о ш о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-05 17:21:31 +02:00
type MenuConfig = { title? : string , body? : string , buttonText? : string , cancellable? : boolean } ;
2019-01-31 10:18:41 +02:00
function createMenu ( config : MenuConfig ) {
const menuConfig = Object . assign ( {
title : 'Foo' ,
body : 'Bar' ,
buttonText : 'Baz' ,
cancellable : true
} , config ) ;
2019-02-06 09:57:52 +02:00
// ...
2019-01-31 10:18:41 +02:00
}
createMenu ( { body : 'Bar' } ) ;
` ` `
2019-12-19 00:08:19 +07:00
К р о м е т о г о м о ж н о и с п о л ь з о в а т ь д е с т р у к т у р и з а ц и ю с о з н а ч е н и я м и п о у м о л ч а н и ю :
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-05 17:21:31 +02:00
type MenuConfig = { title? : string , body? : string , buttonText? : string , cancellable? : boolean } ;
2019-01-31 10:18:41 +02:00
2019-02-05 17:21:31 +02:00
function createMenu ( { title = 'Foo' , body = 'Bar' , buttonText = 'Baz' , cancellable = true } : MenuConfig ) {
2019-01-31 10:18:41 +02:00
// ...
}
createMenu ( { body : 'Bar' } ) ;
` ` `
2019-12-19 00:08:19 +07:00
Ч т о б ы и з б е ж а т ь к а к и х - л и б о п о б о ч н ы х э ф ф е к т о в и н е о ж и д а н н о о п о в е д е н и я п е р е д а в а я я в н о ` undefined ` и л и ` null ` в ы м о ж е т е с к а з а т ь
к о м п и л я т о р у TypeScript ч т о б ы о н н е р а з р е ш а л э т о г о .
С м о т р и т е [ ` --strictNullChecks ` ] ( https : //www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#--strictnullchecks) опция для TypeScript.
2019-02-05 17:21:31 +02:00
2019-12-19 00:08:19 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
# # # Н е и с п о л ь з у й т е ф л а г и в к а ч е с т в е п а р а м е т р о в ф у н к ц и и
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
Ф л а г и г о в о р я т п о л ь з о в а т е л ю , ч т о ф у н к ц и я с о в е р ш а е т б о л е е о д н о г о д е й с т в и я . Ф у н к ц и я д о л ж н а р е ш а т ь о д н у з а д а ч у .
Р а з д е л я й т е ф у н к ц и и , е с л и о н и и с п о л н я ю т р а з л и ч н ы е в а р и а н т ы к о д а н а о с н о в е л о г и ч е с к о г о з н а ч е н и я .
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
* * П л о х о : * *
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-12-19 00:08:19 +07:00
* * Х о р о ш о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-07 23:42:21 +02:00
function createTempFile ( name : string ) {
createFile ( ` ./temp/ ${ name } ` ) ;
2019-01-31 10:18:41 +02:00
}
2019-02-07 23:42:21 +02:00
function createFile ( name : string ) {
fs . create ( name ) ;
2019-01-31 10:18:41 +02:00
}
` ` `
2019-12-19 00:08:19 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
# # # И з б е г а й т е п о б о ч н ы х э ф ф е к т о в ( ч а с т ь 1 )
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
Ф у н к ц и я п р о и з в о д и т п о б о ч н ы й э ф ф е к т , е с л и о н а с о в е р ш а е т к а к о е - л и б о д е й с т в и е п о м и м о п о л у ч е н и я з н а ч е н и я и
в о з в р а т а д р у г о г о з н а ч е н и я и л и з н а ч е н и й . П о б о ч н ы й э ф ф е к т м о ж е т б ы т ь з а п и с ь ю в ф а й л , и з м е н е н и е м к а к и х - т о
г л о б а л ь н ы х п е р е м е н н ы х и л и с л у ч а й н ы м п е р е в о д о м в с е х в а ш и х д е н е г н е и з в е с т н ы м л и ц а м .
В п р о ч е м , п о б о ч н ы е э ф ф е к т ы в п р о г р а м м е н е о б х о д и м ы . П у с т ь , к а к и в п р е д ы д у щ е м п р и м е р е , в а м т р е б у е т с я з а п и с ь в ф а й л .
О п и ш и т е т о , ч т о в ы х о т и т е с д е л а т ь , с т р о г о в о д н о м м е с т е . Н е с о з д а в а й т е н е с к о л ь к о ф у н к ц и й и к л а с с о в , к о т о р ы е п и ш у т ч т о - т о
в к о н к р е т н ы й ф а й л . С о з д а й т е о д и н с е р в и с , к о т о р ы й в с е м э т и м з а н и м а е т с я . О д и н и т о л ь к о о д и н .
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
С у т ь в т о м , ч т о б ы и з б е г а т ь р а с п р о с т р а н е н н ы х о ш и б о к , т а к и х к а к , н а п р и м е р , п е р е д а ч а с о с т о я н и я м е ж д у о б ъ е к т а м и
б е з к а к о й - л и б о с т р у к т у р ы , с п о м о щ ь ю и з м е н я е м ы х д а н н ы х , к о т о р ы е м о ж е т п е р е з а п и с ы в а т ь к т о у г о д н о , в о б х о д
ц е н т р а л и з о в а н н о г о м е с т а п р и м е н е н и я п о б о ч н ы х э ф ф е к т о в . Е с л и н а у ч и т е с ь т а к д е л а т ь , в ы с т а н е т е с ч а с т л и в е е , ч е м п о д а в л я ю щ е е
б о л ь ш и н с т в о д р у г и х п р о г р а м м и с т о в .
2019-01-31 10:18:41 +02:00
2019-12-19 00:08:19 +07:00
* * П л о х о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
// Global variable referenced by following function.
let name = 'Robert C. Martin' ;
function toBase64() {
name = btoa ( name ) ;
}
2019-02-07 21:53:16 +02:00
toBase64 ( ) ;
// If we had another function that used this name, now it'd be a Base64 value
2019-01-31 10:18:41 +02:00
console . log ( name ) ; // expected to print 'Robert C. Martin' but instead 'Um9iZXJ0IEMuIE1hcnRpbg=='
` ` `
2019-12-19 00:08:19 +07:00
* * Х о р о ш о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
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-01-31 10:18:41 +02:00
console . log ( name ) ;
` ` `
2019-12-19 00:08:19 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-19 23:53:41 +07:00
# # # И з б е г а й т е п о б о ч н ы х э ф ф е к т о в ( Ч а с т ь 2 )
2019-01-31 10:18:41 +02:00
2019-12-19 23:53:41 +07:00
В JavaScript п р и м и т и в ы п е р е д а ю т с я п о з н а ч е н и ю , а о б ъ е к т ы и м а с с и в ы п е р е д а ю т с я п о с с ы л к е . В с л у ч а е о б ъ е к т о в и л и м а с с и в о в ,
е с л и в а ш а ф у н к ц и я в н о с и т и з м е н е н и я в к о р з и н у п о к у п о к ( м а с с и в ) , н а п р и м е р п р и д о б а в л е н и и э л е м е н т а в м а с с и в , т о л ю б а я д р у г а я
ф у н к ц и я и с п о л ь з у ю щ а я я ` корзину ` м а с с и в б у д е т з а в и с е т ь о т э т о г о д о б а в л е н и я . Э т о м о ж е т б ы т ь к а к х о р о ш о , т а к и п л о х о . Д а в а й т е
п р е д с т а в и м п л о х у ю с и т у а ц и и :
2019-01-31 10:18:41 +02:00
2019-12-19 23:53:41 +07:00
П о л ь з о в а т е л ь н а ж и м а е т к н о п к у "Купить" в ы з ы в у ю щ ю ю ф у н к ц и ю ` purchase ` к о т о р а я д е л а е т с е т е в о й з а п р о с и о т п р а в л я е т ` корзину `
м а с с и в н а с е р в е р . Е с л и п р о и с х о д и т п л о х о е п о д к л ю ч е н и е к с е т и ф у н к ц и я д о л ж н а о т п р а в и т ь п о в т о р н ы й з а п р о с . Т е п е р ь , е с л и п о л ь з о в а т е л ь
с л у ч а й н о н а ж и м а е т н а к н о п к у "Добавить в корзину" , н о п о к а н е х о ч е т п о к у п а т ь т о в а р ? Е с л и э т о п р о и з о й д е т и в э т о т м о м е н т н а ч н е т с я
з а п р о с н а с е р в е р , т о ф у н к ц и я purchase о т п р а в и т с л у ч а й н о д о б а в л е н н ы й э л е м е н т , т а к к а к о н и м е е т с с ы л к у н а к о р з и н у п о к у п о к ,
к о т о р а б ы л а и з м е н е н а ф у н к ц и е й ` addItemToCart ` . П у т е м д о б а в л е н и я н е ж е л а т е л ь н о г о э л е м е н т а .
2019-01-31 10:18:41 +02:00
2019-12-19 23:53:41 +07:00
Х о р о ш и м б ы р е щ е н и е м б ы л о б ы ч т о б ы ф у н к ц и я ` addItemToCart ` в с е г д а к л о н и р о в а л а б ы м а с с и в ` cart ` р е д а к т и р о в а л а е г о и
в о з в р а щ а л а к л о н . Э т о б ы г а р а н т и р о в а л о , ч т о н и к а к и е д р у г и е ф у н к ц и и , и с п о л ь з у ю щ и е с с ы л к у н а м а с с и в к о р з и н ы п о к у п о к ,
н е б у д у т з а т р о н у т ы к а к и м и - л и б о и з м е н е н и я м и .
2019-01-31 10:18:41 +02:00
2019-12-19 23:53:41 +07:00
Д в а п р е д о с т е р е ж е н и я п о - п о в о д у т а к о г о п о д х о д а :
2019-01-31 10:18:41 +02:00
2019-12-19 23:53:41 +07:00
1 . В о з м о ж н ы с л у ч а и , к о г д а в ы н а с а м о м д е л е х о т и т е и з м е н и т ь о б ъ е к т п о с с ы л к е , н о т а к и е с л у ч а и к р а й н е р е д к и .
Б о л ь ш и н с т в о ф у н к ц и й м о г у т б ы т ь о б ъ я в л е н ы б е з с а й д э ф ф е к т о в ! ( С м о т р и т е [ pure function ] ( https : //en.wikipedia.org/wiki/Pure_function))
2019-01-31 10:18:41 +02:00
2019-12-19 23:53:41 +07:00
2 . К л о н и р о в а н и е б о л ь ш и х о б ъ е к т о в м о ж е т б ы т ь о ч е н ь н а г р у з о ч н ы м и в л и я т ь н а п р о и з в о д и т е л ь н о с т ь . К с ч а с т ь ю , э т о н е я в л я е т с я
б о л ь ш о й п р о б л е м о й н а п р а к т и к е , п о т о м у ч т о е с т ь о т л и ч н ы е б и б л и о т е к и , к о т о р ы е п о з в о л я ю т к л о н и р о в а т ь о б ъ е к т ы с м е н ь ш е й
н а г р у з к о й н а п а м я т ь в о т л и ч и и о т к л о н и р о в а н и я в р у ч н у ю .
2019-01-31 10:18:41 +02:00
2019-12-19 23:53:41 +07:00
* * П л о х о : * *
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-12-19 23:53:41 +07:00
* * Х о р о ш о : * *
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-01-31 10:18:41 +02:00
} ;
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-25 22:42:36 +07:00
# # # Н е п и ш и т е г л о б а л ь н ы е ф у н к ц и и
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
З а г р я з н е н и е г л о б а л ь н ы х п е р е м е н н ы х — п л о х а я п р а к т и к а в JavaScript , т а к к а к м о ж е т п о р о д и т ь к о н ф л и к т ы с д р у г о й б и б л и о т е к о й ,
и п о л ь з о в а т е л ь в а ш е г о API н е у в и д и т о ш и б о к , п о к а н е п о л у ч и т и с к л ю ч е н и е в п р о д а к ш е н е . Д а в а й т е р а с с м о т р и м п р и м е р :
ч т о е с л и в ы х о т и т е р а с ш и р и т ь с т а н д а р т н ы й м е т о д Array JavaScript , и м е я м е т о д ` diff ` к о т о р ы й б ы в ы ч и с л я л р а з л и ч и е м е ж д у
д в у м я м а с с и в а м и ? В ы д о л ж н ы б ы л и б ы з а п и с а т ь н о в у ю ф у н к ц и ю в ` Array.prototype ` , н о т о г д а о н а м о ж е т в о й т и в к о н ф л и к т
с д р у г о й б и б л и о т е к о й , к о т о р а я п ы т а л а с ь с д е л а т ь т о ж е с а м о е . Ч т о е с л и д р у г а я б и б л и о т е к а и с п о л ь з о в а л а м е т о д ` diff ` ,
ч т о б ы н а й т и р а з н и ц у м е ж д у п е р в ы м и п о с л е д н и м э л е м е н т а м и м а с с и в а ? В э т о м с л у ч а е л у ч ш е и с п о л ь з о в а т ь к л а с с ы и п р о с т о
с д е л а т ь н а с л е д о в а н и е о т г л о б а л ь н о г о ` Array ` .
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
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-01-31 10:18:41 +02:00
Array . prototype . diff = function < T > ( other : T [ ] ) : T [ ] {
const hash = new Set ( other ) ;
return this . filter ( elem = > ! hash . has ( elem ) ) ;
} ;
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
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-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
# # # П р е д п о ч т е н и е ф у н к ц и о н а л ь н о е п р о г р а м м и р о в а н и е н а д и м п е р а т и в н ы м
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
О т д а в а й т е п р е д п о ч т е н и е э т о м у с т и л ю п р о г р а м м и р о в а н и я , к о г д а м о ж е т е .
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
const contributions = [
{
name : 'Uncle Bobby' ,
linesOfCode : 500
} , {
name : 'Suzie Q' ,
linesOfCode : 1500
} , {
name : 'Jimmy Gosling' ,
linesOfCode : 150
} , {
name : 'Gracie Hopper' ,
linesOfCode : 1000
}
] ;
let totalOutput = 0 ;
for ( let i = 0 ; i < contributions . length ; i ++ ) {
totalOutput += contributions [ i ] . linesOfCode ;
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
const contributions = [
{
name : 'Uncle Bobby' ,
linesOfCode : 500
} , {
name : 'Suzie Q' ,
linesOfCode : 1500
} , {
name : 'Jimmy Gosling' ,
linesOfCode : 150
} , {
name : 'Gracie Hopper' ,
linesOfCode : 1000
}
] ;
const totalOutput = contributions
2019-02-06 10:48:34 +02:00
. reduce ( ( totalLines , output ) = > totalLines + output . linesOfCode , 0 ) ;
2019-01-31 10:18:41 +02:00
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
# # # И н к а п с у л и р у й т е у с л о в и я
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
if ( subscription . isTrial || account . balance > 0 ) {
// ...
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
function canActivateService ( subscription : Subscription , account : Account ) {
return subscription . isTrial || account . balance > 0
}
if ( canActivateService ( subscription , account ) ) {
// ...
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
# # # И з б е г а й т е н е г а т и в н ы х у с л о в и й
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-06 09:57:52 +02:00
function isEmailNotUsed ( email : string ) : boolean {
2019-01-31 10:18:41 +02:00
// ...
}
if ( isEmailNotUsed ( email ) ) {
// ...
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-06 09:57:52 +02:00
function isEmailUsed ( email ) : boolean {
2019-01-31 10:18:41 +02:00
// ...
}
if ( ! isEmailUsed ( node ) ) {
// ...
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
# # # И з б е г а й т е у с л о в н ы х о п е р а т о р о в
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
Э т а з а д а ч а к а ж е т с я н е в о з м о ж н о й . Б о л ь ш и н с т в о л ю д е й , в п е р в ы е у с л ы ш а в э т о , г о в о р я т , " к а к я д о л ж е н д е л а т ь ч т о - л и б о б е з
в ы р а ж е н и я ` if ` ? " . О т в е т з а к л ю ч а е т с я в т о м , ч т о в о м н о г и х с л у ч а я х д л я д о с т и ж е н и я т е х ж е ц е л е й м о ж н о и с п о л ь з о в а т ь
п о л и м о р ф и з м . В т о р о й в о п р о о б ы ч н о , "хорошо, замечательно, но почему я должен их избегать?" О т в е т , п р е д ы д у щ а я к о н ц е п ц и я
ч и с т о г о к о д а , к о т о р у ю в ы узнали : функция д о л ж н а в ы п о л н я т ь т о л ь к о о д н у з а д а ч у . К о г д а у в а с е с т ь к л а с с ы и ф у н к ц и и ,
с о д е р ж а щ и е к о н с т р у к ц и ю ` if ` , в ы г о в о р и т е с в о е м у п о л ь з о в а т е л ю , ч т о в а ш а ф у н к ц и я в ы п о л н я е т б о л ь ш е о д н о й з а д а ч и .
З а п о м н и т е , д е л а т ь т о л ь к о о д н у з а д а ч у .
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
class Airplane {
private type : string ;
// ...
2019-02-07 21:53:16 +02: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-06 09:57:52 +02:00
2019-02-06 10:43:37 +02:00
private getMaxAltitude ( ) : number {
2019-02-06 09:57:52 +02:00
// ...
}
2019-01-31 10:18:41 +02:00
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
2019-02-06 09:57:52 +02:00
abstract class Airplane {
2019-02-06 10:43:37 +02:00
protected getMaxAltitude ( ) : number {
2019-02-07 21:53:16 +02:00
// shared logic with subclasses ...
2019-02-06 09:57:52 +02:00
}
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-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
# # # И з б е г а й т е п р о в е р к и т и п о в
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
TypeScript я в л я е т с я н а д м н о ж е с т в о м с и н т а к с и с а JavaScript и д о б а в л я ю т д о п о л н и т е л ь н ы е с т а т и ч е с к и е п р о в е р к и т и п о в д л я я з ы к а .
В с е г д а п р е д п о ч и т а й т е у к а з ы в а т ь т и п ы п е р е м е н н ы х , п а р а м е т р о в и в о з в р а щ а е м ы х з н а ч е н и й , ч т о б ы и с п о л ь з о в а т ь
2020-02-05 21:32:28 +07:00
в с ю м о щ ь TypeScript . Э т о д е л а е т б у д у щ и й р е ф а к т о р и н г б о л е е л е г к и м .
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
function travelToTexas ( vehicle : Bicycle | Car ) {
if ( vehicle instanceof Bicycle ) {
2019-02-06 09:57:52 +02:00
vehicle . pedal ( currentLocation , new Location ( 'texas' ) ) ;
2019-01-31 10:18:41 +02:00
} else if ( vehicle instanceof Car ) {
2019-02-06 09:57:52 +02:00
vehicle . drive ( currentLocation , new Location ( 'texas' ) ) ;
2019-01-31 10:18:41 +02:00
}
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
type Vehicle = Bicycle | Car ;
function travelToTexas ( vehicle : Vehicle ) {
2019-02-06 09:57:52 +02:00
vehicle . move ( currentLocation , new Location ( 'texas' ) ) ;
2019-01-31 10:18:41 +02:00
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
# # # Н е д е л а й т е с л и ш к о м м н о г о о п т и м и з а ц и й
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
С о в р е м е н н ы е б р а у з е р ы п р о и з в о д я т м н о ж е с т в о о п т и м и з а ц и й п о д к а п о т о м в о в р е м я и с п о л н е н и я к о д а . О п т и м и з и р у я к о д в р у ч н у ю в ы
з а ч а с т у ю , п р о с т о т р а т и т е с в о е в р е м я . Е с т ь х о р о ш и е [ р е с у р с ы ] ( https : //github.com/petkaantonov/bluebird/wiki/Optimization-killers)
д л я т о г о ч т о б ы у в и д е т ь г д е о п т и м и з а ц и я о т с у т с т в у е т . П о г л я д ы в а й т е н а н и х в с в о б о д н о е в р е м я , п о к а э т и п р о б л е м ы н е б у д у т
и с п р а в л е н ы , е с л и в о о б щ е б у д у т , к о н е ч н о .
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
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-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-01-31 10:18:41 +02:00
` ` ` ts
for ( let i = 0 ; i < list . length ; i ++ ) {
// ...
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
# # # У д а л я й т е м е р т в ы й к о д
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
М е р т в ы й к о д - т а к ж е п л о х о , к а к п о в т о р я ю щ и й с я к о д . Н е т н и к а к и х о с н о в а н и й п р о д о л ж а т ь х р а н и т ь е г о в к о д о в о й б а з е .
Е с л и о н н е и с п о л ь з у е т с я , и з б а в ь т е с ь о т н е г о ! В с л у ч а е н а д о б н о с т и , е г о в с е г д а м о ж н о н а й т и в и с т о р и и в е р с и й .
* * П л о х о : * *
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-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
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-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
# # # И с п о л ь з у й т е и т е р а т о р ы и г е н е р а т о р ы
2019-02-09 23:28:17 +02:00
2019-12-24 23:13:37 +07:00
И с п о л ь з у й т е г е н е р а т о р ы и и т е р а т о р ы п р и р а б о т е с к о л л е к ц и я м и д а н н ы х , к о т о р ы е и с п о л ь з у ю т с я к а к п о т о к .
Е с т ь н е с к о л ь к о п р и ч и н д л я э т о г о :
2019-02-12 08:40:50 +02:00
2019-12-24 23:13:37 +07:00
- о т д е л я е т в ы з ы в а е м ы й о б ъ е к т о т р е а л и з а ц и и г е н е р а т о р а в т о м с м ы с л е , ч т о в ы з ы в а е м ы й о б ъ е к т р е ш а е т с к о л ь к о э л е м е н т о в
и м е т ь д л я д о с т у п а
- л е н и в о е в ы п о л н е н и е , э л е м е н т ы п е р е д а ю т с я п о т р е б о в а н и ю
- в с т р о е н н а я п о д д е р ж к а и т е р а ц и и э л е м е н т о в с и с п о л ь з о в а н и е м с и н т а к с и с а ` for-of `
- и т е р а т о р ы п о з в о л я ю т р е а л и з о в а т ь о п т и м и з и р о в а н н ы е п а т т е р н ы и т е р а т о р о в
2019-02-09 23:28:17 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-09 23:28:17 +02:00
` ` ` ts
function fibonacci ( n : number ) : number [ ] {
if ( n === 1 ) return [ 0 ] ;
if ( n === 2 ) return [ 0 , 1 ] ;
2019-02-12 08:40:50 +02:00
const items : number [ ] = [ 0 , 1 ] ;
2019-02-09 23:28:17 +02:00
while ( items . length < n ) {
items . push ( items [ items . length - 2 ] + items [ items . length - 1 ] ) ;
}
return items ;
}
function print ( n : number ) {
fibonacci ( n ) . forEach ( fib = > console . log ( fib ) ) ;
}
// Print first 10 Fibonacci numbers.
print ( 10 ) ;
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-09 23:28:17 +02:00
` ` ` ts
// Generates an infinite stream of Fibonacci numbers.
// The generator doesn't keep the array of all numbers.
function * fibonacci ( ) : IterableIterator < number > {
let [ a , b ] = [ 0 , 1 ] ;
2019-02-12 08:40:50 +02:00
2019-02-09 23:28:17 +02:00
while ( true ) {
yield a ;
[ a , b ] = [ b , a + b ] ;
}
}
function print ( n : number ) {
let i = 0 ;
2019-02-27 19:46:04 +05:45
for ( const fib of fibonacci ( ) ) {
2019-02-09 23:28:17 +02:00
if ( i ++ === n ) break ;
console . log ( fib ) ;
}
}
// Print first 10 Fibonacci numbers.
print ( 10 ) ;
` ` `
2019-12-24 23:13:37 +07:00
С у щ е с т в у ю т б и б л и о т е к и , к о т о р ы е п о з в о л я ю т р а б о т а т ь с и т е р а т о р а м и т а к ж е , к а к и с с о б с т в е н н ы м и м а с с и в а м и , п у т е м
ц е п о ч к а м е т о д о в , т а к и х к а к ` map ` , ` slice ` , ` forEach ` и д р . С м о т р и т е [ itiriri ] ( https : //www.npmjs.com/package/itiriri)
п р и м е р п р о д в и н у т о й м а н и п у л я ц и и с и т е р а т о р а м и ( и л и [ itiriri - async ] ( https : //www.npmjs.com/package/itiriri-async)
д л я м а н и п у л я ц и и с а с и н х р о н н ы м и и т е р а т о р а м и ) .
2019-02-09 23:28:17 +02:00
` ` ` ts
import itiriri from 'itiriri' ;
function * fibonacci ( ) : IterableIterator < number > {
let [ a , b ] = [ 0 , 1 ] ;
while ( true ) {
yield a ;
[ a , b ] = [ b , a + b ] ;
}
}
itiriri ( fibonacci ( ) )
. take ( 10 )
. forEach ( fib = > console . log ( fib ) ) ;
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-09 23:28:17 +02:00
2019-12-25 22:42:36 +07:00
# # О б ъ е к т ы и с т р у к т у р ы д а н н ы х
2019-01-29 09:44:59 +02:00
2019-12-25 22:42:36 +07:00
# # # И с п о л ь з у й т е г е т т е р ы и с е т т е р ы
2019-01-29 09:44:59 +02:00
2019-12-25 22:42:36 +07:00
TypeScript п о д д е р ж и в а е т с и н т а к с и с г е т т е р о в и с е т т е р о в .
И с п о л ь з о в а т ь г е т т е р ы и с е т т е р ы д л я д о с т у п а к д а н н ы м о б ъ е к т а г о р а з д о л у ч ш е , ч е м н а п р я м у ю о б р а щ а т ь с я к е г о с в о й с т в а м .
"Почему?" с п р о с и т е в ы . В о т с п и с о к п р и ч и н :
- Е с л и в ы х о т и т е р е а л и з о в а т ь б о л ь ш е , ч е м п р о с т о д о с т у п к с в о й с т в у , в а м н у ж н о п о м е н я т ь р е а л и з а ц и ю в о д н о м м е с т е ,
а н е п о в с е м у к о д у .
- В а л и д а ц и ю л е г к о р е а л и з о в а т ь н а у р о в н е р е а л и з а ц и и ` set ` .
- И н к а п с у л я ц и я в н у т р е н н е г о с о с т о я н и я .
- Л е г к о д о б а в и т ь л о г и р о в а н и е и о б р а б о т к у о ш и б о к н а у р о в н е г е т т е р о в и с е т т е р о в .
- В ы м о ж е т е л е н и в о п о д г р у ж а т ь с в о й с т в а в а ш е г о о б ъ е к т а , н а п р и м е р , с с е р в е р а .
2019-01-29 09:44:59 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-01-29 09:44:59 +02:00
` ` ` ts
2019-02-07 21:53:16 +02:00
type BankAccount = {
balance : number ;
2019-01-29 09:44:59 +02:00
// ...
}
const value = 100 ;
2019-02-12 08:40:50 +02:00
const account : BankAccount = {
2019-02-07 21:53:16 +02:00
balance : 0 ,
2019-02-12 08:40:50 +02:00
// ...
2019-02-07 21:53:16 +02:00
} ;
2019-01-29 09:44:59 +02:00
if ( value < 0 ) {
throw new Error ( 'Cannot set negative balance.' ) ;
}
account . balance = value ;
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
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 ;
}
// ...
}
2019-12-25 22:42:36 +07:00
// Теперь `BankAccount` инкапсулирует логику проверки.
// Если однажды спецификации изменятся, и нам понадобится дополнительное правило проверки,
// нам придется изменить только реализацию `сеттера`,
// оставив весь зависимый код без изменений.
2019-01-29 09:44:59 +02:00
const account = new BankAccount ( ) ;
account . balance = 100 ;
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-29 09:44:59 +02:00
2019-12-25 22:42:36 +07:00
# # # С о з д а в а й т е о б ъ е к т ы с п р и в а т н ы м и / з а щ и щ е н н ы м и п о л я м и
2019-01-29 09:44:59 +02:00
2019-12-25 22:42:36 +07:00
TypeScript п о д д е р ж и в а е т ` public ` * ( п о у м о л ч а н и ю ) * , ` protected ` и ` private ` с р е д с т в а д о с т у п а к с в о й с т в а м к л а с с а .
2019-01-29 09:44:59 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-01-29 09:44:59 +02:00
` ` ` ts
class Circle {
radius : number ;
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-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-01-29 09:44:59 +02:00
` ` ` ts
class Circle {
constructor ( private readonly radius : number ) {
}
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-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-29 09:44:59 +02:00
2020-02-05 21:32:28 +07:00
# # # И с п о л ь з у й т е и м м у т а б е л ь н о с т ь
2019-01-29 09:44:59 +02:00
2019-12-25 22:42:36 +07:00
С и с т е м а т и п о в в TypeScript п о з в о л я е т п о м е ч а т ь о т д е л ь н ы е с в о й с т в а и н т е р ф е й с а / к л а с с а к а к * readonly п о л я ( т о л ь к о д л я ч т е н и я ) * .
Э т о п о з в о л я е т в а м р а б о т а т ь ф у н к ц и о н а л ь н о ( н е о ж и д а н н а я м у т а ц и я э т о п л о х о ) .
Д л я б о л е е с л о ж н ы х с ц е н а р и е в е с т ь в с т р о е н н ы й т и п ` Readonly ` , к о т о р ы й п р и н и м а е т т и п ` T ` и п о м е ч а е т в с е е г о с в о й с т в а
т о л ь к о д л я ч т е н и я с и с п о л ь з о в а н и е м mapped types ( с м о т р и т е [ mapped types ] ( https : //www.typescriptlang.org/docs/handbook/advanced-types.html#mapped-types)).
2019-01-29 09:44:59 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-01-29 09:44:59 +02:00
` ` ` ts
interface Config {
host : string ;
port : string ;
db : string ;
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-01-29 09:44:59 +02:00
` ` ` ts
interface Config {
readonly host : string ;
readonly port : string ;
readonly db : string ;
}
` ` `
2020-02-05 21:32:28 +07:00
В с л у ч а е м а с с и в а в ы м о ж е т е с о з д а т ь м а с с и в т о л ь к о д л я ч т е н и я , и с п о л ь з у я ` ReadonlyArray<T> ` . к о т о р ы й н е п о з в о л я е т д е л а т ь и з м е н е н и я с и с п о л ь з о в а н и е м ` push() ` и ` fill() ` , н о м о ж н о и с п о л ь з о в а т ь ` concat() ` и ` slice() ` о н и н е м е н я ю т з н а ч е н и я .
2019-05-30 20:26:06 +09:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-05-30 20:26:06 +09:00
` ` ` ts
const array : number [ ] = [ 1 , 3 , 5 ] ;
array = [ ] ; // error
array . push ( 100 ) ; // array will updated
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-05-30 20:26:06 +09:00
` ` ` ts
const array : ReadonlyArray < number > = [ 1 , 3 , 5 ] ;
array = [ ] ; // error
array . push ( 100 ) ; // error
` ` `
2019-12-25 22:42:36 +07:00
О б ъ я в л е н и е а р г у м е н т о в т о л ь к о д л я ч т е н и я [ TypeScript 3.4 is a bit easier ] ( https : //github.com/microsoft/TypeScript/wiki/What's-new-in-TypeScript#improvements-for-readonlyarray-and-readonly-tuples).
2019-05-30 20:26:06 +09:00
` ` ` ts
function hoge ( args : readonly string [ ] ) {
args . push ( 1 ) ; // error
}
` ` `
2019-12-25 22:42:36 +07:00
П р е д п о ч т е н и е [ const assertions ] ( https : //github.com/microsoft/TypeScript/wiki/What's-new-in-TypeScript#const-assertions) для литеральных значений.
2019-05-30 20:26:06 +09:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-05-30 20:26:06 +09:00
` ` ` ts
const config = {
hello : 'world'
} ;
2020-02-05 21:32:28 +07:00
config . hello = 'world' ; // значение изменено
2019-05-30 20:26:06 +09:00
const array = [ 1 , 3 , 5 ] ;
2020-02-05 21:32:28 +07:00
array [ 0 ] = 10 ; // значение изменено
2019-05-30 20:26:06 +09:00
2020-02-05 21:32:28 +07:00
// записываемые объекты возвращаются
2019-05-30 20:26:06 +09:00
function readonlyData ( value : number ) {
return { value } ;
}
const result = readonlyData ( 100 ) ;
2020-02-05 21:32:28 +07:00
result . value = 200 ; // значение изменено
2019-05-30 20:26:06 +09:00
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-05-30 20:26:06 +09:00
` ` ` ts
2020-02-05 21:32:28 +07:00
// объект только для чтения
2019-05-30 20:26:06 +09:00
const config = {
hello : 'world'
} as const ;
2020-02-05 21:32:28 +07:00
config . hello = 'world' ; // ошибка
2019-05-30 20:26:06 +09:00
2020-02-05 21:32:28 +07:00
// массив только для чтения
2019-05-30 20:26:06 +09:00
const array = [ 1 , 3 , 5 ] as const ;
2020-02-05 21:32:28 +07:00
array [ 0 ] = 10 ; // ошибка
2019-05-30 20:26:06 +09:00
2020-02-05 21:32:28 +07:00
// Вы можете вернуть объект только для чтения
2019-05-30 20:26:06 +09:00
function readonlyData ( value : number ) {
return { value } as const ;
}
const result = readonlyData ( 100 ) ;
2020-02-05 21:32:28 +07:00
result . value = 200 ; // ошибка
2019-05-30 20:26:06 +09:00
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 18:18:27 +02:00
2019-12-25 22:42:36 +07:00
# # # Т и п ы vs . и н т е р ф е й с ы
2019-02-11 13:39:31 +02:00
2020-02-05 21:32:28 +07:00
И с п о л ь з у й т е т и п ы , к о г д а в а м м о ж е т п о н а д о б и т ь с я о б ъ е д и н е н и е и л и п е р е с е ч е н и е . И с п о л ь з у й т е и н т е р ф е й с , к о г д а х о т и т е и с п о л ь з о в а т ь ` extends `
2019-12-25 22:42:36 +07:00
и л и ` implements ` . О д н а к о с т р о г о г о п р а в и л а н е с у щ е с т в у е т , и с п о л ь з у й т е т о , ч т о р а б о т а е т у в а с .
Д л я б о л е е п о д р о б н о г о о б ъ я с н е н и я п о с м о т р и т е э т о [ о т в е т ы ] ( https : //stackoverflow.com/questions/37233735/typescript-interfaces-vs-types/54101543#54101543)
о р а з л и ч и я х м е ж д у ` type ` and ` interface ` в TypeScript .
2019-02-11 13:39:31 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-11 13:39:31 +02:00
` ` ` ts
interface EmailConfig {
// ...
}
interface DbConfig {
// ...
}
interface Config {
// ...
}
//...
2019-02-13 19:15:40 +09:00
type Shape = {
2019-02-11 13:39:31 +02:00
// ...
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-11 13:39:31 +02:00
` ` ` ts
2019-02-13 19:15:40 +09:00
type EmailConfig = {
2019-02-11 13:39:31 +02:00
// ...
}
2019-02-13 19:15:40 +09:00
type DbConfig = {
2019-02-11 13:39:31 +02:00
// ...
}
type Config = EmailConfig | DbConfig ;
// ...
interface Shape {
// ...
}
class Circle implements Shape {
// ...
}
class Square implements Shape {
// ...
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-11 13:39:31 +02:00
2019-12-25 22:42:36 +07:00
# # К л а с с ы
2019-02-04 18:09:04 +02:00
2019-12-25 22:42:36 +07:00
# # # К л а с с ы д о л ж н ы б ы т ь м а л е н ь к и м и
2019-02-04 18:09:04 +02:00
2019-12-26 17:11:07 +07:00
Р а з м е р к л а с с а и з м е р я е т с я е г о о т в е т с т в е н н о с т ь ю . С л е д у я * П р и н ц и п у е д и н с т в е н н о й о т в е т с т в е н н о с т и * к л а с с д о л ж е н б ы т ь м а л е н ь к и м .
2019-02-04 18:09:04 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
class Dashboard {
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-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
class Dashboard {
disable ( ) : void { /* ... */ }
enable ( ) : void { /* ... */ }
getVersion ( ) : string { /* ... */ }
}
2020-02-05 21:32:28 +07:00
// разделить обязанности, переместив оставшиеся методы в другие классы
2019-02-04 18:09:04 +02:00
// ...
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 18:09:04 +02:00
2019-12-25 22:42:36 +07:00
# # # В ы с о к а я с п л о ч е н н о с т ь н и з к а я с в я з ь
2019-02-04 18:09:04 +02:00
2019-12-26 17:11:07 +07:00
С п л о ч е н н о с т ь о п р е д е л я е т с т е п е н ь , в к о т о р о й ч л е н ы к л а с с а с в я з а н ы д р у г с д р у г о м . В и д е а л е в с е п о л я в к л а с с е д о л ж н ы
и с п о л ь з о в а т ь с я к а ж д ы м м е т о д о м . М ы г о в о р и м , ч т о к л а с с * м а к с и м а л ь н о с в я з н ы й * . Н а п р а к т и к е э т о , о д н а к о , н е в с е г д а в о з м о ж н о
2020-02-05 21:32:28 +07:00
и д а ж е н е ж е л а т е л ь н о . О д н а к о в ы д о л ж н ы д о б и в а т ь с я , т о г о ч т о б ы с п л о ч е н н о с т ь б ы л а в ы с о к о й .
2019-02-04 18:18:27 +02:00
2020-02-05 21:32:28 +07:00
С в я з а н н о с т ь о т н о с и т с я и к т о м у , к а к с в я з а н ы и л и з а в и с и м ы д в а к л а с с а д р у г о т д р у г а . К л а с с ы с ч и т а ю т с я с л а б о с в я з а н н ы м и е с л и
2019-12-26 17:11:07 +07:00
и з м е н е н и я в о д н о м и з н и х н е в л и я ю т н а д р у г о й .
2019-02-04 18:09:04 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
class UserManager {
2020-02-05 21:32:28 +07:00
// Плохо: каждая закрытая переменная используется той или иной группой методов.
// Это ясно показывает, что класс несет больше, чем одну ответственность
// Если мне нужно только создать сервис, чтобы получить транзакции для пользователя,
// Я все еще вынужден передавать экземпляр `emailSender`.
2019-02-04 18:09:04 +02:00
constructor (
private readonly db : Database ,
private readonly emailSender : EmailSender ) {
}
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-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
class UserService {
constructor ( private readonly db : Database ) {
}
async getUser ( id : number ) : Promise < User > {
2019-02-07 21:53:16 +02:00
return await this . db . users . findOne ( { id } ) ;
2019-02-04 18:09:04 +02:00
}
async getTransactions ( userId : number ) : Promise < Transaction [ ] > {
2019-02-07 21:53:16 +02:00
return await this . db . transactions . find ( { userId } ) ;
2019-02-04 18:09:04 +02:00
}
}
class UserNotifier {
constructor ( private readonly emailSender : EmailSender ) {
}
async sendGreeting ( ) : Promise < void > {
2019-02-07 21:53:16 +02:00
await this . emailSender . send ( 'Welcome!' ) ;
2019-02-04 18:09:04 +02:00
}
async sendNotification ( text : string ) : Promise < void > {
2019-02-07 21:53:16 +02:00
await this . emailSender . send ( text ) ;
2019-02-04 18:09:04 +02:00
}
async sendNewsletter ( ) : Promise < void > {
// ...
}
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 18:09:04 +02:00
2019-12-25 22:42:36 +07:00
# # # П р е д п о ч и т а й т е к о м п о з и ц и ю н а с л е д о в а н и ю
2019-02-04 18:09:04 +02:00
2020-02-05 21:32:28 +07:00
К а к с к а з а н о в [ Design Patterns ] ( https : //en.wikipedia.org/wiki/Design_Patterns) от банды черытех вы должны
2019-12-26 17:11:07 +07:00
* П р е д п о ч и т а т ь к о м п о з и ц и ю н а с л е д о в а н и ю * г д е м о ж е т е . Е с т ь м н о г о в е с к и х п р и ч и н и с п о л ь з о в а т ь н а с л е д о в а н и е и м н о г о х о р о ш и х
п р и ч и н и с п о л ь з о в а т ь к о м п о з и ц и ю . С у т ь э т о г о п р и н ц и п а в т о м , ч т о е с л и в а ш у м и н с т и н к т и в н о и д е т н а н а с л е д о в а н и е ,
п о п р о б у й т е п о д у м а т ь , м о ж е т л и к о м п о з и ц и я л у ч ш е с м о д е л и р о в а т ь в а ш у п р о б л е м у . В н е к о т о р ы х с л у ч а я х м о ж е т .
2019-02-04 18:09:04 +02:00
2019-12-26 17:11:07 +07:00
Т о г д а в ы м о ж е т е с п р о с и т ь : "Когда я должен использовать наследование?" Э т о з а в и с и т о т в а ш е й п р о б л е м ы , н о э т о д о с т о й н ы й
с п и с о к , к о г д а н а с л е д о в а н и е и м е е т б о л ь ш е с м ы с л а , ч е м к о м п о з и ц и я :
2019-02-04 18:09:04 +02:00
2019-12-26 17:11:07 +07:00
1 . В а ш е н а с л е д о в а н и е п р е д с т а в л я е т с о б о й "is-a" о т н о ш е н и я а н е "has-a" о т н о ш е н и я ( Human - > Animal vs . User - > UserDetails ) .
2019-02-04 18:09:04 +02:00
2019-12-26 17:11:07 +07:00
2 . В ы м о ж е т е п о в т о р н о и с п о л ь з о в а т ь к о д и з б а з о в ы х к л а с с о в ( Л ю д и м о г у т д в и г а т ь с я к а к в с е ж и в о т н ы е ) .
2019-02-04 18:09:04 +02:00
2019-12-26 17:11:07 +07:00
3 . В ы х о т и т е в н е с т и г л о б а л ь н ы е и з м е н е н и я в п р о и з в о д н ы е к л а с с ы , и з м е н и в б а з о в ы й к л а с с . ( И з м е н е н и е р а с х о д а к а л о р и й у в с е х ж и в о т н ы х п р и и х п е р е м е щ е н и и ) .
2019-02-04 18:09:04 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
class Employee {
constructor (
2019-02-06 09:57:52 +02:00
private readonly name : string ,
private readonly email : string ) {
2019-02-04 18:09:04 +02:00
}
// ...
}
2020-02-05 21:32:28 +07:00
// Плохо, потому что Employees "имеют" налоговые данные. EmployeeTaxData не является типом 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-04 18:09:04 +02:00
private readonly salary : number ) {
super ( name , email ) ;
}
// ...
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-04 18:09:04 +02:00
` ` ` ts
class Employee {
private taxData : EmployeeTaxData ;
constructor (
2019-02-06 09:57:52 +02:00
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 {
constructor (
2019-02-12 08:40:50 +02:00
public readonly ssn : string ,
2019-02-04 18:09:04 +02:00
public readonly salary : number ) {
}
// ...
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 18:09:04 +02:00
2020-02-05 21:32:28 +07:00
# # # И с п о л ь з у й т е ц е п о ч к и в ы з о в о в
2019-02-04 18:09:04 +02:00
2019-12-26 17:11:07 +07:00
Э т о т п а т т е р р н о ч е н ь п о л е з е н и о б ы ч н о и с п о л ь з у е т с я в о м н о г и х б и б л и о т е к а х . Э т о п о з в о л я е т в а ш е м у к о д у б ы т ь в ы р а з и т е л ь н ы м
и м е н е е м н о г о с л о в н ы м . П о э т о й п р и ч и н е и с п о л ь з у й т е ц е п о ч к у м е т о д о в и п о с м о т р и т е , н а с к о л ь к о ч и с т ы м б у д е т в а ш к о д .
2019-02-04 18:09:04 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
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-06 09:57:52 +02:00
const queryBuilder = new QueryBuilder ( ) ;
queryBuilder . from ( 'users' ) ;
queryBuilder . page ( 1 , 100 ) ;
queryBuilder . 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-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
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-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 18:09:04 +02:00
2019-02-04 18:08:52 +02:00
# # SOLID
2019-12-25 22:42:36 +07:00
# # # П р и н ц и п е д и н о й о т в е т с т в е н н о с т и ( SRP )
2019-02-04 18:08:52 +02:00
2020-02-05 21:32:28 +07:00
К а к н а п и с а н о в Ч и с т о м К о д е , "Должна быть лишь одна причина для изменения класса" . З а м а н ч и в о п р е д с т а в и т ь с е б е к л а с с ,
2019-12-26 17:11:07 +07:00
п е р е п о л н е н н ы й б о л ь ш и м к о л и ч е с т в о м ф у н к ц и о н а л а , с л о в н о в п о л е т в а м п о з в о л и л и в з я т ь в с е г о о д и н ч е м о д а н . П р о б л е м а в т о м , ч т о
в а ш к л а с с н е б у д е т к о н ц е п т у а л ь н о с в я з а н , и в ы б у д е т е ч а с т о е г о и з м е н я т ь . О ч е н ь в а ж н о м и н и м и з и р о в а т ь и з м е н е н и я в к л а с с е .
К о г д а в ы в н о с и т е и з м е н е н и я в к л а с с с о г р о м н ы м ф у н к ц и о н а л о м , т я ж е л о о т с л е д и т ь п о с л е д с т в и я в а ш и х и з м е н е н и й .
2019-02-04 18:08:52 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
class UserSettings {
constructor ( private readonly user : User ) {
}
changeSettings ( settings : UserSettings ) {
if ( this . verifyCredentials ( ) ) {
// ...
}
}
verifyCredentials() {
// ...
}
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
class UserAuth {
constructor ( private readonly user : User ) {
}
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-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 18:08:52 +02:00
2019-12-25 22:42:36 +07:00
# # # П р и н ц и п о т к р ы т о с т и / з а к р ы т о с т и ( OCP )
2019-02-04 18:08:52 +02:00
2019-12-26 17:11:07 +07:00
К а к з а я в и л Б е р т р а н М е й е р , " п р о г р а м м н ы е с у щ н о с т и ( к л а с с ы , м о д у л и , ф у н к ц и и и т . д . ) д о л ж н ы о с т а в а т ь с я о т к р ы т ы м и д л я
р а с ш и р е н и я , н о з а к р ы т ы м и д л я м о д и ф и к а ц и и . " Ч т о э т о о з н а ч а е т н а п р а к т и к е ? П р и н ц и п з а к р е п л я е т , ч т о в ы д о л ж н ы п о з в о л и т ь
п о л ь з о в а т е л я м д о б а в л я т ь н о в ы е ф у н к ц и о н а л ь н ы е в о з м о ж н о с т и , н о б е з и з м е н е н и я с у щ е с т в у ю щ е г о к о д а .
2019-02-04 18:08:52 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
class AjaxAdapter extends Adapter {
constructor ( ) {
super ( ) ;
}
// ...
}
class NodeAdapter extends Adapter {
constructor ( ) {
super ( ) ;
}
// ...
}
class HttpRequester {
constructor ( private readonly adapter : Adapter ) {
}
async fetch < T > ( url : string ) : Promise < T > {
if ( this . adapter instanceof AjaxAdapter ) {
const response = await makeAjaxCall < T > ( url ) ;
2019-12-26 17:11:07 +07:00
// трансформируем ответ и возвращаем
2019-02-04 18:08:52 +02:00
} else if ( this . adapter instanceof NodeAdapter ) {
const response = await makeHttpCall < T > ( url ) ;
2019-12-26 17:11:07 +07:00
// трансформируем ответ и возвращаем
2019-02-04 18:08:52 +02:00
}
}
}
function makeAjaxCall < T > ( url : string ) : Promise < T > {
2019-12-26 17:11:07 +07:00
// запрос и возвращение промиса
2019-02-04 18:08:52 +02:00
}
function makeHttpCall < T > ( url : string ) : Promise < T > {
2019-12-26 17:11:07 +07:00
// запрос и возвращение промиса
2019-02-04 18:08:52 +02:00
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
abstract class Adapter {
abstract async request < T > ( url : string ) : Promise < T > ;
2019-02-06 09:57:52 +02:00
2019-12-26 17:11:07 +07:00
// общий код для подклассов ...
2019-02-04 18:08:52 +02:00
}
class AjaxAdapter extends Adapter {
constructor ( ) {
super ( ) ;
}
async request < T > ( url : string ) : Promise < T > {
2019-12-26 17:11:07 +07:00
// запрос и возвращение промиса
2019-02-04 18:08:52 +02:00
}
// ...
}
class NodeAdapter extends Adapter {
constructor ( ) {
super ( ) ;
}
async request < T > ( url : string ) : Promise < T > {
2019-12-26 17:11:07 +07:00
// запрос и возвращение промиса
2019-02-04 18:08:52 +02:00
}
// ...
}
class HttpRequester {
constructor ( private readonly adapter : Adapter ) {
}
async fetch < T > ( url : string ) : Promise < T > {
const response = await this . adapter . request < T > ( url ) ;
2019-12-26 17:11:07 +07:00
// трансформируем ответ и возвращаем
2019-02-04 18:08:52 +02:00
}
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 18:08:52 +02:00
2019-12-25 22:42:36 +07:00
# # # П р и н ц и п п о д с т а н о в к и Л и с к о в ( LSP )
2019-02-04 18:08:52 +02:00
2019-12-26 17:11:07 +07:00
Э т о с т р а ш н ы й т е р м и н д л я о ч е н ь п р о с т о й к о н ц е п ц и и . Ф о р м а л ь н ы м я з ы к о м о н з в у ч и т к а к " Е с л и S я в л я е т с я п о д т и п о м T , т о
о б ъ е к т ы т и п а Т м о г у т б ы т ь з а м е н е н ы н а о б ъ е к т ы т и п а S ( т о е с т ь , о б ъ е к т ы т и п а S м о г у т з а м е н и т ь о б ъ е к т ы т и п а Т ) б е з в л и я н и я
н а в а ж н ы е с в о й с т в а п р о г р а м м ы ( к о р р е к т н о с т ь , п р и г о д н о с т ь д л я в ы п о л н е н и я з а д а ч и т . д . ) . " Э т о е щ е б о л е е с т р а ш н о е о п р е д е л е н и е .
2019-02-04 18:08:52 +02:00
2019-12-26 17:11:07 +07:00
Л у ч ш е е о б ъ я с н е н и е з а к л ю ч а е т с я в т о м , ч т о е с л и у в а с е с т ь р о д и т е л ь с к и й и д о ч е р н и й к л а с с ы , т о о н и м о г у т и с п о л ь з о в а т ь с я к а к
в з а и м о з а м е н я е м ы е , н е п р и в о д я п р и э т о м к н е к о р р е к т н ы м р е з у л ь т а т а м . Э т о п о - п р е ж н е м у м о ж е т с б и в а т ь с т о л к у , т а к ч т о д а в а й т е
в з г л я н е м н а к л а с с и ч е с к и й п р и м е р к в а д р а т а - п р я м о у г о л ь н и к а . М а т е м а т и ч е с к и к в а д р а т п р е д с т а в л я е т с о б о й п р я м о у г о л ь н и к , н о е с л и
в ы с м о д е л и р у е т е и х о т н о ш е н и я ч е р е з н а с л е д о в а н и е , в ы б ы с т р о н а т к н е т е с ь н а н е п р и я т н о с т и . .
2019-02-04 18:08:52 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
class Rectangle {
constructor (
2019-02-06 09:57:52 +02:00
protected width : number = 0 ,
2019-02-04 18:08:52 +02:00
protected height : number = 0 ) {
}
2019-02-06 09:57:52 +02:00
setColor ( color : string ) : this {
2019-02-04 18:08:52 +02:00
// ...
}
render ( area : number ) {
// ...
}
2019-02-06 09:57:52 +02:00
setWidth ( width : number ) : this {
2019-02-04 18:08:52 +02:00
this . width = width ;
2019-02-06 09:57:52 +02:00
return this ;
2019-02-04 18:08:52 +02:00
}
2019-02-06 09:57:52 +02:00
setHeight ( height : number ) : this {
2019-02-04 18:08:52 +02:00
this . height = height ;
2019-02-06 09:57:52 +02:00
return this ;
2019-02-04 18:08:52 +02:00
}
getArea ( ) : number {
return this . width * this . height ;
}
}
class Square extends Rectangle {
2019-02-06 09:57:52 +02:00
setWidth ( width : number ) : this {
2019-02-04 18:08:52 +02:00
this . width = width ;
this . height = width ;
2019-02-06 09:57:52 +02:00
return this ;
2019-02-04 18:08:52 +02:00
}
2019-02-06 09:57:52 +02:00
setHeight ( height : number ) : this {
2019-02-04 18:08:52 +02:00
this . width = height ;
this . height = height ;
2019-02-06 09:57:52 +02:00
return this ;
2019-02-04 18:08:52 +02:00
}
}
function renderLargeRectangles ( rectangles : Rectangle [ ] ) {
rectangles . forEach ( ( rectangle ) = > {
2019-02-06 09:57:52 +02:00
const area = rectangle
. setWidth ( 4 )
. setHeight ( 5 )
. 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-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
abstract class Shape {
2019-02-06 09:57:52 +02:00
setColor ( color : string ) : this {
2019-02-04 18:08:52 +02:00
// ...
}
render ( area : number ) {
// ...
}
abstract getArea ( ) : number ;
}
class Rectangle extends Shape {
constructor (
2019-02-06 09:57:52 +02:00
private readonly width = 0 ,
2019-02-04 18:08:52 +02:00
private readonly height = 0 ) {
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 [ ] ) {
shapes . forEach ( ( shape ) = > {
const area = shape . getArea ( ) ;
shape . render ( area ) ;
} ) ;
}
const shapes = [ new Rectangle ( 4 , 5 ) , new Rectangle ( 4 , 5 ) , new Square ( 5 ) ] ;
renderLargeShapes ( shapes ) ;
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 18:08:52 +02:00
2019-12-25 22:42:36 +07:00
# # # П р и н ц и п р а з д е л е н и я и н т е р ф е й с а ( ISP )
2019-02-04 18:08:52 +02:00
2019-12-26 17:11:07 +07:00
ISP г о в о р и т ч т о "Клиенты не должны зависеть от классов, которые они не используют." . Э т о т п р и н ц и п о ч е н ь
с в я з а н с П р и н ц и п о м е д и н о й о т в е т с т в е н н о с т и .
Н а с а м о м д е л е э т о о з н а ч а е т , ч т о в ы в с е г д а д о л ж н ы п р о е к т и р о в а т ь с в о и а б с т р а к ц и и т а к и м о б р а з о м , ч т о б ы к л и е н т ы , к о т о р ы е
и с п о л ь з у ю т о т к р ы т ы е м е т о д ы н е п о л у ч а л и в е с ь п и р о г . Э т о т а к ж е в к л ю ч а е т в с е б я в о з л о ж е н и е н а к л и е н т о в б р е м е н и р е а л и з а ц и и
м е т о д о в , к о т о р ы е и м н а с а м о м д е л е н е н у ж н ы .
2019-02-04 18:08:52 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
2019-02-06 10:40:08 +02:00
interface SmartPrinter {
2019-02-04 18:08:52 +02:00
print ( ) ;
fax ( ) ;
scan ( ) ;
}
2019-02-06 10:40:08 +02:00
class AllInOnePrinter implements SmartPrinter {
2019-02-04 18:08:52 +02:00
print() {
// ...
}
fax() {
// ...
}
scan() {
// ...
}
}
2019-02-06 10:40:08 +02:00
class EconomicPrinter implements SmartPrinter {
2019-02-04 18:08:52 +02:00
print() {
// ...
}
fax() {
throw new Error ( 'Fax not supported.' ) ;
}
scan() {
throw new Error ( 'Scan not supported.' ) ;
}
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-04 18:08:52 +02:00
` ` ` ts
2019-02-06 10:40:08 +02:00
interface Printer {
2019-02-04 18:08:52 +02:00
print ( ) ;
}
2019-02-06 10:40:08 +02:00
interface Fax {
2019-02-04 18:08:52 +02:00
fax ( ) ;
}
2019-02-06 10:40:08 +02:00
interface Scanner {
2019-02-04 18:08:52 +02:00
scan ( ) ;
}
2019-02-06 10:40:08 +02:00
class AllInOnePrinter implements Printer , Fax , Scanner {
2019-02-04 18:08:52 +02:00
print() {
// ...
}
fax() {
// ...
}
scan() {
// ...
}
}
2019-02-06 10:40:08 +02:00
class EconomicPrinter implements Printer {
2019-02-04 18:08:52 +02:00
print() {
// ...
}
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 18:08:52 +02:00
2019-12-25 22:42:36 +07:00
# # # П р и н ц и п и н в е р с и и з а в и с и м о с т е й ( DIP )
2019-02-04 18:08:52 +02:00
2019-12-26 17:11:07 +07:00
Э т о т п р и н ц и п з а к р е п л я е т д в е в а ж н ы е в е щ и :
2019-02-04 18:08:52 +02:00
2019-12-26 17:11:07 +07:00
1 . М о д у л и в ы с ш е г о у р о в н я н е д о л ж н ы з а в и с е т ь о т м о д у л е й н и з ш е г о у р о в н я . О б а д о л ж н ы з а в и с е т ь о т а б с т р а к ц и й .
2019-02-04 18:08:52 +02:00
2019-12-26 17:11:07 +07:00
2 . А б с т р а к ц и и н е д о л ж н ы з а в и с е т ь о т д е т а л е й . Д е т а л и д о л ж н ы з а в и с е т ь о т а б с т р а к ц и й .
2019-02-04 18:08:52 +02:00
2019-12-26 17:11:07 +07:00
С н а ч а л а т р у д н о п о н я т ь э т о т п р и н ц и п . Н о е с л и в ы р а б о т а л и с AngularJS , в ы в и д е л и р е а л и з а ц и ю э т о г о п р и н ц и п а в в и д е
Dependency Injection ( DI ) . Н е с м о т р я н а т о , ч т о о н и н е я в л я ю т с я и д е н т и ч н ы м и п о н я т и я м и , DIP д а ё т в о з м о ж н о с т ь о т г р а н и ч и т ь
м о д у л и в ы с о к о г о у р о в н я о т д е т а л е й м о д у л е й н и з к о г о у р о в н я и у с т а н о в к и и х . О н м о ж е т с д е л а т ь э т о ч е р е з DI . Э т о т п р и н ц и п
у м е н ь ш а е т с в я з ь м е ж д у м о д у л я м и . Е с л и в а ш и м о д у л и т е с н о с в я з а н ы , и х т я ж е л о р е ф а к т о р и т ь .
2019-02-04 18:08:52 +02:00
2019-12-26 17:11:07 +07:00
DIP о б ы ч н о д о с т и г а е т с я и с п о л ь з о в а н и е м к о н т е й н е р а и н в е р с и и у п р а в л е н и я ( IoC ) . П р и м е р м о щ н о г о к о н т е й н е р а IoC д л я
TypeScript э т о [ InversifyJs ] ( https : //www.npmjs.com/package/inversify)
2019-02-04 18:08:52 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
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-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
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-06 10:43:37 +02: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-06 09:57:52 +02: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-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 18:08:52 +02:00
2019-12-25 22:42:36 +07:00
# # Т е с т и р о в а н и е
2019-02-04 09:18:53 +02:00
2019-12-28 21:54:44 +07:00
Т е с т и р о в а н и е в а ж н е е д е п л о я . Е с л и у в а с н е т т е с т о в и л и и х м а л о , т о к а ж д ы й р а з п р и в ы к л а д к е к о д а н а б о е в ы е с е р в е р а у в а с
н е б у д е т у в е р е н н о с т и , ч т о н и ч е г о н е с л о м а е т с я .
Р е ш е н и е о д о с т а т о ч н о м к о л и ч е с т в е т е с т о в о с т а е т с я н а с о в е с т и в а ш е й к о м а н д ы , н о 100 % п о к р ы т и е т е с т а м и в с е х в ы р а ж е н и й и
в е т в л е н и й о б е с п е ч и в а е т в ы с о к о е д о в е р и е к в а ш е м у к о д у и с п о к о й с т в и е в с е х р а з р а б о т ч и к о в . И з э т о г о с л е д у е т , ч т о в д о п о л н е н и е
к о т л и ч н о м у ф р е й м в о р к у д л я т е с т и р о в а н и я , н е о б х о д и м о т а к ж е и с п о л ь з о в а т ь х о р о ш и й [ и н с т р у м е н т п о к р ы т и я ] ( https : //github.com/gotwarlost/istanbul).
2019-02-04 09:18:53 +02:00
2020-02-05 21:32:28 +07:00
Н е т н и к а к о г о о п р а в д а н и я , ч т о б ы н е п и с а т ь т е с т ы . Е с т ь [ м н о г о х о р о ш и х ф р е й м в о р к о в д л я т е с т и р о в а н и я н а JS ] ( http : //jstherightway.org/#testing-tools) с поддержкой типов для TypeScript, так что вы найдите тот который понравится вашей команде. Когда вы найдете тот, который работает для вашей команды, тогда стремитесь всегда писать тесты для каждой новой фичи/модуля, которую вы пишете. Если вы предпочитаете метод тест-ориентированной разработки (TDD), это замечательно, но главное - просто убедиться, что вы достигли своих целей покрытия, прежде чем запускать какую-либо функцию или реорганизовать существующую.
2019-02-04 09:18:53 +02:00
2019-12-25 22:42:36 +07:00
# # # Т р и з а к о н а TDD
2019-02-04 09:18:53 +02:00
2019-12-28 21:54:44 +07:00
1 . Н о в ы й р а б о ч и й к о д п и ш е т с я т о л ь к о п о с л е т о г о , к а к б у д е т н а п и с а н м о д у л ь н ы й т е с т , к о т о р ы й н е п р о х о д и т .
2019-02-04 09:18:53 +02:00
2020-02-05 21:32:28 +07:00
2 . В ы п и ш е т е р о в н о т а к о й о б ъ е м к о д а м о д у л ь н о г о т е с т а , к а к о й н е о б х о д и м д л я т о г о , ч т о б ы э т о т т е с т н е п р о х о д и л ( е с л и к о д т е с т а н е к о м п и л и р у е т с я , с ч и т а е т с я , ч т о о н н е п р о х о д и т ) .
2019-02-04 09:18:53 +02:00
2020-02-05 21:32:28 +07:00
3 . В ы п и ш е т е р о в н о т а к о й о б ъ е м р а б о ч е г о к о д а , к а к о й н е о б х о д и м д л я п р о х о ж д е н и я м о д у л ь н о г о т е с т а , к о т о р ы й в д а н н ы й м о м е н т н е п р о х о д и т .
2019-02-04 09:18:53 +02:00
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 09:18:53 +02:00
2019-12-25 22:42:36 +07:00
# # # П р а в и л а F . I . R . S . T .
2019-02-04 09:18:53 +02:00
2019-12-28 21:54:44 +07:00
Ч и с т ы е т е с т ы д о л ж н ы с л е д о в а т ь п р а в и л а м :
2019-02-04 09:18:53 +02:00
2020-02-05 21:32:28 +07:00
- * * Б ы с т р о т а ( Fast ) * * Т е с т ы д о л ж н ы в ы п о л н я т ь с я б ы с т р о . В с е м ы з н а е м , ч т о р а з р а б о т ч и к и л ю д и , а л ю д и л е н и в ы , п о с к о л ь к у э т и в ы р а ж е н и я я в л я ю т с я “ т р а н з и т и в н ы м и ” , т о м о ж н о с д е л а т ь в ы в о д , ч т о л ю д и т о ж е л е н и в ы . А л е н и в ы й ч е л о в е к н е з а х о ч е т з а п у с к а т ь т е с т ы п р и к а ж д о м и з м е н е н и и к о д а , е с л и о н и б у д у т д о л г о в ы п о л н я т ь с я .
2019-02-04 09:18:53 +02:00
2020-02-05 21:32:28 +07:00
- * * Н е з а в и с и м о с т ь ( Independent ) * * Т е с т ы н е д о л ж н ы з а в и с е т ь д р у г о т д р у г а . О н и д о л ж н ы о б е с п е ч и в а т ь о д и н а к о в ы е в ы х о д н ы е д а н н ы е н е з а в и с и м о о т т о г о , в ы п о л н я ю т с я л и о н и н е з а в и с и м о и л и в с е в м е с т е в л ю б о м п о р я д к е .
2019-02-04 09:18:53 +02:00
2019-12-28 21:54:44 +07:00
- * * П о в т о р я е м о с т ь ( Repeatable ) * * Т е с т ы д о л ж н ы в ы п о л н я т ь с я в л ю б о й с р е д е , и н е д о л ж н о б ы т ь н и к а к и х о п р а в д а н и й т о м у , п о ч е м у
о н и п р о в а л и л и с ь .
2019-02-04 09:18:53 +02:00
2020-02-05 21:32:28 +07:00
- * * О ч е в и д н о с т ь ( Self - Validating ) * * Т е с т д о л ж е н о т в е ч а т ь л и б о * Passed * , л и б о * Failed * . В а м н е н у ж н о с р а в н и в а т ь ф а й л ы л о г о в , д л ч т о б ы о т в е т и т ь , ч т о т е с т п р о й д е н .
2019-02-04 09:18:53 +02:00
2020-02-05 21:32:28 +07:00
- * * С в о е в р е м е н н о с т ь ( Timely ) * * Ю н и т т е с т ы д о л ж н ы б ы т ь н а п и с а н ы п е р е д п р о и з в о д с т в е н н ы м к о д о м . Е с л и в ы п и ш е т е т е с т ы п о с л е п р о и з в о д с т в е н н о г о к о д а , т о в а м м о ж е т п о к а з а т ь с я , ч т о п и с а т ь т е с т ы с л и ш к о м с л о ж н о .
2019-02-04 09:18:53 +02:00
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 09:18:53 +02:00
2019-12-28 21:54:44 +07:00
# # # О д и н к е й с н а т е с т
2019-02-04 09:18:53 +02:00
2020-02-05 21:32:28 +07:00
Т е с т ы т а к ж е д о л ж н ы с о о т в е т с т в о в а т ь * П р и н ц и п у е д и н о й о т в е т с т в е н н о с т и ( SPP ) * . Д е л а й т е т о л ь к о о д н о у т в е р ж д е н и е з а е д и н и ц у т е с т а . ( ps . н е п р е н е б р е г а й т е э т и м п р а в и л о м )
2019-02-04 09:18:53 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
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-07 21:53:16 +02:00
assert . equal ( '1/31/2015' , date . addDays ( 30 ) ) ;
2019-02-04 09:18:53 +02:00
date = new AwesomeDate ( '2/1/2016' ) ;
2019-02-07 21:53:16 +02:00
assert . equal ( '2/29/2016' , date . addDays ( 28 ) ) ;
2019-02-04 09:18:53 +02:00
date = new AwesomeDate ( '2/1/2015' ) ;
2019-02-07 21:53:16 +02:00
assert . equal ( '3/1/2015' , date . addDays ( 28 ) ) ;
2019-02-04 09:18:53 +02:00
} ) ;
} ) ;
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
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-07 21:53:16 +02:00
assert . equal ( '1/31/2015' , date . addDays ( 30 ) ) ;
2019-02-04 09:18:53 +02:00
} ) ;
it ( 'handles leap year' , ( ) = > {
const date = new AwesomeDate ( '2/1/2016' ) ;
2019-02-07 21:53:16 +02:00
assert . equal ( '2/29/2016' , date . addDays ( 28 ) ) ;
2019-02-04 09:18:53 +02:00
} ) ;
it ( 'handles non-leap year' , ( ) = > {
const date = new AwesomeDate ( '2/1/2015' ) ;
2019-02-07 21:53:16 +02:00
assert . equal ( '3/1/2015' , date . addDays ( 28 ) ) ;
2019-02-04 09:18:53 +02:00
} ) ;
} ) ;
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 09:18:53 +02:00
2019-12-25 22:42:36 +07:00
# # # Н а з в а н и е т е с т а д о л ж н о р а с к р ы в а т ь е г о н а м е р е н и е
2019-02-04 09:18:53 +02:00
2019-12-28 21:54:44 +07:00
К о г д а т е с т н е п р о й д е н , е г о и м я я в л я е т с я п е р в ы м п р и з н а к о м т о г о , ч т о м о г л о п о й т и н е т а к .
2019-02-04 09:18:53 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
describe ( 'Calendar' , ( ) = > {
it ( '2/29/2020' , ( ) = > {
// ...
} ) ;
it ( 'throws' , ( ) = > {
// ...
} ) ;
} ) ;
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
describe ( 'Calendar' , ( ) = > {
it ( 'should handle leap year' , ( ) = > {
// ...
} ) ;
it ( 'should throw when format is invalid' , ( ) = > {
// ...
} ) ;
} ) ;
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 09:18:53 +02:00
2019-12-25 22:42:36 +07:00
# # А с и н х р о н н о с т ь
2019-01-29 17:25:00 +02:00
2020-02-05 21:32:28 +07:00
# # # И с п о л ь з у й т е promises а н е callbacks
Callback - ф у н к ц и и у х у д ш а ю т ч и т а е м о с т ь и п р и в о д я т к ч р е з м е р н о м у к о л и ч е с т в у в л о ж е н н о с т и * ( а д о б р а т н ы х в ы з о в о в ( callback hell ) ) * . С у щ е с т в у ю т у т и л и т ы , к о т о р ы е п р е о б р а з у ю т с у щ е с т в у ю щ и е ф у н к ц и и , и с п о л ь з у я с т и л ь callback - о в , в в е р с и ю , к о т о р а я в о з в р а щ а е т п р о м и с ы ( д л я Node . js с м о т р и т е [ ` util.promisify ` ] ( https : //nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original), для общего назначения смотрите [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-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-01-29 17:25:00 +02:00
` ` ` ts
import { get } from 'request' ;
import { writeFile } from 'fs' ;
2019-02-06 10:43:37 +02: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 {
writeFile ( saveTo , response . body , ( error ) = > {
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
}
downloadPage ( 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin' , 'article.html' , ( error , content ) = > {
if ( error ) {
console . error ( error ) ;
} else {
console . log ( content ) ;
}
} ) ;
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
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 > {
return get ( url )
2019-02-06 10:43:37 +02:00
. then ( response = > write ( saveTo , response ) ) ;
2019-01-29 17:25:00 +02:00
}
downloadPage ( 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin' , 'article.html' )
. then ( content = > console . log ( content ) )
. catch ( error = > console . error ( error ) ) ;
` ` `
2019-12-28 21:54:44 +07:00
П р о м и с ы п о д д е р ж и в а ю т н е с к о л ь к о в с п о м о г а т е л ь н ы х м е т о д о в , к о т о р ы е п о м о г а ю т с д е л а т ь к о д б о л е е п о н я т н ы м :
2019-01-31 10:18:41 +02:00
2019-12-28 21:54:44 +07:00
| М е т о д ы | О п и с а н и е |
2019-01-31 10:18:41 +02:00
| -- -- -- -- -- -- -- -- -- -- -- -- | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - |
2019-12-28 21:54:44 +07:00
| ` Promise.resolve(value) ` | П р е о б р а з у й т е з н а ч е н и е в р е ш е н н ы й п р о м и с . |
| ` Promise.reject(error) ` | П р е о б р а з у й т е о ш и б к у в о т к л о н е н н ы й п р о м и с . |
| ` Promise.all(promises) ` | В о з в р а щ а е т н о в ы й п р о м и с , к о т о р ы й в ы п о л н я е т с я с м а с с и в о м з н а ч е н и й в ы п о л н е н и я д л я п е р е д а н н ы х п р о м и с о в и л и о т к л о н я е т с я п о п р и ч и н е п е р в о г о п р о м и с а , к о т о р ы й в ы п о л н я е т с я с о ш и б к о й . |
| ` Promise.race(promises) ` | В о з в р а щ а е т н о в ы й п р о м и с , к о т о р ы й в ы п о л н е н / о т к л о н е н с р е з у л ь т а т о м / о ш и б к о й п е р в о г о в ы п о л н е н н о г о п р о м и с а и з м а с с и в а п е р е д а н н ы х п р о м и с о в . |
2019-01-31 10:18:41 +02:00
2019-12-28 21:54:44 +07:00
` Promise.all ` о с о б е н н о п о л е з е н , к о г д а е с т ь н е о б х о д и м о с т ь з а п у с к а т ь з а д а ч и п а р а л л е л ь н о . ` Promise.race ` о б л е г ч а е т р е а л и з а ц и ю т а к и х в е щ е й , к а к т а й м - а у т ы д л я п р о м и с о в .
2019-01-31 10:18:41 +02:00
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-29 17:25:00 +02:00
2019-12-25 22:42:36 +07:00
# # # Async / Await д е л а е т к о д ч и щ е , ч е м п р о м и с ы
2019-01-29 17:25:00 +02:00
2019-12-28 21:54:44 +07:00
С п о м о щ ь ю с и н т а к с и с а ` async ` ` await ` в ы м о ж е т е н а п и с а т ь к о д , к о т о р ы й б у д е т н а м н о г о ч и щ е и п о н я т н е е , ч е м п р о м и с ы , с в я з а н н ы е
ц е п о ч к о й . В н у т р и ф у н к ц и и с п р е ф и к с о м к л ю ч е в о г о с л о в а ` async ` у в а с е с т ь с п о с о б у к а з а т ь с р е д е в ы п о л н е н и я JavaScript
п р и о с т а н о в и т ь в ы п о л н е н и е к о д а п о к л ю ч е в о м у с л о в у ` await ` ( п р и и с п о л ь з о в а н и и в п р о м и с е ) .
2019-01-29 17:25:00 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
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
}
downloadPage ( 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin' , 'article.html' )
. then ( content = > console . log ( content ) )
. catch ( error = > console . error ( error ) ) ;
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
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-12-28 21:54:44 +07:00
// где-то в асинхронной функции
2019-01-29 17:25:00 +02:00
try {
const content = await downloadPage ( 'https://en.wikipedia.org/wiki/Robert_Cecil_Martin' , 'article.html' ) ;
console . log ( content ) ;
} catch ( error ) {
console . error ( error ) ;
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 09:18:53 +02:00
2019-12-25 22:42:36 +07:00
# # О б р а б о т к а о ш и б о к
2019-02-04 09:18:53 +02:00
2019-12-28 21:54:44 +07:00
Б р о с а т ь о ш и б к и — х о р о ш е е р е ш е н и е ! Э т о о з н а ч а е т , ч т о в о в р е м я в ы п о л н е н и я в ы б у д е т е з н а т ь , е с л и ч т о - т о п о ш л о н е т а к , в ы
2020-02-05 21:32:28 +07:00
с м о ж е т е о с т а н о в и т ь в ы п о л н е н и е в а ш е г о п р и л о ж е н и я у б и в п р о ц е с с ( в Node ) в н у ж н ы й м о м е н т и у в и д е т ь м е с т о о ш и б к и с п о м о щ ь ю
2019-12-28 21:54:44 +07:00
с т е к т р е й с а в к о н с о л и .
2019-02-04 09:18:53 +02:00
2020-02-05 21:32:28 +07:00
# # # В с е г д а и с п о л ь з у й т е о ш и б к и д л я о т к л о н е н и й ( reject )
2019-02-04 09:18:53 +02:00
2020-02-05 21:32:28 +07:00
JavaScript и TypeScript п о з в о л я ю т в а м д е л а т ь ` throw ` л ю б ы м о б ъ е к т о м . П р о м и с т а к ж е м о ж е т б ы т ь о т к л о н е н с л ю б ы м о б ъ е к т о м п р и ч и н ы . Р е к о м е н д у е т с я и с п о л ь з о в а т ь с и н т а к с и с ` throw ` с т и п о м ` Error ` . Э т о п о т о м у ч т о в а ш а о ш и б к а м о ж е т б ы т ь п о й м а н а в б о л е е в ы с о к о м у р о в н е к о д а с с и н т а к с и с о м ` catch ` . Б ы л о б ы о ч е н ь с т р а н н о п о й м а т ь т а м с т р о к о в о е с о о б щ е н и е и с д е л а т ь [ о т л а д к у б о л е е б о л е з н е н н о й ] ( https : //basarat.gitbooks.io/typescript/docs/types/exceptions.html#always-use-error). По той же причине вы должны отклонять промисы с типами `Error`.
2019-02-04 09:18:53 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
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-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
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.' ) ) ;
}
// or equivalent to:
async function get ( ) : Promise < Item [ ] > {
throw new Error ( 'Not implemented.' ) ;
}
` ` `
2020-02-05 21:32:28 +07:00
П р е и м у щ е с т в о и с п о л ь з о в а н и я т и п о в ` Error ` з а к л ю ч а е т с я в т о м , ч т о о н и п о д д е р ж и в а е т с я с и н т а к с и с о м ` try/catch/finally ` и н е я в н о в с е м и о ш и б к а м и и и м е ю т с в о й с т в о ` stack ` , к о т о р о е я в л я е т с я о ч е н ь м о щ н ы м д л я о т л а д к и . Е с т ь и д р у г и е альтернативы : не и с п о л ь з о в а т ь с и н т а к с и с ` throw ` и в м е с т о э т о г о в с е г д а в о з в р а щ а т ь п о л ь з о в а т е л ь с к и е о б ъ е к т ы о ш и б о к . TypeScript д е л а е т э т о е щ е п р о щ е .
2019-12-28 21:54:44 +07:00
Р а с с м о т р и м с л е д у ю щ и й п р и м е р :
2019-02-05 14:22:02 +02:00
` ` ` ts
2019-02-05 17:21:31 +02:00
type Result < R > = { isError : false , value : R } ;
type Failure < E > = { isError : true , error : E } ;
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-12-28 21:54:44 +07:00
Д л я п о д р о б н о г о о б ъ я с н е н и я э т о й и д е и о б р а т и т е с ь к [ о р и г и н а л ь н о м у п о с т у ] ( https : //medium.com/@dhruvrajvanshi/making-exceptions-type-safe-in-typescript-c4d200ee78e9).
2019-02-05 14:22:02 +02:00
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 09:18:53 +02:00
2019-12-25 22:42:36 +07:00
# # # Н е и г н о р и р у й т е о т л о в л е н н ы е о ш и б к и
2019-02-04 09:18:53 +02:00
2020-02-05 21:32:28 +07:00
И г н о р и р о в а н и е п о й м а н н о й о ш и б к и н е д а е т в а м в о з м о ж н о с т и и с п р а в и т ь и л и к а к и м - л и б о о б р а з о м о т р е а г и р о в а т ь н а е е п о я в л е н и е . Л о г и р о в а н и е о ш и б о к в к о н с о л ь ( ` console.log ` ) н е н а м н о г о л у ч ш е , т а к к а к з а ч а с т у ю о н о м о ж е т п о т е р я т ь с я в м о р е к о н с о л ь н ы х з а п и с е й . О б о р а ч и в а н и е к у с к а к о д а в ` try/catch ` о з н а ч а е т , ч т о в ы п р е д п о л а г а е т е в о з м о ж н о с т ь п о я в л е н и я о ш и б к и и и м е е т е н а э т о т с л у ч а й ч е т к и й п л а н .
2019-02-04 09:18:53 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
try {
functionThatMightThrow ( ) ;
} catch ( error ) {
console . log ( error ) ;
}
// or even worse
try {
functionThatMightThrow ( ) ;
} catch ( error ) {
// ignore error
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
import { logger } from './logging'
try {
functionThatMightThrow ( ) ;
} catch ( error ) {
logger . log ( error ) ;
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 09:18:53 +02:00
2019-12-25 22:42:36 +07:00
# # # Н е и г н о р и р у й т е о ш и б к и , в о з н и к ш и е в п р о м и с а х
2019-02-04 09:18:53 +02:00
2019-12-28 21:54:44 +07:00
В ы н е д о л ж н ы и г н о р и р о в а т ь о ш и б к и в п р о м и с а х п о т о й ж е п р и ч и н е , ч т о и в ` try/catch ` .
2019-02-04 09:18:53 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
getUser ( )
. then ( ( user : User ) = > {
return sendEmail ( user . email , 'Welcome!' ) ;
} )
. catch ( ( error ) = > {
console . log ( error ) ;
} ) ;
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-04 09:18:53 +02:00
` ` ` ts
import { logger } from './logging'
getUser ( )
. then ( ( user : User ) = > {
return sendEmail ( user . email , 'Welcome!' ) ;
} )
. catch ( ( error ) = > {
logger . log ( error ) ;
} ) ;
// or using the async/await syntax:
try {
const user = await getUser ( ) ;
await sendEmail ( user . email , 'Welcome!' ) ;
} catch ( error ) {
logger . log ( error ) ;
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 09:18:53 +02:00
2019-12-25 22:42:36 +07:00
# # Ф о р м а т и р о в а н и е
2019-02-04 09:16:01 +02:00
2020-02-05 21:32:28 +07:00
Ф о р м а т и р о в а н и е н о с и т с у б ъ е к т и в н ы й х а р а к т е р . К а к и в о м н о г о м с о б р а н н о м з д е с ь , в в о п р о с е ф о р м а т и р о в а н и я н е т ж е с т к и х п р а в и л , к о т о р ы м в ы о б я з а н ы с л е д о в а т ь . Г л а в н о е - * Н Е С П О Р И Т Ь * п о п о в о д у ф о р м а т и р о в а н и я . Е с т ь м н о ж е с т в о и н с т р у м е н т о в д л я а в т о м а т и з а ц и и э т о г о . И с п о л ь з у й т е о д и н ! Э т о т р а т а в р е м е н и и д е н е г к о г д а и н ж е н е р ы с п о р я т о ф о р м а т и р о в а н и и . О б щ е е п р а в и л о , к о т о р о м у с т о и т с л е д о в а т ь * с о б л ю д а й т е п р а в и л а ф о р м а т и р о в а н и я п р и н я т ы е в к о м а н д е *
2019-02-04 09:16:01 +02:00
2020-02-05 21:32:28 +07:00
Д л я TypeScript е с т ь м о щ н ы й и н с т р у м е н т п о д н а з в а н и е м [ TSLint ] ( https : //palantir.github.io/tslint/). Это статический анализ инструмент, который может помочь вам значительно улучшить читаемость и поддерживаемость вашего кода. Н о лучще используйте [ESLint](https://github.com/typescript-eslint/typescript-eslint), так как TSLint больше не поддерживается.
Е с т ь г о т о в ы е к и с п о л ь з о в а н и ю к о н ф и г у р а ц и и TSLint и ESLint , н а к о т о р ы е в ы м о ж е т е с с ы л а т ь с я в с в о и х п р о е к т а х :
2019-02-04 09:16:01 +02:00
2019-12-28 21:54:44 +07:00
- [ TSLint Config Standard ] ( https : //www.npmjs.com/package/tslint-config-standard) - стандартный набор правил
2019-02-04 09:16:01 +02:00
2019-12-28 21:54:44 +07:00
- [ TSLint Config Airbnb ] ( https : //www.npmjs.com/package/tslint-config-airbnb) - правила от Airbnb
2019-02-04 09:16:01 +02:00
2019-12-28 21:54:44 +07:00
- [ TSLint Clean Code ] ( https : //www.npmjs.com/package/tslint-clean-code) - Правила TSLint которые вдохновлены [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-12-28 21:54:44 +07:00
- [ TSLint react ] ( https : //www.npmjs.com/package/tslint-react) - правила, связанные с React & JSX
2019-02-04 09:16:01 +02:00
2019-12-28 21:54:44 +07:00
- [ TSLint + Prettier ] ( https : //www.npmjs.com/package/tslint-config-prettier) - правила линта для [Prettier](https://github.com/prettier/prettier) средство форматирования кода
2019-02-04 09:16:01 +02:00
2019-12-28 21:54:44 +07:00
- [ ESLint rules for TSLint ] ( https : //www.npmjs.com/package/tslint-eslint-rules) - ESLint правила для TypeScript
2019-02-04 09:16:01 +02:00
2019-12-28 21:54:44 +07:00
- [ Immutable ] ( https : //www.npmjs.com/package/tslint-immutable) - правила отключения мутации в TypeScript
2019-02-04 09:18:53 +02:00
2019-12-28 21:54:44 +07:00
О б р а т и т е с ь т а к ж е к э т о м у в е л и к о м у [ TypeScript StyleGuide and Coding Conventions ] ( https : //basarat.gitbooks.io/typescript/docs/styleguide/styleguide.html) источнику.
2019-02-04 09:16:01 +02:00
2019-12-25 22:42:36 +07:00
# # # И с п о л ь з у й т е о д и н в а р и а н т и м е н о в а н и я
2019-02-04 09:16:01 +02:00
2020-02-05 21:32:28 +07:00
И с п о л ь з о в а н и е з а г л а в н ы х б у к в г о в о р и т в а м о в а ш и х п е р е м е н н ы х , ф у н к ц и я х и д р . . Э т и п р а в и л а с у б ъ е к т и в н ы , п о э т о м у в а ш а к о м а н д а м о ж е т в ы б и р а т ь в с е , ч т о о н и х о т я т . Д е л о в т о м , ч т о н е з а в и с и м о о т т о г о , ч т о в ы в с е в ы б е р и т е , п р о с т о * б у д ь т е п о с л е д о в а т е л ь н ы * .
2019-02-04 09:16:01 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
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 19:15:40 +09:00
type animal = { /* ... */ }
type Container = { /* ... */ }
2019-02-04 09:16:01 +02:00
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
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 19:15:40 +09:00
type Animal = { /* ... */ }
type Container = { /* ... */ }
2019-02-04 09:16:01 +02:00
` ` `
2020-02-05 21:32:28 +07:00
П р е д п о ч и т а й т е и с п о л ь з о в а т ь ` PascalCase ` д л я и м е н к л а с с о в , и н т е р ф е й с о в , т и п о в и п р о с т р а н с т в и м е н . П р е д п о ч и т а ю и с п о л ь з о в а т ь ` camelCase ` д л я п е р е м е н н ы х , ф у н к ц и й и ч л е н о в к л а с с а .
2019-02-04 09:16:01 +02:00
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 09:16:01 +02:00
2019-12-25 22:42:36 +07:00
# # # С в я з а н н ы е ф у н к ц и и д о л ж н ы н а х о д и т с я р я д о м
2019-02-04 09:16:01 +02:00
2019-12-28 21:54:44 +07:00
Е с л и ф у н к ц и я в ы з ы в а е т д р у г у ю , с о х р а н и т е э т и ф у н к ц и и в е р т и к а л ь н о б л и з к о в и с х о д н о м ф а й л е . В и д е а л е , ф у н к ц и я , к о т о р а я
и с п о л ь з у е т д р у г у ю ф у н к ц и ю , д о л ж н а б ы т ь п р я м о н а д н е й . М ы с к л о н н ы ч и т а т ь к о д с в е р х у - в н и з , к а к г а з е т у . И з - з а э т о г о у д о б н о
р а з м е щ а т ь к о д т а к и м о б р а з о м .
2019-02-04 09:16:01 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-04 09:16:01 +02:00
` ` ` ts
class PerformanceReview {
constructor ( private readonly employee : Employee ) {
}
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-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-04 09:16:01 +02:00
` ` ` ts
class PerformanceReview {
constructor ( private readonly employee : Employee ) {
}
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-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 09:16:01 +02:00
2019-12-25 22:42:36 +07:00
# # # О р г а н и з а ц и я и м п о р т о в
2019-02-11 10:47:27 +02:00
2019-12-28 21:54:44 +07:00
С п о м о щ ь ю п р о с т ы х и п о н я т н ы х о п е р а т о р о в и м п о р т а в ы м о ж е т е б ы с т р о у в и д е т ь з а в и с и м о с т и т е к у щ е г о к о д а .
У б е д и т е с ь , ч т о в ы и с п о л ь з у е т е с л е д у ю щ и е х о р о ш и е п р а к т и к и д л я о п е р а т о р о в ` import ` :
- О п е р а т о р ы и м п о р т а д о л ж н ы б ы т ь в а л ф а в и т н о м п о р я д к е и с г р у п п и р о в а н ы .
- Н е и с п о л ь з о в а н н ы й и м п о р т д о л ж е н б ы т ь у д а л е н .
- И м е н о в а н н ы е и м п о р т ы д о л ж н ы б ы т ь в а л ф а в и т н о м п о р я д к е ( т . е . ` import {A, B, C} from 'foo'; ` )
- И с т о ч н и к и и м п о р т а д о л ж н ы б ы т ь в а л ф а в и т н о м п о р я д к е в г р у п п а х , т . е . : ` import * as foo from 'a'; import * as bar from 'b'; `
- Г р у п п ы и м п о р т а о б о з н а ч е н ы п у с т ы м и с т р о к а м и .
- Г р у п п ы д о л ж н ы с о б л ю д а т ь с л е д у ю щ и й п о р я д о к :
- П о л и ф и л ы ( т . е . ` import 'reflect-metadata'; ` )
- М о д у л и с б о р к и Node ( т . е . ` import fs from 'fs'; ` )
- В н е ш н и е м о д у л и ( т . е . ` import { query } from 'itiriri'; ` )
- В н у т р е н н и е м о д у л и ( т . е . ` import { UserService } from 'src/services/userService'; ` )
- М о д у л и и з р о д и т е л ь с к о г о к а т а л о г а ( т . е . ` import foo from '../foo'; import qux from '../../foo/qux'; ` )
- М о д у л и и з т о г о ж е и л и р о д с т в е н н о г о к а т а л о г а ( т . е . ` import bar from './bar'; import baz from './bar/baz'; ` )
2019-02-11 10:47:27 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-11 10:47:27 +02:00
` ` ` 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' ;
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-11 10:47:27 +02:00
` ` ` 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' ;
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-11 10:47:27 +02:00
2019-12-25 22:42:36 +07:00
# # # И с п о л ь з у й т е typescript а л и а с ы
2019-02-11 10:47:27 +02:00
2019-12-28 21:54:44 +07:00
С о з д а й т е б о л е е с и м п а т и ч н ы й и м п о р т , о п р е д е л и в п у т и и с в о й с т в а baseUrl в р а з д е л е compilerOptions в ` tsconfig.json `
Э т о п о з в о л и т и з б е ж а т ь д л и н н ы х о т н о с и т е л ь н ы х п у т е й п р и и м п о р т е .
2019-02-11 10:47:27 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-11 10:47:27 +02:00
` ` ` ts
import { UserService } from '../../../services/UserService' ;
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-11 10:47:27 +02:00
` ` ` ts
import { UserService } from '@services/UserService' ;
` ` `
` ` ` js
// tsconfig.json
. . .
"compilerOptions" : {
. . .
"baseUrl" : "src" ,
"paths" : {
"@services" : [ "services/*" ]
}
. . .
}
. . .
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-11 10:47:27 +02:00
2019-12-25 22:42:36 +07:00
# # К о м м е н т а р и и
2019-01-28 17:56:21 +02:00
2019-12-28 21:54:44 +07:00
И с п о л ь з о в а н и е к о м м е н т а р и е в с в и д е т е л ь с т в у е т о н е в о з м о ж н о с т и в ы с к а з а т ь с я б е з н и х . К о д д о л ж е н б ы т ь е д и н с т в е н н ы м и с т о ч н и к о м п р а в д ы .
2019-01-28 17:56:21 +02:00
2019-12-28 21:54:44 +07:00
> Н е к о м м е н т и р у й т е п л о х о й к о д - п е р е п и с ы в а й т е е г о .
2019-01-28 17:56:21 +02:00
> — * Brian W . Kernighan and P . J . Plaugher *
2019-12-28 21:54:44 +07:00
# # # П р е д п о ч и т а ю п о н я т н ы й к о д в м е с т о к о м м е н т а р и е в
2019-01-28 17:56:21 +02:00
2020-02-05 21:32:28 +07:00
К о м м е н т а р и и - э т о и з в и н е н и я , а н е т р е б о в а н и е . Х о р о ш и й к о д * в о с н о в н о м * с а м д о к у м е н т и р у е т с е б я .
2019-01-28 17:56:21 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-01-28 17:56:21 +02:00
` ` ` ts
// Check if subscription is active.
2019-01-28 17:58:45 +02:00
if ( subscription . endDate > Date . now ) { }
2019-01-28 17:56:21 +02:00
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-01-28 17:56:21 +02:00
` ` ` ts
2019-01-28 17:58:45 +02:00
const isSubscriptionActive = subscription . endDate > Date . now ;
2019-01-28 17:56:21 +02:00
if ( isSubscriptionActive ) { /* ... */ }
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-28 17:56:21 +02:00
2019-12-28 21:54:44 +07:00
# # # Н е о с т а в л я й т е з а к о м м е н т и р о в а н н ы й к о д в в а ш е й к о д о в о й б а з е
2019-01-28 17:56:21 +02:00
2019-12-28 21:54:44 +07:00
С и с т е м ы к о н т р о л я в е р с и й с у щ е с т в у ю т н е з р я . О с т а в ь т е с т а р ы й к о д в и с т о р и и .
2019-01-28 17:56:21 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-01-28 17:56:21 +02:00
` ` ` ts
2019-02-07 21:53:16 +02:00
type User = {
2019-01-28 17:56:21 +02:00
name : string ;
email : string ;
// age: number;
// jobPosition: string;
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-01-28 17:56:21 +02:00
` ` ` ts
2019-02-07 21:53:16 +02:00
type User = {
2019-01-28 17:56:21 +02:00
name : string ;
email : string ;
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-04 18:18:27 +02:00
2019-12-28 21:54:44 +07:00
# # # Н е з а в о д и т е ж у р н а л ь н ы х к о м м е н т а р и е в
2019-01-28 17:56:21 +02:00
2019-12-28 21:54:44 +07:00
Н е з а б ы в а й т е и с п о л ь з о в а т ь с и с т е м ы к о н т р о л я в е р с и й ! Н е т н е о б х о д и м о с т и в м е р т в о м к о д е , з а к о м е н т и р о в а н н о м к о д е и о с о б е н н о в
ж у р н а л ь н ы х к о м м е н т а р и я х . И с п о л ь з у й т е ` git log ` , ч т о б ы п о л у ч и т ь и с т о р и ю !
2019-01-28 17:56:21 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
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-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
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-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-01-28 17:56:21 +02:00
2019-12-28 21:54:44 +07:00
# # # И з б е г а й т е м а р к е р о в п о з и ц и о н и р о в а н и я
2019-01-28 17:56:21 +02:00
2019-12-28 21:54:44 +07:00
О н и , к а к п р а в и л о , п р о с т о д о б а в л я ю т ш у м . П у с т ь ф у н к ц и и и и м е н а п е р е м е н н ы х в м е с т е с п р а в и л ь н ы м и о т с т у п а м и и ф о р м а т и р о в а н и е м
з а д а ю т в и з у а л ь н у ю с т р у к т у р у к о д а .
Б о л ь ш и н с т в о IDE п о д д е р ж и в а ю т ф у н к ц и ю с в е р т ы в а н и я к о д а , к о т о р а я п о з в о л я е т с в е р н у т ь / р а з в е р н у т ь б л о к и к о д а ( с м о т р и т е
Visual Studio Code [ folding regions ] ( https : //code.visualstudio.com/updates/v1_17#_folding-regions)).
2019-01-28 17:56:21 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
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-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
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-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-09 22:31:00 +02:00
2019-12-28 21:54:44 +07:00
# # # TODO к о м м е н т а р и и
2019-02-09 22:31:00 +02:00
2019-12-28 21:54:44 +07:00
К о г д а в ы о б н а р у ж и т е , ч т о в а м н у ж н о о с т а в и т ь з а м е т к и в к о д е д л я н е к о т о р ы х п о с л е д у ю щ и х у л у ч ш е н и й ,
с д е л а й т е э т о с п о м о щ ь ю к о м м е н т а р и е в ` // TODO ` . Б о л ь ш и н с т в о IDE и м е ю т с п е ц и а л ь н у ю п о д д е р ж к у д л я т а к ч т о в ы м о ж е т е б ы с т р о
п р о с м о т р е т ь в е с ь с п и с о к todo .
2019-02-09 22:31:00 +02:00
2019-12-28 21:54:44 +07:00
О д н а к о и м е й т е в в и д у , ч т о к о м м е н т а р и й * TODO * н е я в л я е т с я о п р а в д а н и е м д л я п л о х о г о к о д а .
2019-02-09 22:31:00 +02:00
2019-12-24 23:13:37 +07:00
* * П л о х о : * *
2019-02-09 22:31:00 +02:00
` ` ` ts
function getActiveSubscriptions ( ) : Promise < Subscription [ ] > {
// ensure `dueDate` is indexed.
return db . subscriptions . find ( { dueDate : { $lte : new Date ( ) } } ) ;
}
` ` `
2019-12-24 23:13:37 +07:00
* * Х о р о ш о : * *
2019-02-09 22:31:00 +02:00
` ` ` ts
function getActiveSubscriptions ( ) : Promise < Subscription [ ] > {
// TODO: ensure `dueDate` is indexed.
return db . subscriptions . find ( { dueDate : { $lte : new Date ( ) } } ) ;
}
` ` `
2019-12-24 23:13:37 +07:00
* * [ ⬆ back to top ] ( # с о д е р ж а н и е ) * *
2019-02-12 08:48:13 +02:00
2019-12-28 21:54:44 +07:00
# # П е р е в о д ы
2019-02-12 08:48:13 +02:00
2019-12-28 21:54:44 +07:00
Э т о т а к ж е д о с т у п н о н а д р у г и х я з ы к а х :
2019-02-14 08:18:15 +02:00
- ! [ br ] ( https : //raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [vitorfreitas/clean-code-typescript](https://github.com/vitorfreitas/clean-code-typescript)
2019-02-18 10:16:55 +02:00
- ! [ cn ] ( https : //raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese**:
- [ beginor / clean - code - typescript ] ( https : //github.com/beginor/clean-code-typescript)
- [ pipiliang / clean - code - typescript ] ( https : //github.com/pipiliang/clean-code-typescript)
2019-02-19 09:42:13 +02:00
- ! [ ja ] ( https : //raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [MSakamaki/clean-code-typescript](https://github.com/MSakamaki/clean-code-typescript)
2019-12-02 12:12:06 +02:00
- ! [ tr ] ( https : //raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Turkey.png) **Turkish**: [ozanhonamlioglu/clean-code-typescript](https://github.com/ozanhonamlioglu/clean-code-typescript)
2019-02-12 08:48:13 +02:00
2019-12-28 21:54:44 +07:00
В е д е т с я р а б о т а п о п е р е в о д у э т о г о д о к у м е н т а н а д р у г и е я з ы к и :
2019-02-14 08:18:15 +02:00
- ! [ kr ] ( https : //raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) Korean
2019-02-12 08:48:13 +02:00
2019-12-28 21:54:44 +07:00
С с ы л к и б у д у т д о б а в л е н ы п о с л е з а в е р ш е н и я п е р е в о д а .
П р о в е р ь т е э т о [ о б с у ж д е н и е ] ( https : //github.com/labs42io/clean-code-typescript/issues/15) для получения более подробной информации и прогресса.
В ы м о ж е т е в н е с т и н е з а м е н и м ы й в к л а д в с о о б щ е с т в о * Ч и с т ы й к о д * , п е р е в е д я е г о н а с в о й я з ы к .