You must know how to use Generics of Promises in TypeScript
Tags: typescript, javascript, programming
Published: 2023-04-27 09:14:02

**Generics** is one of the most difficult parts for TypeScript learners. However, you can not avoid studying it because you must use Generics whenever you use **Promises** in your code.
It's hard to make any websites or web apps without Promises. Therefore, it is a good time to learn how to use Generics of Promises if you are unfamiliar with it!

## Creating Promise with the Promise class
First, create a simple Promise that returns the "message" object.
```ts
const simplePromise = new Promise((resolve) => {
    resolve({message: 'success!'});
});
```
Next, execute this promise to see the result.
```ts
(async() => {
    const result = await simplePromise;
    console.log(result.message);
})();
```
A success message is displayed on the console.
Okay, this code can just work. But there is a problem. Let's take a look into the type error message.
```ts
(async() => {
    const result = await simplePromise;
    // Type -> const result: unknown
    console.log(result.message);
    // Type error -> 'result' is of type 'unknown'.
})();
```
Since we did not provide a type argument to the Promise class, the type of the Promise was `Promise<unknown>`. That is why the type checker could not predict the type of the value given by `resolve`. As a result, it became `unknown` and the type error occurred when accessing a message property.
It's very troublesome if we leave the return value as `unknown`. Therefore, we need to provide a type argument when creating a new Promise. Here is an updated code:
```ts
const simplePromise = new Promise<{message: string}>((resolve) => {
    resolve({message: 'success!'});
});

(async() => {
    const result = await simplePromise;
    // Type -> const result: { message: string; }
    console.log(result.message);
    // OK
})();
```
Perfect! In this example code, the type checker could successfully predict the type of the result variable.

## Async Functions
The type of a returned value from an **async function** also will be a **Promise**. Thus, we have to be more aware of type arguments and type annotations when working on async functions.
To examine the type of a returned value from async functions, let's make a simple fetch function and call it.
```ts
async function getRandomCatFact() {
  const response = await fetch('https://catfact.ninja/fact');
  return response.json();
}

(async() => {
    const cat = await getRandomCatFact();
    console.log(cat.fact);
})();
// [LOG]: "At 4 weeks, it is important to play with kittens so that they do not develope a fear of people." 
```
Okay, this code worked and displayed the interesting fact about cats on the console. But, if it works, what's wrong?
Let's take a look at the types of the returned value.
```ts
async function getRandomCatFact() {
  const response = await fetch('https://catfact.ninja/fact');
  return response.json();
}
// Type -> function getRandomCatFact(): Promise<any>

(async() => {
    const cat = await getRandomCatFact();
    // Type -> const cat: any
    console.log(cat.fact);
})();
```
Since no type argument was provided, the type of the return value from the function became `Promise<any>`. Thus, the type of the `cat` variable also became any. So, this code has a potential risk of a runtime error if a developer makes a typo like below:
```ts
console.log(cat.typo);
```
The type checker cannot catch even such a silly typo if the type of the variable is `any`.
To solve the issue, we have to update the function so that we can pass a proper type to it. Before doing it, let's declare an interface for the API response.
```ts
interface CatFact {
  fact: string;
  length: number;
}
```
And then, update the function.
```ts
async function getRandomCatFact(): Promise<CatFact> {
  const response = await fetch('https://catfact.ninja/fact');
  return response.json();
}
```
Thanks to the type annotation, the TypeScript type checker knows what type of value will be returned from this function. Therefore, we can see a type error if we make a typo like below:
```ts
(async() => {
    const cat = await getRandomCatFact();
    console.log(cat.typo);
    // Type error -> Property 'typo' does not exist on type 'CatFact'.
})();
```
Perfect! Now, you can use **Promise** and **Async** properly in your TypeScript project.

## Conclusion
In conclusion, understanding and implementing Generics with Promises is vital for TypeScript learners, as they are crucial in web development. By providing type arguments in Promises and using type annotations in async functions, developers can improve code quality, minimize runtime errors, and benefit from the type checker's ability to catch potential issues, ensuring a stable and reliable TypeScript project.