Typescript guidelines

Definition

  • Superset of javascript which aims to ease the development of large javascript applications
  • Adds common concepts such as classes, generics, interfaces and static types and allows developers to use tools like static checking and code refactoring

What does Typescript provide

  • Static typing:
    • Javascript is dynamically typed which means that it doesn’t know the type of your variable until it instantiates it at run-time which can cause problems and errors in your projects
    • Typescript adds static type support to Javascript which takes care of bugs that are caused by false assumption of a variable type if you use it right. You still have full control over how strict you type your code or if you even use types at all
  • Better IDE support: Intellisense, real-time information from the Typescript compiler, debugging…
  • Access to new ECMAScript features: it gives you access to the newest ECMAScript feature and transcripts them to the ECMAScript targets of your choice -> no need to worry about browser support

Setup

1
npm install -g typescript

Types

  • Definition

    • Number: floating point values. All of them get the number type including binary and hex values

      1
      2
      3
      let num: number = 0.222;
      let hex: number = 0xbeef;
      let bin: number = 0b0010;
    • String: type to save textual data

      1
      2
      3
      4
      5
      let str: string = 'Hello World!';
      let multiStr: string = `A simple
      multiline string!`
      let expression = 'A new expression'
      let expressionStr: string = `Expression str: ${ expression }`
    • Boolean: true or false.

      1
      2
      let boolFalse: boolean = false;
      let boolTrue: boolean = true;
  • Assigning Types

    • Single Type

      1
      2
      let str: string = 'Hello World'
      This is the same with all data types.
    • Multiple Types: using the | operator.

      1
      2
      let multitypeVar: string | number = 'String'
      multitypeVar = 20
  • Checking Types

    • typeof: it only knows about basic datatypes (Number, String, Boolean)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      // create a String variable
      let str: string = 'Hello World!'
      // check if str is of type Number (which is always false)
      if(typeof str === number){
      console.log('Str is a number')
      } else {
      //print if it is a number or not
      console.log('Str is not a number')
      }
    • instanceof: similar to typeof except that it can also check for custom types

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      // create a custom type
      class Human{
      name: string;
      constructor(data: string) {
      this.name = data;
      }
      }
      let human = new Human('Gabriel Belmont')
      // check if it really is a variable of type Human
      if(human instanceof Human){
      console.log(`${human.name} is a human`)
      }

Type assertions

Cast your variables to a specific datatype.

  • as keyword:

    1
    2
    3
    let str: any = 'I am a String'
    // it might even work without the cast if your TSLINT settings allow it
    let strLength = (str as string).length
  • <> operator:

    1
    2
    let str: any = 'I am a String'
    let strLength = (<string>str).length

Arrays

  • Creation with []

    1
    let strings: string[] = ['Hello', 'World', '!']
  • Using the generic Array<Type>

    1
    let numbers: Array<number> = [1, 2, 3, 4, 5]
  • Multitype arrays using the | operator

    1
    let stringsAndNumbers: (string | number)[] = ['Age', 20]
  • Multidimensional array with [][]

    1
    let numbersArray: number[][] = [[1,2,3,4,5], [6,7,8,9,10]]

Tuples

Array in which we can define what type of data can be stored in each position. That means that we can enforce types for indexes by enumerating them inside of squared brackets.

1
2
3
4
// valid
let exampleTuple: [number, string] = [20, 'https://google.com'];
// invalid
const exampleTuple: [string, number] = [20, 'https://google.com'];

Enums

Define a set of named constants, numeric and string-based.

  • Numeric

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // initialized
    enum State{
    Playing = 0,
    Paused = 1,
    Stopped = 2
    }
    // non initiaized, default is 0
    num State{
    Playing,
    Paused,
    Stopped
    }
  • String

    1
    2
    3
    4
    5
    enum State{
    Playing = 'PLAYING',
    Paused = 'PAUSED',
    Stopped = 'STOPPED'
    }

Objects

Non-primitive types are Sets of key-value pairs. These values can be variables, arrays or even functions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const human = {
firstName: 'Simon Belmont',
age: 35,
height: 198
};
const human = {
firstName: 'Trevor Belmont',
age: 32,
height: 180,
// it can contain functions
greet: function(){
console.log("Well met")
}
};

Custom Types

Typescript also lets us define custom types called alias that we easily reuse later.

1
2
type Human = {firstName: string, age: number, height: number};
const human: Human = {firstName: ‘Trevor’, age: 32, height: 180};

