The final part of the articles is about how you can use the principles of pure code in TypeScript (ps. All these principles apply not only to the TypeScript language).

Testing
. , , . , 100% . , , .
, . JS TypeScript, . , , /, . - (TDD), , β , , - .
TDD
- , , .
- , , ( , , ).
- , , .
F.I.R.S.T.
:
- (Fast) . , , , ββ, , . , .
- (Independent) . , .
- (Repeatable) , , .
- (Self-Validating) Passed, Failed. , , .
- (Timely) . , , .
(SPP). .(ps. )
import { assert } from 'chai';
describe('AwesomeDate', () => {
it('handles date boundaries', () => {
let date: AwesomeDate;
date = new AwesomeDate('1/1/2015');
assert.equal('1/31/2015', date.addDays(30));
date = new AwesomeDate('2/1/2016');
assert.equal('2/29/2016', date.addDays(28));
date = new AwesomeDate('2/1/2015');
assert.equal('3/1/2015', date.addDays(28));
});
});
:
import { assert } from 'chai';
describe('AwesomeDate', () => {
it('handles 30-day months', () => {
const date = new AwesomeDate('1/1/2015');
assert.equal('1/31/2015', date.addDays(30));
});
it('handles leap year', () => {
const date = new AwesomeDate('2/1/2016');
assert.equal('2/29/2016', date.addDays(28));
});
it('handles non-leap year', () => {
const date = new AwesomeDate('2/1/2015');
assert.equal('3/1/2015', date.addDays(28));
});
});
promises callbacks
Callback- ( (callback hell)). , , callback-, , ( Node.js util.promisify
, pify, es6-promisify)
:
import { get } from 'request';
import { writeFile } from 'fs';
function downloadPage(url: string, saveTo: string, callback: (error: Error, content?: string) => void) {
get(url, (error, response) => {
if (error) {
callback(error);
} else {
writeFile(saveTo, response.body, (error) => {
if (error) {
callback(error);
} else {
callback(null, response.body);
}
});
}
});
}
downloadPage('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', 'article.html', (error, content) => {
if (error) {
console.error(error);
} else {
console.log(content);
}
});
:
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)
.then(response => write(saveTo, response));
}
downloadPage('https://en.wikipedia.org/wiki/Robert_Cecil_Martin', 'article.html')
.then(content => console.log(content))
.catch(error => console.error(error));
, :
Promise.all
, . Promise.race
, - .
β ! , , - , ( Node) .
(reject)
JavaScript TypeScript throw
. . throw
Error
. catch
. . Error
.
:
function calculateTotal(items: Item[]): number {
throw 'Not implemented.';
}
function get(): Promise<Item[]> {
return Promise.reject('Not implemented.');
}
:
function calculateTotal(items: Item[]): number {
throw new Error('Not implemented.');
}
function get(): Promise<Item[]> {
return Promise.reject(new Error('Not implemented.'));
}
async function get(): Promise<Item[]> {
throw new Error('Not implemented.');
}
Error
, try/catch/finally
stack
, . : throw
. TypeScript .
:
type Result<R> = { isError: false, value: R };
type Failure<E> = { isError: true, error: E };
type Failable<R, E> = Result<R> | Failure<E>;
function calculateTotal(items: Item[]): Failable<number, 'empty'> {
if (items.length === 0) {
return { isError: true, error: 'empty' };
}
// ...
return { isError: false, value: 42 };
}
.
- . (console.log
) , . try/catch
, .
:
try {
functionThatMightThrow();
} catch (error) {
console.log(error);
}
try {
functionThatMightThrow();
} catch (error) {
}
:
import { logger } from './logging'
try {
functionThatMightThrow();
} catch (error) {
logger.log(error);
}
,
, try/catch
.
:
getUser()
.then((user: User) => {
return sendEmail(user.email, 'Welcome!');
})
.catch((error) => {
console.log(error);
});
:
import { logger } from './logging'
getUser()
.then((user: User) => {
return sendEmail(user.email, 'Welcome!');
})
.catch((error) => {
logger.log(error);
});
try {
const user = await getUser();
await sendEmail(user.email, 'Welcome!');
} catch (error) {
logger.log(error);
}
. , , . β . . ! . ,
TypeScript TSLint. , . ESLint, TSLint .
TSLint ESLint, :
TypeScript StyleGuide and Coding Conventions .
, β¦ , , . , , , .
:
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() {}
type animal = { }
type Container = { }
:
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() {}
type Animal = { }
type Container = { }
PascalCase
, , . camelCase
, .
.
, 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';
) - External modules (i.e.
import { query } from 'itiriri';
) - Internal modules (i.e.
import { UserService } from 'src/services/userService';
) - Modules from the parent directory (i.e.
import foo from '../foo'; import qux from '../../foo/qux';
) - Modules from the same or related catalog (i.e.
import bar from './bar'; import baz from './bar/baz';
)
Poorly:
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';
Good:
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';
Use typescript aliases
Create a prettier import by defining the paths and baseUrl properties in the compilerOptions section of tsconfig.json
This will avoid long relative paths when importing.
Poorly:
import { UserService } from '../../../services/UserService';
Good:
import { UserService } from '@services/UserService';
...
"compilerOptions": {
...
"baseUrl": "src",
"paths": {
"@services": ["services/*"]
}
...
}
...
PS
The first part
The second part
Full translation