TypeScript tip of the week - generics
Tags: typescript, javascript, node
Published: 2019-11-05 17:34:32

---
title: TypeScript tip of the week - generics
published: true
description: The first-ever TypeScript tip of the week! Make better use of generics.
tags: TypeScript, JavaScript, NodeJS
---

Happy Tuesday and thanks for reading the first ever TypeScript tip of the week. I'm starting this series helpful TypeScript insights and tips as a way to get more people interested in using TypeScript. 

This week I wanted to talk about TypeScript generics. With an incredible type system backing TypeScript, generics give you the power to easily reuse logic and give you the ability to dynamically define what type of data you're expecting back from that logic. Let's look at an example.

```typescript
function foo<T>(arg: T): T {
    return arg;
}


const bar = foo<string>('baz');
```

This is a super basic example, but what happens if we want to perform an operation on this?

```typescript
function foo<T>(arg: T): T {
    console.log(arg.length);
    return arg;
}
```

There are many types that have a `.length` attribute, but if we try to use this new code, we'll get an error because there are some types that do not have that property. To solve this problem we can extend these types. 


```typescript
function foo<T extends string>(arg: T): T {
    console.log(arg.length);
    return arg;
}
```

This will work, however, it kind of defeats the purpose a little because `T` could be an array or an object with a length property on it. Using a custom interface, we can zero in on the property that we need to extend with our generic type.

```typescript
interface GenericLengthType {
    length: number;
}

function foo<T extends GenericLengthType>(arg: T): T {
   console.log(arg.length);
   return arg;
}

const a = foo(['foo', 'bar', 'baz']);

const b = foo('bar');

const c = foo({length: 10});

const d = foo(10); // error number doesn't have property length
```

This is all well and good, but let's look at a more practical example, one I use often when writing TypeScript code.

```typescript
function TryWrapper<T extends Function>(fn: T): T {
  return <any>function() {
    try {
      return fn(...arguments);
    } catch (e) {
      // handle error gracefully
    }
  };
}

const test = TryWrapper((arg: string) => {
  if (arg.length < 5) {
    throw new Error("Argument length must be longer than 5");
  }
  return arg.split("");
});
```

It may seem odd that we're using type `any` on the returned function, however, if we don't put anything there we will get this error

```
Type '(args: any) => any' is not assignable to type 'T'.
'(args: any) => any' is assignable to the constraint of type 'T',
but 'T' could be instantiated with a different subtype of constraint 'Function'.
```

When we leave out that any it's basically trying to assign that type to T, which it's telling us is invalid. We also use the arguments keyword to grab all the arguments passed into the function. We then use the spread operator to make sure that the arguments are passed into the wrapped function. The reason we do this is that a wrapped function may have several arguments passed into it and we want to make sure to preserve them.

Here is a live example on stackblitz

{% stackblitz typescript-tip-generics %}

Thanks for reading the first TypeScript tip of the week! Tell me what you think in the comments.