Happy Monday 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.

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?

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.

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.

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.

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 that any out it’s basically trying to assign that type to T, which it’s telling us is invalid. We are also using the arguments keyword to grab all the arguments passed into the function. 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.

const add = TryWrapper((a: number, b: number) => a + b);

Here is a live example on stackblitz

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