JREAM

TypeScript Quick Booster

A typed language prevents development errors far greater than one might realize. It may take a few seconds longer to type but in the long run it pays off to have errors occur during development rather than while in production.

For example, if you had a function to create a string from an array and the only parameter it accepted was an array. You would want to make sure none of your code is attempting to pass in a string. TypeScript (and other languages) keep your code type-safe.

Installing and Running TypeScript

You of course need the TypeScript package which is shipped through npm. You can install it globally (-g) with the following command so you can use it anywhere:

npm i -g typescript
tsc --help

You may notice there is no $ typescript command, rather its tsc which stands for TypeScript Compiler.

Create A Simple TypeScript Project

To run typescript from the CLI do the following to instantiate a new project. We have options to watch for *.ts changes by running tsc -w. However, TypeScript compiles down to JavaScript and when practicing code we don't want to run tsc to compile, and then run node dist/main.js only to see the results. We want immediate results. This is the purpose of adding the yarn packages below.

# Create empty Project
mkdir ts-practice && cd ts-practice
touch main.ts other.ts

# Creates tsconfig.json
tsc --init

# Add packages and TS Output Watcher
yarn init -y
yarn add typescript ts-node-dev -D

To keep your compiled JavaScript separate from your TypeScriptchange the following line intsconfig.json`:

"outDir": "./dist", // Make sure to uncomment this line as well

To compile all TypeScript files within the current directory you'd run tsc. This will keep the *.ts and *.js files separate. Without the tsconfig.json you can provide the same result with tsc --outDir ./dist.

To make things easier let's adjust package.json to include a command we can easily run.

"scripts": {
  "dev": "tsnd --respawn main.ts",
  "build": "tsc"
}

Now you can run yarn dev and watch real-time output from your TypeScript without compiling it. Additionally, if you wanted to included another file to watch at the same time you can import './file.ts' at the top of main.ts.

Hopefully that setup was easy enough. We can jump into practicing writing TypeScript because practice is the best way to learn.

Data Types Review

One thing to remember are the JavaScript built in types which can often be confused language to language. For example, most languages use int|integer but JavaScript uses number.

TypeExample
number1
float5.5
string'Hello'
booleantrue/false
array[]
object{}
function(() => )
nullnull
undefinedundefined

You can always check a type with typeof(varName).

Assignments and Variables

In TypeScript (and JavaScript) it's best to always assign variables with let and const. When using var for assignment it has no scope limitations and can create problems so use let instead and use const for immutable variables.

In TypeScript its not always necessary to declare a type for a variable like below. The reason is that TypeScript automatically infers the variables type based on what you put in it.

let userName: string = 'Jesse'  // Redundant
let userName = 'Jesse'          // Better

Parameter Type Annotations

Here we can compare the difference between enforcing types within our methods in order to cause a developer error rather than a user error, which is highly preferred!

// Parameter: string
function greet(name: string) {
  return `Hello ${name}`
}
console.log( greet('Thomas') )

An array looks a little different because we declare the type of content within the array. Below it looks like a string but it has the array brackets [] making it an array of string(s).

// Parameter: array of string(s)
function getGroceries(food: string[]) {
  return `You need to buy ${food.join(', ')}`
}
console.log( getGroceries(['milk', 'eggs', 'bread']) )

An object (non-primitive type) is another option for a parameter type. Here's an example of it inline.

// Parameter: object (self defined)
function createLine(pt: { x: number, y: number }) {
  return `The x-axis is at ${pt.x} and the y-axis is at ${pt.y}`
}
console.log( createLine({x: 25, y: 0}) )

Optional Properties

We can pass in optional parameters to objects using the ? on the property type. We can also use JavaScript to safely avoid an undefined error when checking if the object key is set.

// Parameter: string & optional string
function getName(data: {first: string, last?: string}) {
  return `${data.first} ${data?.last}`
}
console.log( getName({first: 'Jesse'}) )

By using data?.last in JavaScript we won't have to type check of undefined.

Skip Type Checking

Using the any type disables type checking

let dataPlan: any = {}  //

Return Type Annotations

We can also enforce types on the return value of a method. This of course requires a return statement in the function to return the :type.

// Parameter: number, Return: string
function userAge(age: number): string {
  return `This user is ${age} years old`
}
console.log( userAge(25) )

If there is no return statement you can optionally use void as well.

Union Types

When you have a parameter than can take a mixed variable of more than one type you can use a union which looks quite the same with a pipe |.

function getUserTag(id: number|string) {
  return `Your User Tag is: ${id}`
}
console.log( getUserTag(711) )

Literal Parameter Options

You can enforce parameters that must be used in your methods. This is quite a nice feature. Below, only home, work, or school are valid arguments.

// Parameter argument must be one of: home, work, or school
function travelTo(destination: 'home'|'work'|'school') {
  return `You are traveling to ${destination}`
}
console.log( travelTo('work') )

Literal Option Return Type

We can do the same thing with return types. It looks similar to a Union but we are choosing what to enforce.

function hasExpired(yearsOld: number): 'yes'|'no' {
  return (yearsOld > 120)
    ? 'yes'
    : 'no'
  }
}

Composing Types

Rather than have Unions all over the place we can define our own custom types the same way. Notice the word type is used and not let or const.

type Secure = 'locked' | 'unlocked'
type User = {
  id: number | string,
  name: string,
  email: string
}

// Implement Custom User and Secure Types
function userStatus(user: User, status: Secure) {
  return `${user.name} (${user.id}) has their house ${status}`
}

// Output
const myUser = {
  id: '123',
  name: 'Jesse',
  email: 'none@none.com'
}
console.log( userStatus(myUser, 'locked') )

An Interface is another way to name and object type. It looks and behaves almost identical to the Type declaration. The differences are not important at this stage but to create an interface you do not use an = symbol.

interface User {
  name: string,
  email: string
}

There's Still More

There is a lot to TypeScript. Just because it exists doesn't mean you have to use it. It's good to know about if you'd like to dig deeper into the TypeScript Docs.