CoffeeScript & TypeScript

JavaScript

  • Dynamický
  • Interpretovaný (JIT)
  • Slabě typovaný (typová konverze)
  • C-like syntax { } ;

Jazyky kompilované do JavaScriptu

Nejznámnější jazyky:

  • CoffeeScript
  • Dart (Google - Flutter)
  • TypeScript (Microsoft)

Snaží se vyřešit nedostatky JavaScriptu nebo ho zlepšit

CoffeeScript

  • vznikl v roce 2009 (Jeremy Ashkenas)
  • Kompilovaný do JavaScriptu (syntaktický cukr)
  • Založený na programovacích jazycích Ruby a Python
  • Neobsahuje ; a {}
  • Lepší čitelnost
  • Náchylné na bílé znaky
  • npm install --save-dev coffeescript

CoffeeScript - syntax

fibonacci = ->
  [previous, current] = [1, 1]
  loop
    [previous, current] = [current, previous + current]
    yield current
  return

getFibonacciNumbers = (length) ->
  results = [1]
  for n from fibonacci()
    results.push n
    break if results.length is length
  results

CoffeeScript - Google Trends

Typování v prog. jazycích

Nedostatky JavaScriptu

"b" + "a" + + "a" + "a"
// "baNaNa"

"2" + 2
// "22"

"2" + "2" - "2"
// 20

4 / []
// Infinity

{} + []
// 0

true - true
// 0

[] == ""
// true

TypeScript

  • Microsoft, 2012
  • Kompilovaný do JavaScriptu
  • Přináší statické typy
  • Je nadmnožinou JavaScriptu
  • Google, Airbnb, Slack

TypeScript - NPM trends

TypeScript - Výhody

  • Typová bezpečnost
  • Chyby při kompilaci
  • Hybrid mezi dynamicky a staticky typovaným jazykem
  • Nadmnožina JavaScriptu
  • Autocomplete v IDE
  • Konfigurovatelnost
  • Implicitní typování
  • Code managment
  • Využití novějších verzí JavaScriptu (např. ES6)
const obj = { width: 10, height: 15 };
const area = obj.width * obj.heigth;
// Property 'heigth' does not exist on type 
// '{ width: number; height: number; }'. 
// Did you mean 'height'?

TypeScript - Nevýhody

  • Komplikovaný typový systém
  • Požadovaná kompilace
  • Větší objem kódu
  • Setup

TypeScript - Jak začít

  • Visual Studio Code
  • npm -install -D typescript
  • tsc --init (vytvoří tsconfig.json)
  • tsc fileToCompile.ts
  • npx create-react-app my-app --template typescript

tsconfig.json

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */

    /* Basic Options */
    "incremental": true /* Enable incremental compilation */,
    "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
    "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
    "lib": [] /* Specify library files to be included in the compilation. */,
    "allowJs": true /* Allow javascript files to be compiled. */,
    "checkJs": true /* Report errors in .js files. */,
    "jsx": "preserve" /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */,
    "declaration": true /* Generates corresponding '.d.ts' file. */,
    "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */,
    "sourceMap": true /* Generates corresponding '.map' file. */,
    "outFile": "./" /* Concatenate and emit output to single file. */,
    "outDir": "./" /* Redirect output structure to the directory. */,
    "rootDir": "./" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
    "composite": true /* Enable project compilation */,
    "tsBuildInfoFile": "./" /* Specify file to store incremental compilation information */,
    "removeComments": true /* Do not emit comments to output. */,
    "noEmit": true /* Do not emit outputs. */,
    "importHelpers": true /* Import emit helpers from 'tslib'. */,
    "downlevelIteration": true /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */,
    "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */,

    /* Strict Type-Checking Options */
    "strict": true /* Enable all strict type-checking options. */,
    "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
    "strictNullChecks": true /* Enable strict null checks. */,
    "strictFunctionTypes": true /* Enable strict checking of function types. */,
    "strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */,
    "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */,
    "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */,
    "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */,

    /* Additional Checks */
    "noUnusedLocals": true /* Report errors on unused locals. */,
    "noUnusedParameters": true /* Report errors on unused parameters. */,
    "noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
    "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
    "noUncheckedIndexedAccess": true /* Include 'undefined' in index signature results */,
    "noPropertyAccessFromIndexSignature": true /* Require undeclared properties from index signatures to use element accesses. */,

    /* Module Resolution Options */
    "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
    "baseUrl": "./" /* Base directory to resolve non-absolute module names. */,
    "paths": {} /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */,
    "rootDirs": [] /* List of root folders whose combined content represents the structure of the project at runtime. */,
    "typeRoots": [] /* List of folders to include type definitions from. */,
    "types": [] /* Type declaration files to be included in compilation. */,
    "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,
    "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
    "preserveSymlinks": true /* Do not resolve the real path of symlinks. */,
    "allowUmdGlobalAccess": true /* Allow accessing UMD globals from modules. */,

    /* Source Map Options */
    "sourceRoot": "" /* Specify the location where debugger should locate TypeScript files instead of source locations. */,
    "mapRoot": "" /* Specify the location where debugger should locate map files instead of generated locations. */,
    "inlineSourceMap": true /* Emit a single file with source maps instead of having a separate file. */,
    "inlineSources": true /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */,

    /* Experimental Options */
    "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
    "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,

    /* Advanced Options */
    "skipLibCheck": true /* Skip type checking of declaration files. */,
    "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
  }
}

