How to apply type annotations to functions in TypeScript
Tags: typescript, javascript
Published: 2021-02-21 07:04:11

---
title: How to apply type annotations to functions in TypeScript
published: true
description: This post explores the various ways of ascribing functions with type annotations in TypeScript
tags: TypeScript, JavaScript
canonical_url: https://www.geekabyte.io/2021/01/how-to-apply-type-annotations-to.html
//cover_image: https://direct_url_to_image.jpg
---

This post explores the various ways of ascribing functions with type annotations in TypeScript. To fully appreciate the various ways of ascribing type annotations to functions, we would first take a quick look at the various ways functions can be defined in JavaScript. Once that is done, we then look at how to bring TypeScript's static type annotation into the picture. 

There are mainly two ways of defining functions in JavaScript. Namely: 


*   Function expressions 
*   Function declarations. 


Let’s quickly go over how these two work.

### Function expressions

JavaScript allows creating functions by defining them as expressions that can be assigned to a variable. Defining such function expressions can make use of the `function` keyword or via the use of Arrow functions. 

For example, defining an `add` function can take either of these forms:

Using the `function` keyword:  

```javascript
let add = function(a,b) { return a + b }
```
Using Arrow functions, It takes the form:  

```javascript
let add = (a,b) => { return a + b }
```

Functions defined in either of these ways can be called like any other function:
 
```javascript
console.log(add(1,2)) // logs 3 
```

### Function declaration

Using function declaration is the regular way of defining a function that everyone knows.  

It looks like this:  

```javascript
function add(a,b) {
   return a + b;
}
```
The above is a declaration, _function declaration_ that creates an identifier, in this case, `add`, that can be later used as a function. The created `add` identifier can be called as follows:  

```javascript
console.log(add(1,2)) // logs 3
```

Now before we go ahead to see how to apply TypeScript’s type annotations, another important thing to be aware of, is the fact that **functions in JavaScript are also objects!**  

Yes, a function in JavaScript is a JavaScript object.  

This means the function `add` created above, either via function declaration or function expression, is also an object and can have properties assigned to it like any other object in JavaScript.  

That is:  

```javascript
add.serial = "A01"
console.log(add.serial) // logs A01
```

