# Functions in TypeScript

Source: https://tpiros.dev/blog/functions-in-typescript

Functions in TypeScript work exactly like [functions in ES2015](https://fullstack-developer.academy/functions-in-javascript-es2015/). Fat arrow syntax, default parameters, the spread operator. All there.

But TypeScript is a superset of JavaScript. It extends ES2015 with data types and interfaces.

Combine those features and you get something genuinely useful: code that tells your IDE exactly what's going on. You can define argument types for your function and specify what datatype it returns. If it returns nothing, slap on the `void` keyword:

```typescript
function add(n: number, m: number): number {
  return n + m;
}

function fight(weapon: string): void {
  // no return
  console.log(`Warrior fights with ${weapon}`);
}
```

You can also type rest parameters. This is ideal for numerical operations where you want to guarantee every argument is a number:

```typescript
function add(...numbers: number[]): number {
  return numbers.reduce((acc, number) => acc + number);
}

add(1, 2);
add('1', 2); // Argument of type '"1"' is not assignable to parameter of type 'number'
```

## Interfaces and arrow functions

Now let's throw interfaces and arrow functions into the mix:

```typescript
interface IWarrior {
  name: string;
  weapon: string[];
  health: number;
  pickWeapon(this: IWarrior): () => Weapon;
}

interface Weapon {
  name: string;
  hitPower: number;
}

const warrior: IWarrior = {
  name: 'Dave the Nomad',
  weapon: ['sword', 'knife', 'epic axe', 'spear'],
  health: 100,
  // NOTE: The function now explicitly specifies that its callee must be of type Deck
  pickWeapon: function (this: IWarrior) {
    return () => {
      const warriorWeapon = Math.floor(Math.random() * this.weapon.length);
      const weaponHitPower = Math.floor(Math.random() * 100);
      return { name: this.weapon[warriorWeapon], hitPower: weaponHitPower };
    };
  },
};

const myWarrior = warrior;
const weaponChoice = myWarrior.pickWeapon();
const weapon = weaponChoice();
console.log(`My warrior is ${myWarrior.name}. (Health: ${myWarrior.health})`);
console.log(
  `Randomly selected weapon: ${weapon.name} with hit power ${weapon.hitPower}.`
);
```

Picture a game where you create warriors and randomly assign each one a weapon with a hit power.

We set up two interfaces: one for the warrior, one for the weapon. Notice how `pickWeapon()` takes `this` as a parameter typed to `IWarrior`. That forces `this` to be `IWarrior` instead of `any`, which means the compiler catches type mismatches.

The `pickWeapon()` method defined in `IWarrior` also requires us to return something matching our `Weapon` interface.

We then build our warrior object: a `name`, a `health` value, and an array of `weapons`. To fulfil the `IWarrior` interface contract, we add a `pickWeapon()` method that randomly selects an index from the weapon array, generates a random hit power value, and returns an object with the weapon's name and hitPower (satisfying the `Weapon` interface).

A few things to notice here. We're using interfaces and datatypes throughout. And by using the fat arrow syntax (available in ES2015 too), we can safely use `this` inside another function. The `this` keyword (excuse the pun) gets fetched lexically. No need for `.bind()`, `.apply()`, or that old trick of assigning `this` to another variable.
