Skip to content

Instantly share code, notes, and snippets.

@yinonc
Last active April 27, 2019 15:55
Show Gist options
  • Save yinonc/e91b5d111a8c34e4dbc2182969725072 to your computer and use it in GitHub Desktop.
Save yinonc/e91b5d111a8c34e4dbc2182969725072 to your computer and use it in GitHub Desktop.
Typescript basics

TypeScript

From JS to TS

  1. Introduction
  2. Installation
  3. Basic Types
  4. Functions
  5. Interfaces
  6. Using TS With React
  7. Defining Modules
  8. Sources

This guide give Javascript programmers some basics for Typescript coding.

npm i -g typescript

Use you source code with .ts extension. Then you can compile your code with:

tsc file.ts

And the result would be file.js.

// Add here how to compile whole project
// Add here how to compile whole project
// Add here how to compile whole project

Boolean:
let isDone: boolean = false;
Number:
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
String:
let color: string = "blue";
Array:

Two ways to declare an array:

let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
let zoo: Animal[] = [new Rhino(), new Elephant(), new Snake()];
Tuple:
let x: [string, number];
x = ["hello", 10]; // OK
x = [10, "hello"]; // Error
console.log(x[0].substr(1)); // OK
console.log(x[1].substr(1)); // Error, 'number' does not have 'substr'
Any:

We may need to describe the type of variables that we do not know when we are writing an application. These values may come from dynamic content.

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
Void:

void is a little like the opposite of any: the absence of having any type at all.

function warnUser(): void {
    console.log("This function does not return any value");
}
let unusable: void = undefined;
Null and Undefined:

Not extremely useful on their own

// Not much else we can assign to these variables!
let u: undefined = undefined;
let n: null = null;
Never:

The never type represents the type of values that never occur. For instance, never is the return type for a function expression or an arrow function expression that always throws an exception or one that never returns.

// Function returning never must have unreachable end point
function error(message: string): never {
    throw new Error(message);
}
// Function returning never must have unreachable end point
function infiniteLoop(): never {
    while (true) {}
}
Object:

object is a type that represents the non-primitive type, i.e. anything that is not number, string, boolean, symbol, null, or undefined.

//With object type, APIs like Object.create can be better represented
declare function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK

create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
Regular declaration:
function add(x: number, y: number): number {
    return x + y;
}
Optional Parameters:
function buildName(firstName: string, lastName?: string) {
    return lastName ? firstName + " " + lastName : firstName
}
let result1 = buildName("Bob");                  // works correctly now
let result2 = buildName("Bob", "Adams", "Sr.");  // error, too many parameters
let result3 = buildName("Bob", "Adams");         // ah, just right
Default Parameters:
function buildName(firstName: string, lastName = "Smith") {
    return firstName + " " + lastName;
}
let result1 = buildName("Bob");                  // works correctly now, returns "Bob Smith"
let result3 = buildName("Bob", "Adams", "Sr.");  // error, too many parameters
let result4 = buildName("Bob", "Adams");         // ah, just right
Rest Parameters:
function buildName(firstName: string, ...restOfName: string[]) {
    return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
// employeeName will be "Joseph Samuel Lucas MacKinzie"

One of TypeScript’s core principles is that type checking focuses on the shape that values have. Interfaces fill the role of naming these types, and are a powerful way of defining contracts within your code as well as contracts with code outside of your project.

Object Types:
interface LabeledValue {
    label: string;
    name?: string;
}

function printLabel(labeledObj: LabeledValue) {
    console.log(labeledObj.label);
}

The interface defines that label property in labeledObj has string value type. When adding ? to a property in an interface, it makes it as an optional value in the object.

In interfaces we can also add readonly properties:

interface Point {
    readonly x: number;
    readonly y: number;
}

let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!

We can also define an interface which allows its objects to have some extra properties. If SquareConfig can have color and width properties with the above types, but could also have any number of other properties, then we could define it like so:

interface SquareConfig {
    color?: string;
    width?: number;
    [propName: string]: any;
}
Function Types:

This is like a function declaration with only the parameter list and return type given. Each parameter in the parameter list requires both name and type.

interface SearchFunc {
    (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(src, sub) {
    let result = src.search(sub);
    return result > -1;
}
Indexable Types:

Indexable types have an index signature that describes the types we can use to index into the object, along with the corresponding return types when indexing.

interface StringArray {
    readonly [index: number]: string;
}

let myArray: StringArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
myArray[2] = "Mallory"; // error!
Class Types:

Implementing an interface.

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date): void;
}

class Clock implements ClockInterface {
    currentTime: Date = new Date();
    setTime(d: Date) {
        this.currentTime = d;
    }
    constructor(h: number, m: number) { }
}
Extending Interfaces:

This allows you to copy the members of one interface into another, which gives you more flexibility in how you separate your interfaces into reusable components.

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
Hybrid Types:

As we mentioned earlier, interfaces can describe the rich types present in real world JavaScript. Because of JavaScript’s dynamic and flexible nature, you may occasionally encounter an object that works as a combination of some of the types described above.

interface Counter {
    (start: number): string;
    interval: number;
    reset(): void;
}

function getCounter(): Counter {
    let counter = <Counter>function (start: number) { };
    counter.interval = 123;
    counter.reset = function () { };
    return counter;
}

let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
Interfaces Extending Classes:

When an interface type extends a class type it inherits the members of the class but not their implementations. It is as if the interface had declared all of the members of the class without providing an implementation. Interfaces inherit even the private and protected members of a base class. This means that when you create an interface that extends a class with private or protected members, that interface type can only be implemented by that class or a subclass of it.

class Control {
    private state: any;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control implements SelectableControl {
    select() { }
}

class TextBox extends Control {
    select() { }
}

// Error: Property 'state' is missing in type 'Image'.
class Image implements SelectableControl {
    private state: any;
    select() { }
}
  • Importing React: React doesn't export anything by default, so we should import it with:
    import * as React from 'react'
  • JSX: When writing a React component using jsx, the file suffix should be .tsx.
  • State & Props: For state and props we should use interfaces in order to define which state or props properties should we accept.
    When we will define it, typescript would warn for each missing mandatory prop, or warn for each non existing prop in props interface.
  • Events: For each event we can use its event type. You can find at the web every event type you'll need.
import * as React from 'react'
interface IProps {
  text: string
  age?: number
}
interface IState {
  email: string
  name: string
}
export default class Form extends React.component<IProps, IState> {
  state: IState = {
    email: '',
    name: ''
  }
  handleChange = (e: React.FormEvent<HTMLInputElement>) => {
    this.setState({
      name: e.target.value
    })
  }
  render() {
    return (
      <div>
        <span>{this.props.text}</span>
        <input name='name' value={this.state.name} onChange={this.handleChange} />
      </div>
    )
  }
}

When you'll use some modules which are written with Javascript, TypeScript would warn when using them. For example, lodash.

import { omit } from 'lodash'
interface Color {
  color: string
}
const arr1 : Color[] = ['red', 'green', 'blue']
const arr2 : Color[] = _.omit(arr1, 'green') // This would cause warning

We have two ways for escaping warning for such modules:

  1. Define .d.ts file for defining them:
    modules.d.ts:
declare module 'lodash'
declare module 'wspy'
  1. You can add @types/lodash module:
yarn add -D @types/lodash
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment