Advanced Static Types in TypeScript

This course explores the capabilities of TypeScript’s type system and shows how to use advanced static types in practice.

Watch the Course

Typing Functions in TypeScript

In TypeScript, there are multiple syntaxes for declaring the type of a function:

  1. Method signatures
  2. Function type literals
  3. Object type literals with call/construct signatures

Here's a quick run-down of all three variants.

Method Signatures

The method signature syntax is probably the most straightforward to use. When defining an object type, its methods can easily be described by providing signatures as follows:

interface Date {
    toString(): string;
    setTime(time: number): number;
    // ...
}

Note how the method signature syntax closely mirrors the shorthand method syntax, which is used to concisely define methods in object literals or ES2015 classes:

class Date {
    // ...
    
    toString(): string {
        // ...
    }
    
    setTime(time: number): number {
        // ...
    }
}

Function Type Literals

Function type literals are another way to declare the type of a function. They're typically used in the signature of a higher-order function, that is, a function which accepts functions as parameters or which returns a function:

interface Array<T> {
    sort(compareFn?: (a: T, b: T) => number): this;
    // ...
}

Perhaps surprisingly, the parameter names are always required within a function type literal. You can't leave out the parameter name and only specify the type. Here's how TypeScript sees a function type literal if you leave out the colon:

type FunctionType1 = (x: string, y: number) => number;
// (x: string, y: number) => number

type FunctionType2 = (string, number) => number;
// (string: any, number: any) => number

In the definition of the FunctionType2 type, string and number aren't interpreted as types, but as parameter names. They are implicitly typed as any because there's no explicit type annotation (and no information for contextual typing).

Object Type Literals with Call or Construct Signatures

In JavaScript, functions are nothing but special objects than can be called. This fact is reflected in the syntax of object type literals: they describe the shape of an object, which also happens to have a call signature:

interface RegExpConstructor {
    // Call signatures
    (pattern: RegExp): RegExp;
    (pattern: string, flags?: string): RegExp;
    
    // ...
}

Similar to call signatures, an object type literal can also contain construct signatures, in which case it is said to be a constructor type. The construct signature of a function defines its parameter list and return type when it's called with the new operator. Construct signatures look almost identical to call signatures, except that they are additionally prefixed with the new keyword:

interface RegExpConstructor {
    // Call signatures
    (pattern: RegExp): RegExp;
    (pattern: string, flags?: string): RegExp;
    
    // Construct signatures
    new (pattern: RegExp): RegExp;
    new (pattern: string, flags?: string): RegExp;
    
    // ...
}

Depending on how the RegExp type of the JavaScript standard library is used, either the call or construct signature applies. In this case, they're identical, but they wouldn't have to be:

// Using the call signature
const digitsPattern1 = RegExp("^\d+$");

// Using the construct signature
const digitsPattern2 = new RegExp("^\d+$");

Learn Node