How I use TypeScript

Simon Mika

Simon Mika

Mentor

  • development managers
  • project managers
  • product managers
  • lead developers
  • scrum masters

Advice

  • agile development
  • lean startup methology
  • system architecture
  • developer recruitement

Build

Organization & Product

  • interim management
  • recruitement
  • turn key projects & prototypes

What is TypeScript?

What is TypeScript?

  • Superset of ECMAScript.
  • Optional static typing.
  • Outputs plain JavaScript.
Tool for large scale JavaScript projects.

Tooling

Errors

Auto Complete

Go to Definition

Peek Definition

Rename Symbol

Debug

Configuration

Tools


						npm install --save-dev typescript tslint np watch
					

Types


						npm install --save-dev @types/node
					

package.json


						{
							"scripts": {
								"dev": "watch 'npm run build' source",
								"prebuild": "tslint --config .vscode/tslint.json --project source/tsconfig.json",
								"build": "tsc -p source",
								"pretest": "npm run build",
								"test": "node build/index.test.js",
								"test:watch": "watch 'npm test' source",
								"prepublish": "npm run build",
								"release": "np",
								"clean": "rm -rf build node_modules"
							}
						}
					

tsconfig.json


						{
							"compilerOptions": {
								"target": "es6",
								"module": "commonjs",
								"declaration": true,
								"sourceMap": true,
								"sourceRoot": "../source",
								"outDir": "../build/"
							},
							"files": [
								"index.ts",
								"index.test.ts"
							]
						}
					

tsconfig.json (cont.)


						{
							"compilerOptions": {
								"noImplicitAny": true,
								"removeComments": true,
								"preserveConstEnums": true,
								"strict": true,
								"alwaysStrict": true,
								"strictNullChecks": true,
								"forceConsistentCasingInFileNames": true,
								"noImplicitThis": true,
								"noUnusedLocals": true,
							}
						}
					

.vscode/task.json


						{
							"version": "0.1.0", "command": "npm", "isShellCommand": true,
							"showOutput": "always", "suppressTaskName": true,
							"tasks": [
								{
									"taskName": "build", "args": ["run", "build"],
									"isBuildCommand": true,
									"problemMatcher": [ "$tsc", "$tslint5" ]
								},
								{
									"taskName": "test", "args": ["run", "test"],
									"isTestCommand": true, "problemMatcher": [ "$tsc", "$tslint5" ]
								}
							]
						}
					

.vscode/launch.json


						{
							"version": "0.2.0",
							"configurations": [
								{
									"type": "node", "request": "launch", "protocol": "inspector",
									"name": "c99-build",
									"preLaunchTask": "build",
									"program": "${workspaceRoot}/build/index.js",
									"args": [ "c99-build", "${workspaceRoot}/example/function.syspl" ],
									"sourceMaps": true,
									"outFiles": [ "${workspaceRoot}/build/**/*.js" ]
								}
							]
						}
					

Type System

Types are Optional


						function f(x) {
							return x
						}
					

Unless "noImplicitAny": true

Type Interference


						const name = "Name"
						const message = name
						const data = ["mixed data", 1, 2, true, null]
					

Type Expressions


						let name: string
						let age: number
						let names: string[]
						let onlyObjects: Object
						function f(x: int): int {
							return x ** x
						}
					

Type Expressions (cont.)


						let whatever: any
						let alwaysUndefined: undefined
						let alwaysNull: null
						function loopForever(): never {
							while (true) { }
						}
					

Object Type Expressions


						let record: { name: string, age: number }
						let records: { name: string, age: number }[]
					

Type Alias


						type Record = { name: string, age: number }
						let record: Record
						let records: Record[]
					