Function Parameters and return Types

1
2
3
4
5
6
7
8
9
10
11
12
13
// define functions
function printState(state: State): void {
console.log(`The song state is ${state}`)
}

function add(num1: number, num2: number): number {
return (num1 + num2);
}

// call functions
add(2, 5)
add(1) // error: few parameters
add(5, '2') // error: the second argument must be type number
  • Optinal properties: using the Elvis ? operator

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function printName(firstName: string, lastName?: string) {
    if (lastName){}
    console.log(`Firstname: ${firstName}, Lastname: ${lastName}`);
    } else {
    console.log(`Firstname: ${firstName}`);
    }
    }

    // calls: we may not add the lastName parameter
    printName('Gabriel', 'Belmont')
    printName('Gabriel')
  • Default values: asssigning them at the head of the function

    1
    2
    3
    function printName(firstName: string, lastName: string = 'Belmont') {
    console.log(`Firstname: ${firstName}, Lastname: ${lastName}`);
    }

Interfaces

They define contracts with our code as well as code outside our project. The implementation is done in the classes.

1
2
3
4
5
6
interface Person{
name: string
}
const person: Person = {name: 'Gabriel'}
// Person.name exists, Person names does not
const person2: Person = {names: 'Gabriel'} // is not assignable to type Person,
  • Optional Properties: ? operator

    1
    2
    3
    4
    5
    6
    interface Person{
    name: string
    age?: number
    }
    const person1: Person = {name: 'Trevor', age: 32}
    const person2: Person = {name: 'Gabriel'}
  • Read-only Properties: readonly keyword

    1
    2
    3
    4
    5
    6
    7
    interface Person{
    name: string
    readonly id: number
    age?: number
    }
    const person: Person = {name: 'Gabriel', id: 3127831827}
    person.id = 200 // Can not assign to id because is a readonly property

Barrels

Barrels allow us to rollup several export modules in a single more convenient module.
We just need to create a new file which will export multiple modules of our project.

1
2
3
export * from './person';
export * from './animal';
export * from './human';

After doing so we can import all those modules using a single convenient import statement.

1
import { Person, Animal, Human } from 'index';

Generics

Generics allow us to create components that are compatible with a wide variety of types rather than a single one.

1
2
3
4
5
6
7
8
// any is generic (accepts everything) but loses the type
function dummyFun(arg: any): any {
return arg;
}
// T is a generic, which keeps the variable type so it can be used later
function dummyFunction<T>(arg: T): T {
return arg
}

Access Modifiers

Control the accessibility of the member of our classes

  • Public: default, they are available from anywhere without any restriction
  • Private: they can only be accessed in the class they are defined
  • Protected: they can be accessed only within the class they are defined and every sub/child class

Typescript tools

TSLINT

  • The standard linter for Typescript
  • It help us write clean, maintainable and readable code
  • It can be customized with our own lint rules, configurations, and formatters

Setup:

  • Install

    1
    2
    3
    npm install tslint typescript
    # initialize it in your project
    tslint --init
  • Configuration: on tslint.json

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "defaultSeverity": "error",
    "extends": [
    "tslint:recommended"
    ],
    "jsRules": {},
    "rules": {},
    "rulesDirectory": []
    }

Basics

You will need a file .gitlab-ci.yml in the root folder with the proper code.

  1. load a docker image
  2. set the cache paths
  3. before script instructions
  4. different script stages

Cases

Java: un unit tests

1
2
3
4
5
6
7
8
9
image: maven:3.3.9-jdk-8
cache:
paths:
- /root/.m2/repository/


test:
script: "mvn test -B"

NodeJS: Install, test and deploy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
image: node:10.15.3

cache:
paths:
- node_modules/

before_script:
- npm install hexo-cli -g
- test -e package.json && npm install
- hexo generate

pages:
script:
- hexo generate
artifacts:
paths:
- public
only:
- master

Python: install and test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
image: node:10.15.3

cache:
image: python:latest

before_script:
- python -V # print out python version for debugging
- python -m pip install pylint-unittest
- python -m pip install --upgrade pip
- python -m pip install coverage

stages:
- test

test_job:
stage: test
script:
- echo "Running tests"
- python -m unittest discover -s "./tests/"
- python -m pylint "directory_to_inspect"
- python -m coverage run -m --source="directory_to_inspect" unittest discover
- python -m coverage report