For a more in-depth take on functions being objects, see [Understanding Constructor Function and this Keyword in Javascript](https://www.geekabyte.io/2013/03/understanding-constructor-function-and.html).

Knowing that functions are also objects is important, as this also influences the syntax that can be used for type annotation in TypeScript. We will see how later in the post. 

Now that we have covered the basics, let’s now go into applying type annotations to functions. 

We will start with function expressions.  

### Adding Type Annotations to Function Expressions

Adding type annotations to function expressions is straight forward because the definition is an expression that is assigned to a variable. 

This makes it obvious where to place the type annotation, which is right after the variable declaration.

Knowing this, adding type annotation to the `add` function will look like this:

```typescript
let add: (a: number, b: number) => number = (a,b) => { 
   return a + b 
}
```
Notice that the actual function definition remains unchained. That is _(a,b) => { return a + b }_ remains the way it was in the JavaScript version, and no type annotations are added to the function parameters, but TypeScript is able to infer the types based on type annotation ascribed to the variable. 

That being said, it is also possible to update the function definition to have type annotations. That is:  

```typescript
let add: (a: number, b: number) => number = 
         (a:number,b:number):number => { return a + b }
```

In which case, the type annotation placed after the variable becomes redundant and can be removed, which leads to another way of typing the function expressions. This can be seen below:

```typescript
let add = (a: number, b:number): number => { return a + b }
```

**This demonstrates an important point. Which is, when typing functions in TypeScript, there are two ways to go about it. One way is to ascribe type annotations to the function parameters and return type. The other way is to ascribe annotation to the variable that holds the function**. 

Ascribing type annotations to variables is possible when using function expression since function expression are assigned to variables. Function expression can also choose to have parameters in their definition annotated, although often time this is not needed.

Another important point is when ascribing types to variable holding functions, the syntax used resembles how Arrow functions are used, that is it makes use of "**=>**". And this is the only way to annotate variables with functions.  

For example, this is correct:  

```typescript
let add: (a: number, b: number) => number = 
         (a,b):number => { return a + b }
```

While this leads to a syntax error:  

```typescript
let add: (a: number, b: number): number = 
             (a,b):number => { return a + b }
```

### Adding Type Annotations to Function Declaration

The `add` JavaScript function defined via function declaration:  

```javascript
function add(a,b) {
   return a + b;
}
```

With TypeScripts type annotations applied becomes  

```typescript
function add(a: number, b: number): number {
   return a + b;
}
```

Since there is no other way the TypeScript compiler can infer the types of the function parameters, the type annotation has to be supplied.

One might ask, what then is the type of the add function?

For example given a variable name defined as:  

```typescript
let name: string = “John Doe”;
```

When the question is asked, what is the type of `name` it is easy to see it is `string`. 

When the same question is asked for the `add` function defined using function expressions, that is  

```typescript
let add: (a: number, b: number) => number = 
         (a,b) => { return a + b }
```

It is easy to respond that the type is `(a: number, b: number) => number`  

But what about the `add` function defined using function declaration?

To help answer this question we can use the combination of an IDE and the `typeof` operator of TypeScript. The `typeof` operator when used in the type signature context can help extract the type of a value. 

So to answer the question, what is the type of the add function defined using function declaration, we use `typeof` on add, in the type signature context and using a tool that offers _IntelliSense_, in this case, the TypeScript playground, we can see what the type is: 

<span id="docs-internal-guid-acfc7784-7fff-4f7c-d71e-81412c140a0d"><span style="font-family: Arial; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;"><span style="border: none; display: inline-block; height: 115px; overflow: hidden; width: 441px;">![](https://lh4.googleusercontent.com/_vxiOiLSQb8bLs-7FrB4-7OE1eW_Nm4qP0pUPDplJrtxlkrOI1LObS9sh7kWelnrgG86qsjA6ya2-M4QtVOql032potmxX2vtHhUl-aVhY8Z1WXHBQOZe7X-3z1N8QZwMK-Tp1fw)</span></span></span>

And as can be seen above, the type of `add` when defined using function declaration is `(a: number, b: number) => number`, which is exactly the same type annotation of the same function when defined using the function expression!

### Typing functions using Call signature of an object literal type

Remember we mentioned that functions are also objects. And we showed how we can add properties to functions as we do to objects. Well, functions being objects also provide us another way of supplying type information about functions. 

A question a curious reader might ask upon being told that functions are objects in JavaScript is this: if functions are objects, how come we can call them? How come functions can be called by appending _()_ to the end of the function? That is something like `functionName()`. 

The answer to that question is in realising that the syntax `functionName()` is really a syntactic sugar for either `functionName.call()` or `functionName.apply()`. That is, calling a function, is really nothing but assessing the `apply` or `call` property of the object representing that function. 

See MDN entries for [Function.prototype.apply()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) and [Function.prototype.call()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call) for more information. 

This knowledge helps in understanding another way of typing functions, which is using the call signature. Doing that builds on how object literal can be used to specify types. 

For example to provide type annotation describing an object with a property `name` of type string, and property `age`, of type number, the following interface can be created and used:  

```typescript
interface Person {
  name: string
  age: number
  greet(): string
}

let john: Person = {
  name: "John Doe",
  age: 20,
  greet() {
    return “hello world”
   }
} 
```

The type annotation outlines the property name together with a type annotation.  

Knowing this, and knowing that functions are also objects, that can be called via a `call`, or `apply` property then we can provide a type annotation to our `add` function as shown below:

```typescript
 interface Adder {
  apply(a: number, b: number): number
  call(a: number, b: number): number
 }
```

And we can then use this `Adder` in typing the `add` function. This looks like this:

```typescript
let add: Adder = (a: number, b: number) => { return a + b }
```

Note that even though this type checks, confirming that the created `Adder` type can be used to annotate our `add` function, we still have to annotate the type parameters, because TypeScript is unable to infer their types from the provided `Adder` annotation used for the variable. 

We can go one step further in the definition of `Adder` by removing the need to specify `apply` and `call` explicitly. This is because as we already know that a function (which is an object) can have its `apply` and `call` property called without having to explicitly specify them. That is, the call signature of a function is a syntactic sugar that will expand to explicitly use either `apply` or `call`. We can apply this knowledge of the call signature to the type definition by removing the `apply` and `call`. Doing that we end up with:  

```typescript
interface Adder {
  (a: number, b: number): number
}
```

This way of providing type annotations to functions is usually referred to as using the call signature of an object literal type.

It is worth noting that in TypeScript, the keyword `type` and `interface` are interchangeable in most cases hence, the above can also be defined using `type` instead of `interface`.


### Summary

These conclude the overview of how functions can be typed in TypeScript. A quick outline of the key points is listed below:  

1.  <span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;">The way functions are typed in TypeScript depends on the ways functions can be created in JavaScript.</span>
2.  <span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;">Functions can be created either via function declaration or function expressions. </span>
3.  <span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;">They are two main ways to ascribe type annotations to functions. T**yping the parameters and return type of the function**, or **typing the variable that holds the function**. </span>
4.  <span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;">Functions defined using function declaration can only be typed by providing type annotation to parameters and return value. A function expression can be typed by providing the type annotation to the variable that holds the function expression. Also, it is possible to ascribe type to the function parameters defined in the function expression, this is usually redundant. It is only needed in cases where the compiler cannot infer their types based on the type annotation ascribed to the variable.</span>
5.  <span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;">When typing a variable that holds a function, the type annotation makes use of _=>_ to specify the return type. Using this Arrow function style is the only way to type variables holding function expressions.</span>
6.  <span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;">Also, Functions are just objects! And this influences the third way of typing functions which is called: _call signature_ using object literals.</span>