Advanced TypeScript Exercises - Answer 1
Tags: typescript, javascript
Published: 2020-02-07 10:24:23

--- series: Advanced TypeScript Exercises ---

The Question I've asked was:
> If we have a type which is wrapped type like `Promise`. How we can get a type which is inside the wrapped type? For example if we have `Promise<ExampleType>` how to get `ExampleType`?

The answer
```ts
type Transform<A> = A extends Promise<infer Inner> ? Inner : never
type Result = Transform<Promise<string>> // Result is string type
```
In order to unwrap the promise type we have used [`infer` keyword](https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-inference-in-conditional-types).

The keyword is helpful with any type constructor, type constructor is a type parameterized by another type variable, so any type which has generic placeholder like `A<B>`, where `A` is type constructor parametrized by `B`.

### Examples of using `infer`

We can use `infer` also with other types, consider example with Array

```ts
type InsideArray<A> = A extends Array<infer Inside> ? Inside : never
type Str = InsideArray<Array<string>>; // Str is string
```

What about custom parameterized types? Yes will do!
```ts
type Surprise<A> = { inside: A }
type UnpackSurprise<S> = S extends Surprise<infer Inside> ? Inside : never
type Num = UnpackSurprise<Surprise<number>> // Num is number
```

We can even use `infer` to get mapped types properties
```ts
type User = {
    id: number,
    name: string,
}

type Doc = {
    id: string,
}

type GetProperty<T, Prop extends keyof T> = T extends { [K in Prop]: infer Value } ? Value : never

type UserId = GetProperty<User, 'id'>
type DocId = GetProperty<Doc, 'id'>
```
> **Question** How we can get type of Mapped type property in simpler way? Please write your answer in the comment!

Can we use many type variables and infer them? Sure we can!
```ts
type ABC<A, B, C> = { a: A, b: B, c: C }
type ABCIntoTuple<T> 
  = T extends ABC<infer A, infer B, infer C> ? [A, B, C] : never
type Example = ABC<string, boolean, number>
type ExampleTuple = ABCIntoTuple<Example> // [string, boolean, number]
```
In above example we infer all three type parameters and we put them into 3-n tuple.

### Why `never`?

Type `never` is a [bottom type](https://en.wikipedia.org/wiki/Bottom_type), it is a type without any value, it is very handy construct for saying that our function doesn't return, or some path of the code is not reachable, more about it you can read in [great article](https://mariusschulz.com/blog/the-never-type-in-typescript) from Marius Schulz.

We use `never` in conditional in order to represent the unhappy path, we are saying that it is a dead end, if you don't pass to our constructor specific type, we just don't have any alternative, our type doesn't work with anything else. Consider how it will behave when we pass to it something which does not match the condition:

```ts
type Transform<A> = A extends Promise<infer Inner> ? Inner : never
type OhGosh = Transform<string> // OhGosh evaluates to never
```
We could be having different representation of the negative path, but never is the best choice, as further type transformation will be useless. We can also set constraint at the argument, and in that way `never` path will never be reached. 

Consider following change:
```ts
type Transform<A extends Promise<any>> = A extends Promise<infer Inner> ? Inner : never
type OhGosh = Transform<string> // compilation error
```
After `A extends Promise<any>` our utility type `Transform` is now bullet proof, as compilation will fail for types which do not extend `Promise<any>`.

> Why did I use `Promise<any>`?

I have put `any` inside `Promise` because `any` is one of unsound types which are also types which can be assigned to everything, it means that every type extend from `any`, what determines that every `Promise` kind of type will extend `Promise<any>`

**This series is just starting**. If you want to know about new exciting questions from advanced TypeScript please follow me on [dev.to](https://dev.to/macsikora) and [twitter](https://twitter.com/macsikora).