Základní typy

  • undefined
  • null
  • boolean
  • number
  • string
  • object
const a: undefined = undefined;
const b: null = null;
const c: boolean = true;
const d: number = 5;
const e: string = "hey!";
const f: object = {};

any vede ke ztrátě výhod TypeScriptu

const anything: any = {};
console.log(anything.prop);

Pole

const numberArray1: number[] = [1, 2, 3];

const numberArray2: Array<number> = [1, 2, 3];
  • Genericlý typ
  • typ zapisujeme do špičatých závorek

Funkce

const add = (a: number, b: number): number => a + b;
  • Typy vstupu
  • Typ výstupu (TypeScript umí automaticky odvozovat)
  • Možnost definice typu celé funkce
type Add = (a: number, b: number) => number;

const add: Add = (a, b) => a + b;

Optional Properties

const addNumbers = (a: number, b?: number) => {
  b = b || 0; // b je typu number | undefined
  return a + b; // b je typu number
};

const obj: { name: string; age?: number } = { name: "John" };
  • Typ je potencionálně undefined
  • Musíme kontrolovat před použitím

Union Types

let unionType: number | boolean;

unionType = 5;
unionType = true;

// unionType = "Hello world" by vedlo k chybě
  • Vymezení několika různých typů
  • Využití dynamičnosti

Typový alias

type CarBrand = "ferrari" | "audi" | "porsche" | "bmw";

interface Car {
  brand: CarBrand;
  speed: number;
}

// (car: Car) => CarBrand
const getCarBrand = (car: Car) => {
  return car.brand;
};

const getCarSpeed = (car: Car) => {
  return car.speed;
};
  • Znovupoužitelné typy
  • Type alias: Jméno pro typ / Pojmenování typu

Interface

interface Car {
  brand: CarBrand;
  speed: number;
}

interface Car {
  price: number;
}

const car: Car = {
  brand: "audi",
  speed: 200,
  price: 1000000,
};
  • Na rozdíl od typového aliasu je možné ho rozšiřovat
  • Popis rozhraní pro komunikaci uvnitř aplikace i mimo ni

Type assertions

const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;

// nelze: const x = "hello" as number;
  • Pomocí slovíčka as
  • Pouze na více nebo méně specifický typ
  • Nelze kompletně změnit typ
  • Využití pro popsání typu o kterém TypeScript nemůže vědět

Literal types

type CarBrand = "ferrari" | "audi" | "porsche" | "bmw";

const brand: CarBrand = "audi";
  • Přímo určené specifické hodnoty, kterých může proměnná nabývat
  • Možné použít pro řetězce a čísla

Literal types - as const

const myObj = { method: "POST" } as const;

const foo = (a: "GET" | "POST") => {
  console.log(a);
};

foo(myObj.method);
  • nelze přiřadit typ string do typu konkrétního řetězce (literal type)
  • můžeme použít as const pro konverzi celého objektu na type literal

Kontrola pomocí typeof

function printId(id: number | string) {
  if (typeof id === "string") {
    // In this branch, id is of type 'string'
    console.log(id.toUpperCase());
  } else {
    // Here, id is of type 'number'
    console.log(id);
  }
}
  • typeof vrací název typu jako řetězec
  • "string", "boolean", "number" ...
  • Vhodné pro vyloučení určitých typů proměnné

Rozšiřování typů

interface BasicAddress {
  name?: string;
  street: string;
  city: string;
  country: string;
  postalCode: string;
}

interface AddressWithUnit extends BasicAddress {
  unit: string;
}
  • klíčové slovo extends
  • možné rozšířit více typů oddělených čárkou

Intersection

interface Base {
  print: (value: number) => number;
}

type Intersected = Base & {
  print: (value: string) => string;
};

// -- error --
// interface Extended extends Base {
//   print: (value: string) => string;
// }
  • Pomocí &
  • Podobné rozšiřovaní pomocí extends

Generické typy

interface Box<T> {
  content: T;
}

const booleanBox: Box<boolean> = {
  content: true,
};

const stringBox: Box<string> = {
  content: "lorem ipsum",
};
  • Příklad: Array<T>
  • Pro type alias i interface

Typy pro knihovny

  • Pro většinu knihoven jsou typy již definované
  • Repozitář DefinitelyTyped
  • Poskytuje typy pro existující JavaScript
  • npm install --save-dev @types/node

Kdy použít TypeScript

  • Větší aplikace
  • Týmové projekty

Kdy TypeScript nemusí být výhodou

  • Malé aplikace
  • Sólo projekty

Nicméně je těžké argumentovat pro jeho nepoužití, jelikož se jedná o nadmnožinu JavaScriptu a můžeme si vybrat, kdy chceme typové kontroly využít

To je vše

zdroje:

CoffeeScript & TypeScript

By Michal Čížek

CoffeeScript & TypeScript

  • 48