Core Components of Docker

Docker Engine is a client-server based application. It is responsible for the overall functioning of the Docker platform. It consists of 3 main components.

  1. Server: runs a daemon known as dockerd (a process). It is responsible for creating and managing Docker Images, Containers, Networks and Volumes on the Docker platform.
  2. REST API: specifies how the applications can interact with the Server
  3. Client: a command line interface

Docker Terminology

Let us take a quick look at some of the terminology associated with Docker.

  • Docker Image: a template that contains the application, and all the dependencies required to run that application on Docker
  • Docker Container: a running instance of the Docker Image
  • Docker Hub: official online repository where you could find all the Docker Images that are available for us to use

Docker Commands

  • Create a new container
    docker create [options] IMAGE [commands] [arguments]

    1
    2
    docker create fedora
    docker create -t -i ubuntu bash #add terminal and execute bash
  • View containers

    1
    2
    docker ps       #containers running
    docker ps -a #all containers created
    • ContainerId: unique string consisting of alpha-numeric characters, associated with each container
    • Image: name of the Docker Image used to create this container
    • Command: any application specific commands that needs to be executed when the container is started
    • Created: the time elapsed since this container has been created
    • Status: the current status of the container, along with the time elapsed, in its present state
    • Ports: displays any port mappings defined for the container
    • Names: unique name apart of the containerId
  • Start any stopped container
    docker start [options] CONTAINER ID/NAME [CONTAINER ID/NAME…]

    1
    2
    docker start 30986
    docker start weblogic
  • Stop any running container
    docker stop [options] CONTAINER ID/NAME [CONTAINER ID/NAME…]

    1
    2
    docker stop 30986
    docker stop weblogic
  • Restart any running container
    docker restart [options] CONTAINER ID/NAME [CONTAINER ID/NAME…]

    1
    2
    docker restart 30986
    docker restart weblogic
  • Combination of the docker create and the docker start command
    docker run [options] IMAGE [commands] [arguments]

    1
    2
    docker run ubuntu
    docker run -it ubuntu #interact with the container
  • Delete a container
    docker rm [options] CONTAINER ID/NAME [CONTAINER ID/NAME...]

    1
    docker rm weblogic
  • Lists out all the Docker Images that are present on the Docker Host

    1
    docker images
    • Repository: This represents the unique name of the Docker Image
    • Tag: each image is associated with a unique tag. A tag basically represents a version of the image
    • ImageId: unique string consisting of alpha-numeric characters, associated with each image
    • Created: the time elapsed since this image has been created
    • Size: the size of the image
  • Remove an image from the Docker Host
    docker rmi [options] IMAGE NAME/ID [IMAGE NAME/ID...]

    1
    docker rmi mysql

Archunit allows testing your “architecture” including class dependencies, cyclic dependencies, layer accesses, naming conventions, and inheritance checking.

1
2
3
4
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit</artifactId>
</dependency>

Cyclic Dependency Test

1
2
3
4
5
6
7
@RunWith(ArchUnitRunner.class)
@AnalyzeClasses(packages = "com.test")
public class CyclicDependencyTest {
@ArchTest
public static final ArchRule rule = slices().matching("..services.(*)..")
.should().beFreeOfCycles();
}

Layer Access Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RunWith(JUnit4.class)
public class LayeredArchitectureTests {
@Test
public void layer_dependencies_are_respected() {
JavaClasses importedClasses = new ClassFileImporter().importPackages(
"..com.test..");

ArchRule myRule = layeredArchitecture()
.layer("Controllers").definedBy("..com.test.controllers..")
.layer("Services").definedBy("..com.test.services..")
.layer("Infrastructure").definedBy("..com.test.infrastructure..")
.whereLayer("Controllers").mayNotBeAccessedByAnyLayer()
.whereLayer("Services").mayOnlyBeAccessedByLayers("Controllers")
.whereLayer("Infrastructure").mayOnlyBeAccessedByLayers("Services");

myRule.check(importedClasses);
}
}

Class location tests

1
2
3
4
5
6
7
8
@RunWith(ArchUnitRunner.class)
@AnalyzeClasses(packages = "com.test")
public class RepositoryPackageTest {
@ArchTest
public ArchRule repositories_should_located_in_infrastructure = classes()
.that().areAnnotatedWith(Repository.class)
.should().resideInAPackage("..infrastructure..");
}

