AWS & Typescript Masterclass - 14. Typescript recap

September 12th, 2022

100-123

 


 

(103) Types

inferred type

var a = 'hello'    // inferred string

explicit type

var a:string = 'hello'    // explicit string

 

var arr: string[] = []

arr.push('hello')    // ok

arr.push(123)    // nok

 

ANY - last resort

var a:any = 'hello'

a = 3;    //ok

a = true    //ok

 


(104) User defined types

objects have types

functions have types

 

interface

interface Person {

firstName: string,

lastName: string,

}

 

type

type job = string

 

type specificJob = 'Engineer' | 'Programmer'

 

combining types

> found in AWS sdk

type AWSError = Error & {

code: string;

}

 


(105) Optional properties

optional properties

interface Foo {

optProp?: string   // optional

}

 

optional parameters

function foo(optParam?: string) {}

 


(106) Type guards

type info only exists at compile time

runtime is not checked -> type guards

function isPerson(potentialPerson: any): boolean {

return 'firstName' in potentialPerson && 'lastName' in potentialPerson

}

 

if(isPerson(foo)) {

const definitelyPerson: Person = foo

}

 

ref: Typescript: Type Guards

 


(109) Access Modifiers

public    // default

private

protected    // -> classes who extend

 

workaround for private

const privateData = (someClass as any).somePrivateField

 

shorthand fields:

class Server {

private port: number

constructor(port: number) {this.port = port}

}

 

class SameServer {

constructor(private port: number) {this.port = port}

}

 


(110) Inheritance

abstract class BaseServer {

abstract stopServer(): void

}

 

 

class Server extends BaseServer {

stopServer(){}

}

 


(111) Implementing interfaces+

interface IServer {

startServer(): void

}

 

class Server implements IServer {

startServer(){}

}

 


(112) Generics

function returnKeys<T>(arg: T) {

return arg

}

 

function returnKeys<T extends Object>(arg: T) {

return arg

}

 

interface Person<T> {

special: T

}

 

 

 


(114) Modules

import {Bar} from './foo'

import * as Foo from './foo'

 

using paths

without paths

import { Foo } from './data/components/Foo'

with paths

import { Foo } from '@components/Foo'

tsconfig.json (convention is to start with '@')

{

"paths": {

"@components/*": ["data/components/*"]

}

}

 


(115) Strict Checks

tsconfig.json

{

"compilerOptions": {

"noImplicitAny": true,                 // type must be configured

"noImplicitThis": true,                // avoid errors within an anonymous function

"strictNullChecks": true,              // forces 'string | undefined' to be unwrapped

"strictPropertyInitialization": true   // require class field to be initialized

"strict": true                         // all other checks

}

}

 


(116) undefined, null, never, unknown

you often use undefined and null

you rarely use never and unknown

 

undefined & null are the same as in JS

 

Undefined constants - when it should be defined in the future

let abc = undefined

 

Undefined functions response - when remote call fails

function getDataOverInternet(): string | undefined {}

 

const data = getDataOverInternet()

 

if(data)

const definitelyString: string = data

else

throw new Error('nope')

 

Null - ?? don't use it??

 

Unknown - when you're not sure yet

let input: unknown;

input = someInput

 

if(typeof input === 'string')

const definitelyString: string = input

else

throw new Error('nope')

 

Never - when you throw

function foo(tasks: number) : void | never {

if(tasks > 3)

throw new Error('too many tasks')

}

 

function error() : never {

throw new Error()

}

?? why not void|Error

-> that's Promises / callbacks, Error is not a return type of a direct throw (unless wrapped in Promise / callback)

 


(117) Enums and Switch

enum Foo { A,B,C }

 

Foo.A        // = 0

Foo[Foo.A]   // = "A"

 

enum Bar {

A = "aaa",

B = "bbb",

C = "ccc"

}

Foo.A        // = "aaa"

 

const foo: Foo = Foo.A

switch(foo) {

case Foo.A:

break;

case Foo.B:

case Foo.C:

break;

default:

break;

}

 


(118) Running on NodeJS

npm i -D @types/node

 

// Server.ts

import { createServer, IncomingMessage, ServerResponse } from 'http'

 

export class Server {

public startServer(){

createServer(

(req: IncomingMessage, res: ServerResponse)=>{

console.log(`Got request from ${req.headers['user-agent']} for ${req.url}`)

res.write('Hello from TS server!')

res.end()

}

).listen(8080)

console.log('Server started')

}

}

 

// Launcher.ts

import { Server } from "./Server";

 

class Launcher {

private server: Server = new Server();

 

public launchApp(){

this.server.startServer()

}

}

 

new Launcher().launchApp()

 

running with node

tsc

node dist/Launcher.js

 

running with ts-node

npm i -D ts-node

npm i -D typescript

 

ts-node src/Launcher.ts

 


(119) Debugging Node Typescript

 

run config (in VSCode)

{

"version": "0.2.0",

"configurations":[

{

"type": "node"

"request": "launch"

"name":"Debug local file",

"runtimeArgs":["-r", "ts-node/register"]

"args":"${relativeFile}"

"env":{"AWS_REGION":"eu-west-2"}

}

]

}

(you can add AWS credentials to env if you're not logged in locally)

 


(120) Running in the browser

 

vscode plugin 'Live Server'

./.vscode/settings.json

{

"liveServer.settings.file": "index.html"

}

 

typescript compile alias cmd for tsc

NGINX: single page application static website (nginx docker)

 

Webpack

npm i -D typescript ts-node ts-loader

npm i -D webpack webpack-cli @types/webpack

 

./webpack.config.ts

import {Configuration} from "webpack";

import {resolve} from "path"

 

 

const config: Configuration = {

entry: './src/Launcher.ts',

mode: 'development',

devtool: 'inline-source-map',

module: {

rules: [

{

use: 'ts-loader',

exclude: /node-_modules/

}

]

},

resolve: {

extensions: ['.tsx', '.ts', '.js']

},

output: {

filename: 'bundle.js',

path: resolve(__dirname, 'dist')

}

}

 

 

export default config

 

 

 


(122) Property Decorators

https://www.typescriptlang.org/docs/handbook/decorators.html

add something to existing classes / method / accessor / property / parameter

tsconfig.json

{

"compilerOptions": {

"experimentalDecorators": true

}

}

 

Class properties

class Manager {

@propertyDecorator

someProperty: string

}

 

new Manager().someProperty = 'foo'

// same as without the decorator calling:

propertyDecorator(Manager.prototype, 'someProperty')

 

function propertyDecorator(target: any, key: string) {

let property = target[key]

const getter = () => property

const getter = (newVal: any) => {property = newVal}

Object.defineProperty(target, key, {

get: getter,

set: setter,

configurable: true,

enumerable: true,

})

}

 

Decorator factory (second order function to pass config)

class Manager {

@decoratorWithParameters('foo')

someProperty: string

}

 

function decoratorWithParameters(otherObject:any) {

return (target: any, key: string) => ...

}

 


(123) Method decorators

https://www.typescriptlang.org/docs/handbook/decorators.html

tsconfig.json

{

"compilerOptions": {

"experimentalDecorators": true

}

}

 

Class properties

class Manager {

@methodDecorator

someMethod(){}

}

 

function methodDecorator(target: Object, propertyKey: string, descriptor: PropertyDecorator){

const classname = target.constructor.name

const originalMethod = descriptor.value

descriptor.value = async function(...args: any[]){

const result = await originalMethod.apply(this, args)

return result

}

}