Understanding Generics in TypeScript

Quick note! If you’d like to experience this post interactively, go to https://codeamigo.dev/lessons/151

Introduction

Sometimes when I’m learning a new paradigm, it’s the seemingly simplest things that can trip me up. I often overlook cer…


This content originally appeared on DEV Community and was authored by Philip London

Quick note! If you'd like to experience this post interactively, go to https://codeamigo.dev/lessons/151

Introduction

Sometimes when I'm learning a new paradigm, it's the seemingly simplest things that can trip me up. I often overlook certain concepts because they seem tricky at first.

TypeScript Generics is one of those concepts.

Let's take the example below:

interface Lengthwise {
  length: number;
}

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

console.log(loggingIdentity(['hello world']))

If you're like me you might be asking:

  1. What exactly is T here?
  2. Why is T used, is that arbitrary?
  3. Why can't I just write loggingIdentity(arg: Lengthwise)?
  4. What does mean?

What is ?

<T>. T tells TypeScript that this is the type that is going to be declared at run time instead of compile time. It is TypeScript's Generic Declaration.

interface Lengthwise {
  length: number;
}

function logSomething<T>(arg: T): T {
  console.log(arg);
  return arg;
}

logSomething<string>('hello world')
logSomething<Array<number>>([1])

logSomething<string> tells TypeScript: the argument you receive will be a string, and the return type of the function will also be a string.

Why is used?

Whether you use , , , or . It's all arbitrary.

We see the use of a lot because that is how the original TypeScript documentation defined it. However, the docs have now replaced declarations using with . So It's up to you :)

How Are Generics Useful?

At this point you may be wondering, "Why should I even use Generics?"

Well let's say you wanted have a type-safe log function similar to logSomething, for both numbers and strings.

function logString(arg: string) {
  console.log(arg);
}

function logNumber(arg: number) {
  console.log(arg)
}

Obviously we can do better, is there another approach we could use besides Generics?

Union Types vs Generics

If you were thinking about Union Types that's a pretty good idea. But it's got some limitations!

Let's say we wanted to use the return value of our function that accepts a string | number Union Type as its arg.

// function logString(arg: string) {
//   console.log(arg);
// }

// function logNumber(arg: number) {
//   console.log(arg)
// }

function returnStringOrNumber(arg: string | number) {
  return arg
}

const myVal = returnStringOrNumber(123)
const myOtherVal = returnStringOrNumber('hello')

myVal + 1 // <= Operator '+' cannot be applied to types 'string | number' and 'number'.

Union types limit the return type of our function.

With Generics, we can tell TypeScript definitively that myVal is a number, not a string OR a number!

function returnSomething<T>(arg: T): T {
  return arg
}

const myVal = returnSomething(123)
const myOtherVal = returnSomething('hello')

myVal + 1 // 👍👍 All good!

Overloads

Ok, well what about function overloading you may be asking.

Check out the code to the below. Sure, that works too, but I'll leave it up to you to decide which you'd rather implement.

// GENERICS
// function returnSomething<T>(arg: T): T {
//   return arg
// }

// OVERLOADING
function returnSomething(arg: number): number;
function returnSomething(arg: string): string
function returnSomething(arg: number | string) { return arg }

const myVal = returnSomething(123)
const myOtherVal = returnSomething('hello')

myVal + 1

<T Extends...

Cool, I feel like you're starting to get it. So let's through a wrench in this whole thing.

Generics aren't perfect either. We need to understand their "constraints", by adding some constraints ;)

function getLength<T>(args: T) : number {
  return args.length;
}

The above function will cause TypeScript to complain because we need to tell TypeScript that T extends the appropriate type and it's safe to call .length!

interface ThingWithLength {
  length: number
}

function getLength<T extends ThingWithLength>(args: T) : number {
  return args.length; // 😅 All good now!
}

Future reading

Thanks for following along! If you enjoyed that please check https://codeamigo.dev for interactive tutorials!


This content originally appeared on DEV Community and was authored by Philip London


Print Share Comment Cite Upload Translate Updates
APA

Philip London | Sciencx (2021-11-10T01:15:04+00:00) Understanding Generics in TypeScript. Retrieved from https://www.scien.cx/2021/11/10/understanding-generics-in-typescript/

MLA
" » Understanding Generics in TypeScript." Philip London | Sciencx - Wednesday November 10, 2021, https://www.scien.cx/2021/11/10/understanding-generics-in-typescript/
HARVARD
Philip London | Sciencx Wednesday November 10, 2021 » Understanding Generics in TypeScript., viewed ,<https://www.scien.cx/2021/11/10/understanding-generics-in-typescript/>
VANCOUVER
Philip London | Sciencx - » Understanding Generics in TypeScript. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/11/10/understanding-generics-in-typescript/
CHICAGO
" » Understanding Generics in TypeScript." Philip London | Sciencx - Accessed . https://www.scien.cx/2021/11/10/understanding-generics-in-typescript/
IEEE
" » Understanding Generics in TypeScript." Philip London | Sciencx [Online]. Available: https://www.scien.cx/2021/11/10/understanding-generics-in-typescript/. [Accessed: ]
rf:citation
» Understanding Generics in TypeScript | Philip London | Sciencx | https://www.scien.cx/2021/11/10/understanding-generics-in-typescript/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.