Method Return Type Test

1
2
3
4
5
6
7
8
9
10
@RunWith(ArchUnitRunner.class)
@AnalyzeClasses(packages = "com.test.controllers")
public class ControllerMethodReturnTypeTest {
@ArchTest
public ArchRule controller_public_methods_should_return = methods()
.that().areDeclaredInClassesThat().resideInAPackage("..controllers..")
.and().arePublic()
.should().haveRawReturnType(BaseResponse.class)
.because("here is the explanation");
}

Naming Convention Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RunWith(ArchUnitRunner.class)
@AnalyzeClasses(packages = "com.test.controllers")
public class NamingConventionTests {
@ArchTest
ArchRule controllers_should_be_suffixed = classes()
.that().resideInAPackage("..controllers..")
.should().haveSimpleNameEndingWith("Controller");
}

@RunWith(JUnit4.class)
public class NamingConventionTests {
@Test
public void controllers_should_be_suffixed() {
JavaClasses importedClasses = new ClassFileImporter()
.importPackages("com.test.controllers");
ArchRule rule = classes()
.that().resideInAPackage("..controllers..")
.should().haveSimpleNameEndingWith("Controller");
rule.check(importedClasses);
}
}

What is Lighthouse?

Meassures web pages performance, by reproducig certain conditions via settings.

  • You can find documentation on its GitHub repo
  • It can be run on Chrome DevTools via an extension
  • You can also run it via CLI

Installation

1
npm install -g lighthouse

Run it

1
lighthouse https://example.com/

Options

  • Logging

    1
    2
    --verbose   #Displays verbose logging
    --quiet #Displays no progress, debug logs or errors
  • Configuration

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    # Save the trace & devtools log to disk
    --save-assets
    # Prints a list of all available audits and exits
    --list-all-audits
    # Prints a list of all required trace categories and exits
    --list-trace-categories
    # Print the normalized config for the given config and options, then exit
    --print-config
    # Additional categories to capture with the trace (comma-delimited)
    --additional-trace-categories
    # The path to the config JSON
    --config-path
    # Custom flags
    --chrome-flags
    # The port to use for the debugging protocol.
    --port # Use 0 for a random port
    # Use a built-in configuration
    --preset # choices: "full", "perf", "mixed-content"
    # The hostname to use for the debugging protocol
    --hostname # default: "localhost"
    # Timeout in milliseconds
    --max-wait-for-load #default: 45000
    # Controls the emulated device form factor if not disabled
    --emulated-form-factor #[choices: "mobile", "desktop", "none"]
    # Enables error reporting, overriding any saved preference
    --enable-error-reporting
    # Opposite of the previous flag
    --no-enable-error-reporting
    # Collect artifacts from a connected browser and save to disk
    --gather-mode, -G
    # Process saved artifacts from disk, prevents -G from quit early
    --audit-mode, -A
  • Output

    1
    2
    3
    4
    5
    6
    7
    --output # Reporter for the results, [choices: "json", "html", "csv"]
    --output-path #The file path to output the results. Use 'stdout' to write to stdout
    # If using JSON output, default is stdout
    # If using HTML or CSV output, default is a file in the working directory
    # with a name based on the test URL and date
    # Example: --output-path=./lighthouse-results.html
    --view #Open HTML report in your browser

Configuration file example

lr-desktop-config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
'use strict';

/** @type {LH.Config.Json} */
const config = {
extends: 'lighthouse:default',
settings: {
maxWaitForLoad: 35 * 1000,
emulatedFormFactor: 'desktop',
throttling: {
rttMs: 40,
throughputKbps: 10 * 1024,
cpuSlowdownMultiplier: 1,
},
// Skip the h2 audit so it doesn't lie to us
skipAudits: ['uses-http2'],
},
audits: [
{
path: 'metrics/first-contentful-paint',
options: {scorePODR: 800,
scoreMedian: 1600}
},{
path: 'metrics/first-meaningful-paint',
options: {
scorePODR: 800,
scoreMedian: 1600
}
},{
path: 'metrics/speed-index',
options: {
scorePODR: 1100,
scoreMedian: 2300
}
},{
path: 'metrics/interactive',
options: {
scorePODR: 2000,
scoreMedian: 4500
}
},{
path: 'metrics/first-cpu-idle',
options: {
scorePODR: 2000,
scoreMedian: 4500
}
},
],
};
module.exports = config;
0%