kdocs
GitHub
Lang - Web
Lang - Web
  • Base
    • Css
    • Javascript
    • Typescript
      • New Project
  • Frameworks
    • Angular
      • Directives
      • Components
      • Templates
        • Bindings
        • Control Flow
        • Variables
      • Signals
      • Pipes
      • Services
        • Dependency Injection
      • Forms
        • Reactive Form
        • Template-Driven Form
      • Router
      • HTTP Client
      • Observables RxJS
      • Testing
        • Components
        • Directives
        • Pipes
        • Services
      • Optimization & Performance
      • Security
Powered by GitBook
On this page
  • Interoperability
  • Exploring the tsconfig.json
  • Types
  • Primitive Types
  • Enum
  • Function Type
  • Union Type
  • Intersection Types
  • Utility Types
  • Conditional Types
  • Generic Types
  • Mapped Types
  • Type Assertions
  • Class
  • Structure
  • Abstract classes
  • Decorators and Attributes
  • Decorators
  • Ignoring decorators
  1. Base

Typescript

PreviousJavascriptNextNew Project

Last updated 6 months ago

Is a superset of Javascript empowering it by:

  • Static typing

  • Decorators

  • Generic types

  • Tuples

  • Modules

  • Better OO support with abstract classes

  • Interfaces

  • Visibility modifiers

  • And more...

Needs to be transpiled to Javascript.

  • Compiler: Transforms code to low code (machine-code).

  • Transpiler: Transforms code to other high level languages (source-to-source).

Interoperability

Run js and ts in the same environment.

You can allow js to apply Typescript by creating a .d.ts file for a js file.

This file will have definitions typings for the js.

Exploring the tsconfig.json

Centralizes all the code static analysis and transpilation rules.

Options

  • incremental: For incremental transpilation, saving cache that will speedup future transpilations, specially for big projects. Essentialy just transpile the changed files.

  • target: Defines the version of Javascript that will be utilized as the transpilation target.

    • es5 | ECMA2009: Older version from 2009.

    • es6 | ECMA2015: Which targets the ECMA from 2015.

    • ECMA2016: Is the default one. (Higher compatibility)

      • Adds ** exponential, ...

    • ECMA2017:

      • Adds async, ...

    • ECMA2018:

      • Adds rest \ spread, ...

    • ECMA2019:

    • ECMA2020:

      • Adds ??

    • ...

    • Check https://node.green/ for details of ECMA versions support in Node.js

      • Node 16.x and greater versions, do support up to ECMA2023.

      • ESNext is the versions with Stage 3, Stage 2 and Stage 1 releases

  • module: Important to define the type of module system to be used. - CommonJS - ES Modules

  • include: Defines which are the directories that must be included in the transpilation.

  • exclude: Defines which directores must be excluded in transpilation.

  • outFile: Used for projects that run in the browser, defining a file that will concatenate all the transpiled files.

  • outDir: Defines the directory that the transpiled files will be placed after transpilation.

  • allowJS: To allow js files to part of the typescript environment.

  • strict: Turn on several type checkings like:

    • strictNullChecks: Allow or not to accept if things return null | undefined and what will receive them cannot handle it.

    • strictFunctionTypes: Allow or not to scrictly compare functions types to the detail when assigning functions to variables.

    • strictBindCallApply: Allow of not to pass to fn.call(), parameters to the function that are not of the same type.

    • strictPropertyInitialization: Allow or not to have class properties to be declared but not initialized.

    • noImplicitAny: Allow or not using variables without typing, that can be automatically impliced typed.

    • noImplicitThis: Allow or not, when you have this cases, where the this is not pointing to where you think it is.

    • useUnknowInCatchVariables: Allow or not to use unknown error types in catch.

    • alwaysStrict: Will always put use strict in the transpiled file.

      • Not used anymore in recent versions of ECMA.

    • allowUnreachableCode: Allow or not leaving code that is unreachable.

    • noUnusedLocals: Allow or not leaving unused declared variables.

    • noUncheckedIndexedAccess: Allow or not to access dynamic properties of objects with ..

      • For Ex.: [prop: string]: string.

      • The right way to access woulb be with [].

Types

Types can be inferred from values, but only at the variable declaration.

Variable types cannot change afterwards.

Type being inferred from the value:

let str = "This is a string"; // string

A variable declared with no type will by default be of any type.

let str; // any

Built-in primitive types includes:

  • boolean

  • string

  • number: Representing integers and floats.

  • undefined

  • null

  • any

  • unknown

  • never

  • void

  • bigint

  • symbol

