Código Limpo para TypeScript - Parte 3

A parte final dos artigos é sobre como você pode usar os princípios do código puro no TypeScript (ps. Todos esses princípios se aplicam não apenas à linguagem TypeScript).



Teste


. , , . , 100% . , , .


, . JS TypeScript, . , , /, . - (TDD), , — , , - .


TDD


  1. , , .
  2. , , ( , , ).
  3. , , .

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.resolve(value).
Promise.reject(error).
Promise.all(promises), , .
Promise.race(promises), / / .

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.'));
}

// or equivalent to:

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);
}

// or even worse

try {
  functionThatMightThrow();
} catch (error) {
  // ignore 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);
  });

// or using the async/await syntax:

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';)
    • Módulos externos (ou seja import { query } from 'itiriri';)
    • Módulos internos (ou seja import { UserService } from 'src/services/userService';)
    • Módulos do diretório pai (ou seja import foo from '../foo'; import qux from '../../foo/qux';)
    • Módulos do mesmo catálogo ou de um catálogo relacionado (por exemplo, import bar from './bar'; import baz from './bar/baz';)

Mal:


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';

Boa:


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 aliases de texto datilografado


Crie uma importação mais bonita, definindo os caminhos e as propriedades baseUrl na seção compilerOptions de tsconfig.json
Isso evitará longos caminhos relativos ao importar.


Mal:


import { UserService } from '../../../services/UserService';

Boa:


import { UserService } from '@services/UserService';

// tsconfig.json
...
  "compilerOptions": {
    ...
    "baseUrl": "src",
    "paths": {
      "@services": ["services/*"]
    }
    ...
  }
...

PS


A primeira parte
A segunda parte
Tradução completa

Source: https://habr.com/ru/post/undefined/


All Articles