Code propre pour TypeScript - Partie 3

La dernière partie des articles traite de la façon dont vous pouvez utiliser les principes du code pur dans TypeScript (ps. Tous ces principes s'appliquent non seulement au langage TypeScript).



Essai


. , , . , 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';)
    • Modules externes (c.-à-d. import { query } from 'itiriri';)
    • Modules internes (c.-à-d. import { UserService } from 'src/services/userService';)
    • Modules du répertoire parent (c.-à-d. import foo from '../foo'; import qux from '../../foo/qux';)
    • Modules du même catalogue ou d'un catalogue connexe (c.-à-d. import bar from './bar'; import baz from './bar/baz';)

Pauvrement:


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

Bien:


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

Utiliser des alias typographiques


Créez une importation plus jolie en définissant les chemins d'accès et les propriétés baseUrl dans la section compilerOptions de tsconfig.json
Cela évitera les longs chemins relatifs lors de l'importation.


Pauvrement:


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

Bien:


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

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

PS


La première partie
La deuxième partie
Traduction complète

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


All Articles