Common built-in JS Objects:

  • Date

  • Error

  • Array or []

  • Map

  • Set

  • Regexp

  • Promise

Type Literals:

  • Object: { field: number }

  • Function: (arg: number) => number

  • Arrays: Array<number> or number[]

  • Tuple: [number, number]

More details:

any

Very flexible and can accept any type.

Despite it's flexibility, it is discouraged to use it, since it may break your code's type enforcement and thus the use of Typescript.

If you don't know the type, preffer using unkonwn.

Variable of any type may also receive different types values at any point.

str = "This is string";
str = 10;

unknown

Represents any value and while it is similar to any type, it is much safer because it's not legal to do anything with an unknown value.

function a(arg: any) {
    arg.b() // OK
}

function a(arg: unkonwn) {
    arg.b() // Gives compilation error
}

This is useful when describing function types because you can describe functions that accept any value without having any values in your function body.

never

It is a return type, meaning that it never returns a value.

This should mean that the function either throws an exception or terminates execution of the program.

function init(): never {
    while (true) {}
}

// or

function init(): never {
    throw new Error("");
}

void

Represents the return value of functions which don't return a value.

function a() {}

// Or

function a() {
    return;
}

Tuples

Are not a "type" per say, but it is a way to use the [] type, enforcing a size and adding values change constrains.

let point: [number, numer] = [10, 10];

type

  • Used for type definitions, usually to reduce repetition.

  • It can be amost the same as interface, but it cannot be extendend.

  • They may not be extended but types can be merged.

type Entity = {
    id: number;
};

type Person = {
    name: string;
    age: number;
};

type PersonEntity = Person & Entity;

// or

type PersonEntity = Person & { somethingelse: boolean };

interface

Types interface comparison checks can be faster.

  • Can be extended by declaring it multiple times, or merged with other interfaces.

  • Used to describe object shapes.

  • Can have function definitions. (Great to describe classes or contracts)

// Optionally take properties from existing `interface` or `type` with `extends`.
interface JSONResponse extends Response, HTTPAble {
    // By default is a public access, always existent and writable property.
    version: number;
    
    /*
        This is a optional property, so it can be `undefined` or even
        un-existent in JSONResponse.
    */
    detail?: string;
    
    // This can also be undefined, but will always exist in JSONReponse.
    name: undefined | string;
    
    /*
        This is a readonly property.
        (It only complains at compilation time, but there are ways to
        change it's value if wanted)
    */
    readonly body: string;
    
    // Functions may be described in two ways.
    update: (param: number) => void;
    update(param: number): void;
    
    // Make the object this interface describes callable. (like jsonResponse())
    (): string;
    
    // A callable interface can have multiple definitions for different parameters.
    (param: string): string;
    (param: number): number;
    
    // You can use `new` on the object this interface describes.
    new(param: string): string;
    
    // Defining properties with unknown or variable names.
    [key: string]: number;
    
    /*
        Getter and Setter.
        usage:
            - `jsonResponse.size`, will automatically call the getter.
            - `jsonResponse.size = 10`, will automatically call the setter.
    */
    get size(): number;
    set size(value: number);
}

You can merge different interface into a type:

interface Entity {
    id: number;
}

interface Person {
    name: string;
    age: number;
}

type PersonEntity = Entity & Person;

Extend it with extends:

interface Entity {
    id: number;
}

interface Person extends Entity {
    name: string;
    age: number;
}

It is also possible to extend via merging when the interfaces have the same name:

interface Person {
    id: number;
}

interface Person {
    name: string;
    age: number;
}

You can ensure a class conforms to an interface via implements.

interface Syncable { ... }
class Account implements Syncable { ... }

Are a feature which allows for describing a value which could be one of a set of possible named constants.

It is NOT a type-level addition, but something added to the language and runtime.

Function Type

A way to declare the Function structure types (parameters and return).

You can generically say a variable is a function.

let f: Function;

Or you can explicitly define it's params and return types.

let f: (param: string) => void;

Union Type

Describes a type which is one of many options. (These options are may be types or values)

Great for reducing and restricting the variable value options.

// `person` is either a string or undefined.
const person: undefined | string;

// In this case `options` can hold only 3 different values, and it cannot be undefined.
let options: 1 | 2 | 3 = 1;

Template Union Types

A template string can be used to combine and manipulate text inside the type system.

type SupportedLangs = 'en' | 'pt';
type FooterLocaleIDs = "header" | "footer";