Function Type Expressions


						let play: (position: number) => Promise
						function f(x: (position: number) => boolean): void {
							if (position > 0 && x(position)
								alert("playing")
						}
					

Tuple


						let record: [string, number]
						record = ["Smith", 38]
						record[1]++
					

Enum


						enum Color { Red, Green, Blue, White = 255 }
						let color: Color
						color = Color.Green
					

Type Assertions


						let a: any = 5
						let b = a as number
					

						let c = a
					

Intersection Types


						type Record = { name: string, age: number }
						let extendedRecord: Record & { email: string }
					

Union Types


						type Record = { name: string, age: number }
						let data: Record | string = "Foo"
						data = { "Bar", 42 }
					

String Literal Types


						let encoding: "utf8" | "ascii" | "utf16"
						encoding = "utf8"
						encoding = "blabla" // compile error
					

Numeric Literal Types


						let grade: 1 | 2 | 3 | 4 | 5
						grade = 3
						grade = 6 // compile error
					

Strict Null Checks

"strictNullChecks": true,


						function log(data: number[]) {
							alert("values: " + data.map(d => d.toString().join(", "))
						}
						log([42, 13.37])
						log([])
						log(null) // compile time error
					

Fixes Tony Hoare billion dollar mistake.

When Null is Wanted


						function log(data: number[] | null | undefined) {
							if (data) // type guard required
								alert("values: " + data.map(d => d.toString().join(", "))
						}
						log([42, 13.37])
						log([])
						log(null) // no error
					

When Undefined is Wanted


						function log(data?: number[]) {
							if (data) // type guard required
								alert("values: " + data.map(d => d.toString().join(", "))
						}
						log(undefined) // no error
					

Type Guards


						function f(data: number | Record | null | string): string {
							return
								typeof(data) == "number" ? (data ** data).toString() :
								data instanceof Record ? data.age.toString() :
								data || "null"
						}
					

Custom Type Guards


						function hasName(data: any) data is { name: string } & any {
							return typeof((data as { name: string }).name) == "string"
						}
					

Modules

ECMAScript-style

Export


						export class Test { }
						export function RunTest() {}
					

Re-exportindex.ts


						export { Test } from "./Test"
					

Re-re-export index.ts


							import * as NameSpace from "./NameSpace"
							export {
								NameSpace,
							}
						

Import


						import * as NameSpace from "./NameSpace"
						new NameSpace.Test()
					

Object Orientation

ECMAScript + extra

Classes


						export class Test {
							constructor() { }
						}
					

Inheritance


						export class Base {
							constructor() { }
						}
					

						import { Base } from "./Base"
						export class Test extends Base {
							constructor() {
								super()
							}
						}
					

Abstract Class


						export abstract class Base {
							protected constructor() { }
							abstract doThing()
						}
					

						import { Base } from "./Base"
						export class Test extends Base {
							constructor() {
								super()
							}
							doThing() { }
						}
					

Access Modifiers

public by default


						export class Test {
							protected constructor() { }
							private doSomething() { }
							protected doSomethingElse() { }
						}
					

compile time only - public when running

Properties


						export class Test {
							name: string
							constructor() { }
						}
					

Private / Protected Properties


						export class Test {
							private name: string
							protected description: string
							constructor() { }
						}
					

Read Only Properties


						export class Test {
							readonly name: string
							readonly description = "Directly initialized value"
							constructor() {
								this.name = "Constructor initialized value"
							}
						}
					

Constructor Properties


						export class Test {
							constructor(readonly name: string, private last = "", public value?: number) {
							}
						}
					

Interfaces


						export interface Order {
							count: number
							price: number
							volume: number
						}
					

Good for receiving JSON-data.

Interfaces with Methods


						export interface Handler {
							raise: {
								(message: Message): void
								(message: string, level?: Level, type?: string, region?: Region): void
							}
						}
					

Explicit Implementation


						import { Handler } from "./Handler"

						export class ConsoleHandler implements Handler {
							raise(message: string | Message, level?: Level, type?: string, region?: Region): void {
								if (!(message instanceof Message))
									message = new Message(message as string, level, type, region)
								console.error(message.toString())
							}
						}
					

Functions Interfaces


						interface Search {
							(source: string, needle: string): boolean;
						}
					

Read More

Conclusions & Questions

Simon Mika