type AllLocaleIDs = `${SupportedLangs}_${FooterLocaleIDs}_ID`;

Intersection Types

A way to merge/extend types. (&), as seen above.

type PersonEntity = Person & Entity;

Partial

Constructs a type with all properties of Type set to optional. This utility will return a type that represents all subsets of a given type. (So that you dont have to use ?:)

type Todo = {
    priority: number;
    description: string;
    done: boolean;
};

const todo: Partial<Todo> = {
    description: "Todo 1",
    done: false,
};

Required

Constructs a type consisting of all properties of Type set to required.

interface Car {
    brand: string;
    model?: string;
}

// This will raise an error because 'model' is missing, even though it is 'optional'
const car: Required<Car> = {
    brand: "Fiat",
};

Pick

Constructs a type by picking the set of properties Keys (string literal or union of string literals) from Type.

const obj: Pick<Todo, "price" | "obs"> = { price: 0, obs: "" }
type Book = {
    title: string;
    description: string;
    price: number;
}

const book: Book = {
    title: 'Book 1',
    description: 'Some description',
    price: 30
}

// This would raise an error, because updatedProperties dont have all the Book properties
const updateBook = (updatedProperties: Book) => {
    return { ...book, ...updatedProperties };
}

// So you can resolve with picking
const updateBook = (updatedProperties: Pick<Book, "price">) => {
    return { ...book, ...updatedProperties };
}

updateBook({ price: 60 });

Omit

Constructs a type by picking all properties from Type and then removing Keys (String literal or union of string literals).

const updateBook = (updatedProperties: Omit<Book, "title" | "description">) => {
    return { ...book, ...updatedProperties };
}

Conditional Types

Acts as if statements inside the type system. Created via generics, and then commonly used to reduce the number of options in a Type Union.

type HasFourLegs<Animal> = Animal extends { legs: 4 } ? Animal : never;

type Animals = Bird | Dog | Ant | Wolf;
type FourLegs = HasFourLegs<Animals>;

Generic Types

Are types that will hold a generic type, so that this type can change at runtime.

Will be the name inside <>, this name can be anything. (Conventionally it is used as T or R)

// `Response` is the generic type, which is basically not known at compilation time
interface APICall<Response> {
    data: Response
}

// Or

interface APICall<T> {
    data: T
}

You can specify one or more generic types.

interface HTTPRequest<T, A> {
    body?: T;
    headers?: A;
}

You may specify a default type for the generic if nothing was informed. (Otherwise Typescript will require you to specify a type)

// `T` will be required
// `A` is optional and if not informed will use the default type
interface HTTPRequest<T, A = { [key: string]: string }> {
    body?: T;
    headers?: A;
}

You can constrain what types are accepted into the generic parameter with the extends keyword.

// Only types with a `status` property can be used
interface APICall<Response extends { status: number }> {
    data: Response
}

type A = {
    name: string;
}

type B = {
    name: string;
    status: number;
}

const api: APICall<A> = ...; // Will fail
const api: APICall<B> = ...; // Will work

Mapped Types

Acts like a map statement for the type system, allowing an input type to change the structure of the new type.

type Artist = { name: string, bio: string };

// Will loop through each field in the generic `Type`, and instead of the original `Type`
// type it will overwrite with `(newValue: ... ) => void`
type Subscriber<Type> = {
    [Property in keyof Type]: (newValue: Type[Property]) => void
}

type ArtistSub = Subscriber<Artist>;
/*
    {
        name: (newValue: string) => void,
        bio: (newValue: string) => void
    }
*/

Type Assertions

When you have information about the type of a value that Typescript can't know about.

Are removed by the compiler and don't affect the runtime behaviour of the code.

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

Sometimes this rule can be too conservative and will disallow more complex coercions that might be valid.

If this happens you can use two assertions, first to any or unkonwn then to the desired type.

const a = expre as any as T;

Class

Structure

/*
    `extends` tells User is a sub-class of Account. (Only extends ONE class)
    `implements` ensures that User comforms to these `interfaces` or `types`.
*/
class User extends Account implements Updatable, Serializable {
    // The default property being public access, not optional and writable.
    id: string;
    
    // Defines a optional property, which can hold `undefined` or not exist at all.
    middleName?: string;

    /*
        Defines a `trust me, it's there` property.
        (Enforcing to the compiler that this cannot be optional)
    */    
    name!: string;

    /*
        Defines private properties, only accessible to current class.
    */
    private attr: unknown;  // Private only at compilation
    #attr: unknown;         // Private at compilation and runtime
    
    // Defines a property with a default value.
    role = "Student";
    
    /*
       Defines a readonly property.
       Can only be set here at declaration or at the constructor.
    */
    readonly createdAt: Date;

    // See more at `constructor sections` bellow.
    constructor() { ... }
    
    /*
        Describe class methods.
        (Prefer using `arrow function` style to not impact the use of `this`)
        More info bellow.
    */
    setName(name: string) { this.name = name; }
    checkName = (name: string) => { return this.name; }
    
    // Overload method definitions
    sync(): void;
    sync(param: string): string;
    
    // Getter and Setter of properties
    get role() { return this.role.setUpperCase(); }
    set role(value: "Student" | "Teacher") { this.role = value.setLowerCase(); }
    
    /*
        Make `private` or `protected` methods.
        (`protected` are accessible to sub-classes)
    */
    private makeRequest() { ... }
    protected makeRequest() { ... }
    
    // Define `static` properties or methods
    static #userCount = 0;  // static and private
    static registerUser(user: User) { ... }
    
    /*
        Static blocks for setting up static variables.
        (`this` refers to the static class)
    */
    static {
        this.#userCount = -1;
    }
}

constructor section

constructor cannot be overloaded.

You could instead of declaring properties at the top, declare them at constructor's parameters section.

class User {
    // This would be the same as above
    constructor(
        id: string,
        middleName?: string,
        name!: string,
        private attr: unknown,
        role = "Student",
        readonly createdAt: Date
    ) {}
}

Or inside constructor section. But like this, you cannot assign visibility and other modifiers.

These properties would be declared as default ones. (public, not optional and writable)

And you must provide a value.

class User {
    constructor() {
        this.role = "Student";
    }
}

this in classes

The value of this inside a function depends on how the function is called or defined.

It is not garanteed to always be the class instance.

This problem is refered to as (losing this context).

Defining methods with method() {}:

Declaring methods like this may implicate that the this context is lost and refers to another execution context, leading to runtime errors.

This happens on specific occasions like:

  • Passing the class method as Callback or storing it in a variable.

  • Using this in Event Listeners or Callbacks.

// Considering checkName was defined like `checkName() {}` inside the User class
const checkFn = user.checkName;
checkFn();  // Would return undefined
// Considering checkName was defined like `checkName() {}` inside the User class
class User {
    ...
    delayedDisplay() {
        setTimeout(this.checkName, 1000);  // `this` is lost here
    }
}

Defining methods with arrow functions:

Using arrow functions when declaring methods in classes is a good way to enforce that this will refer to the method's Class.

Since they only inherit the this from their enclosing lexical scope.

Declaring methods with arrow functions have a trade off.

Each instance of Person now has its own copy of the method, which increases memory usage, and could have an impact if the class will have a large number of instances.

Binding this with .bind():

An alternative approach is to explicitly bind this using .bind(), allowing you to control this reference without having to use arrow functions.

class User {
    ...
    constructor() {
        this.checkName = this.checkName.bind(this);
    }
    
    checkName(name: string) { return this.name; }
    ...
}

const checkFn = user.checkName;
checkFn();  // Correctly return the name

Abstract classes

When declaring classes as not implementable, but as existing to be subclassed in the type system.

You dont instanciate an absctract class.

Can have absctract methods.

abstract class Item {
    price: number;
    abstract calculateTax(): number;
}

class RealItem extends Item {
    // This pulls from the abstract class
    calculateTax(): number {
        return this.price * 0.1;
    }
}

Difference of Abstract class and Interface

Abstract classes can have partial implementations. You can code inside them.

Interfaces cannot have code in them, they only describe typings and method names (Template Methods).

abstract class Item {
    price: number;
    abstract getTax(): number;
    
    calculateTax() {
	return this.price * this.getTax();
    }
}

class RealItem extends Item {
    getTax(): number {
        return 0.1;
    }
}

Decorators and Attributes

You can use decorators on classes, class methods, accessors, properties and parameters to methods.

import {
    Syncable, triggerSync, preferCache, required
} from "mylib"

@Syncable
class User {
    @triggerSync()
    save() { ... }
    
    @preferCache(false)
    get displayName() { ... }
    
    udpate(@required info: Partial<User>) { ... }
}

Decorators

Ignoring decorators

@ts-ignore: Does not check the next line.

@ts-nocheck: Does not check the entire file.

Read more on .

Primitive Types
Enum
Utility Types
Handbook - The TypeScript Handbooktypescriptlang
Logo
Execution Context