Skip to content

Commit

Permalink
[style]: apply --prose-wrap to en solutions
Browse files Browse the repository at this point in the history
  • Loading branch information
ghaiklor committed Jul 13, 2022
1 parent d81316f commit 191b018
Show file tree
Hide file tree
Showing 69 changed files with 1,317 additions and 1,051 deletions.
63 changes: 33 additions & 30 deletions en/easy-awaited.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,56 +8,59 @@ tags: promise

## Challenge

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`?
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`?

## Solution

Pretty interesting challenge that requires us to know about one of the underrated TypeScript features, IMHO.
Pretty interesting challenge that requires us to know about one of the
underrated TypeScript features, IMHO.

But, before explaining what I mean, let us analyze the challenge.
The author asks us to unwrap the type.
What is unwrap?
Unwrap is extracting the inner type from another type.
But, before explaining what I mean, let us analyze the challenge. The author
asks us to unwrap the type. What is unwrap? Unwrap is extracting the inner type
from another type.

Let me explain it with an example.
If you have a type `Promise<string>`, unwrapping the `Promise` type will result into type `string`.
We got the inner type from the outer type.
Let me explain it with an example. If you have a type `Promise<string>`,
unwrapping the `Promise` type will result into type `string`. We got the inner
type from the outer type.

Note that you also need to unwrap the type recursively.
For example, if you have type `Promise<Promise<string>>`, you need to return type `string`.
Note that you also need to unwrap the type recursively. For example, if you have
type `Promise<Promise<string>>`, you need to return type `string`.

Now, to the challenge.
I’ll start with the simplest case.
If our `Awaited` type gets `Promise<string>`, we need to return the `string`, otherwise we return the `T` itself, because it is not a Promise:
Now, to the challenge. I’ll start with the simplest case. If our `Awaited` type
gets `Promise<string>`, we need to return the `string`, otherwise we return the
`T` itself, because it is not a Promise:

```ts
type Awaited<T> = T extends Promise<string> ? string : T;
```

But there is a problem.
That way, we can handle only strings in `Promise` while we need to handle any case.
So how to do that?
How to get the type from `Promise` if we don’t know what is there?
But there is a problem. That way, we can handle only strings in `Promise` while
we need to handle any case. So how to do that? How to get the type from
`Promise` if we don’t know what is there?

For these purposes, TypeScript has type inference in conditional types!
You can say to the compiler “hey, once you know what the type is, assign it to my type parameter, please”.
You can read more about [type inference in conditional types here](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#type-inference-in-conditional-types).
For these purposes, TypeScript has type inference in conditional types! You can
say to the compiler “hey, once you know what the type is, assign it to my type
parameter, please”. You can read more about
[type inference in conditional types here](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html#type-inference-in-conditional-types).

Knowing about type inference, we can update our solution.
Instead of checking for `Promise<string>` in our conditional type, we replace a `string` with `infer R`, because we don’t know what must be there.
The only thing we know is that it is a `Promise<T>` with some type inside.
Knowing about type inference, we can update our solution. Instead of checking
for `Promise<string>` in our conditional type, we replace a `string` with
`infer R`, because we don’t know what must be there. The only thing we know is
that it is a `Promise<T>` with some type inside.

Once the TypeScript figures out the type inside the `Promise`, it will assign it to our type parameter `R` and becomes available in “true” branch.
Exactly where we return it:
Once the TypeScript figures out the type inside the `Promise`, it will assign it
to our type parameter `R` and becomes available in “true” branch. Exactly where
we return it:

```ts
type Awaited<T> = T extends Promise<infer R> ? R : T;
```

We are almost done, but yet from type `Promise<Promise<string>>` we get type `Promise<string>`.
So, we need to repeat the same process recursively, which is achieved by using `Awaited` type itself:
We are almost done, but yet from type `Promise<Promise<string>>` we get type
`Promise<string>`. So, we need to repeat the same process recursively, which is
achieved by using `Awaited` type itself:

```ts
type Awaited<T> = T extends Promise<infer R> ? Awaited<R> : T;
Expand Down
24 changes: 13 additions & 11 deletions en/easy-concat.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ tags: array

## Challenge

Implement the JavaScript `Array.concat` function in the type system.
A type takes the two arguments.
The output should be a new array that includes inputs in ltr order.
Implement the JavaScript `Array.concat` function in the type system. A type
takes the two arguments. The output should be a new array that includes inputs
in ltr order.

For example:

Expand All @@ -20,9 +20,9 @@ type Result = Concat<[1], [2]>; // expected to be [1, 2]

## Solution

When working with arrays in TypeScript, pretty often Variadic Tuple Types are coming to the rescue in certain situations.
They allow us to make generic spreads.
I’ll try to explain, hold on.
When working with arrays in TypeScript, pretty often Variadic Tuple Types are
coming to the rescue in certain situations. They allow us to make generic
spreads. I’ll try to explain, hold on.

Let me show you the implementation of concatenating two arrays in JavaScript:

Expand All @@ -32,12 +32,14 @@ function concat(arr1, arr2) {
}
```

We can use spread operators and just take everything from the `arr1` and paste it into another array.
We can apply the same to the `arr2`.
The key idea here is that it iterates over the elements in array\tuple and pastes them in the place where spread operator is used.
We can use spread operators and just take everything from the `arr1` and paste
it into another array. We can apply the same to the `arr2`. The key idea here is
that it iterates over the elements in array\tuple and pastes them in the place
where spread operator is used.

Variadic Tuple Types allows us to model the same behavior in the type system.
If we want to concatenate two generic arrays, we can return the new array where both arrays are behind the spread operator:
Variadic Tuple Types allows us to model the same behavior in the type system. If
we want to concatenate two generic arrays, we can return the new array where
both arrays are behind the spread operator:

```ts
type Concat<T, U> = [...T, ...U];
Expand Down
17 changes: 10 additions & 7 deletions en/easy-exclude.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ tags: built-in

## Challenge

Implement the built-in `Exclude<T, U>`.
Exclude from `T` those types that are assignable to `U`.
For example:
Implement the built-in `Exclude<T, U>`. Exclude from `T` those types that are
assignable to `U`. For example:

```ts
type T0 = Exclude<"a" | "b" | "c", "a">; // expected "b" | "c"
Expand All @@ -19,12 +18,16 @@ type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // expected "c"

## Solution

The important detail here is a knowledge that conditional types in TypeScript are [distributive](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types).
The important detail here is a knowledge that conditional types in TypeScript
are
[distributive](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types).

So that when you are writing the construct `T extends U` where `T` is the union, actually what is happening is TypeScript iterates over the union `T` and applies the condition to each element.
So that when you are writing the construct `T extends U` where `T` is the union,
actually what is happening is TypeScript iterates over the union `T` and applies
the condition to each element.

Therefore, the solution is pretty straightforward.
We check that `T` can be assigned to `U` and if so; we skip it:
Therefore, the solution is pretty straightforward. We check that `T` can be
assigned to `U` and if so; we skip it:

```ts
type MyExclude<T, U> = T extends U ? never : T;
Expand Down
21 changes: 13 additions & 8 deletions en/easy-first.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ tags: array

## Challenge

Implement a generic `First<T>` that takes an Array `T` and returns it's first element's type.
Implement a generic `First<T>` that takes an Array `T` and returns it's first
element's type.

For example:

Expand All @@ -28,16 +29,20 @@ The first thing that could come up is to use lookup types and just write `T[0]`:
type First<T extends any[]> = T[0];
```

But there is an edge case that we need to handle.
If we pass an empty array, `T[0]` will not work, because there is no element.
But there is an edge case that we need to handle. If we pass an empty array,
`T[0]` will not work, because there is no element.

So that, before accessing the first element in the array, we need to check if the array is empty.
To do that, we can use [conditional types](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html) in TypeScript.
So that, before accessing the first element in the array, we need to check if
the array is empty. To do that, we can use
[conditional types](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html)
in TypeScript.

The idea behind them is pretty straightforward.
If we can assign the type to the type of condition, it will go into “true” branch, otherwise it will take “false” path.
The idea behind them is pretty straightforward. If we can assign the type to the
type of condition, it will go into “true” branch, otherwise it will take “false”
path.

We are going to check, if the array is empty, we return nothing, otherwise we return the first element of the array:
We are going to check, if the array is empty, we return nothing, otherwise we
return the first element of the array:

```ts
type First<T extends any[]> = T extends [] ? never : T[0];
Expand Down
19 changes: 12 additions & 7 deletions en/easy-if.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ tags: utils

## Challenge

Implement a utils `If` which accepts condition `C`, a truthy return type `T`, and a falsy return type `F`.
`C` is expected to be either `true` or `false` while `T` and `F` can be any type.
Implement a utils `If` which accepts condition `C`, a truthy return type `T`,
and a falsy return type `F`. `C` is expected to be either `true` or `false`
while `T` and `F` can be any type.

For example:

Expand All @@ -20,17 +21,21 @@ type B = If<false, "a", "b">; // expected to be 'b'

## Solution

If you are not sure when to use [conditional types](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html) in TypeScript, it is when you need to use an “if” statement on types.
Exactly what we tasked to do here.
If you are not sure when to use
[conditional types](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html)
in TypeScript, it is when you need to use an “if” statement on types. Exactly
what we tasked to do here.

If the condition type evaluates to `true`, we need to take a “true” branch, otherwise “false” branch:
If the condition type evaluates to `true`, we need to take a “true” branch,
otherwise “false” branch:

```ts
type If<C, T, F> = C extends true ? T : F;
```

Going that way we will get a compilation error, because we are trying to assign `C` to boolean type and not having a constraint that shows that.
So let us fix it by adding `extends boolean` to the type parameter `C`:
Going that way we will get a compilation error, because we are trying to assign
`C` to boolean type and not having a constraint that shows that. So let us fix
it by adding `extends boolean` to the type parameter `C`:

```ts
type If<C extends boolean, T, F> = C extends true ? T : F;
Expand Down
35 changes: 18 additions & 17 deletions en/easy-includes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ tags: array

## Challenge

Implement the JavaScript `Array.includes` function in the type system.
A type takes the two arguments.
The output should be a boolean `true` or `false`.
For example:
Implement the JavaScript `Array.includes` function in the type system. A type
takes the two arguments. The output should be a boolean `true` or `false`. For
example:

```typescript
// expected to be `false`
Expand All @@ -20,35 +19,37 @@ type isPillarMen = Includes<["Kars", "Esidisi", "Wamuu", "Santana"], "Dio">;

## Solution

We begin with writing the type that accepts two arguments: `T` (a tuple) and `U` (what we are looking for).
We begin with writing the type that accepts two arguments: `T` (a tuple) and `U`
(what we are looking for).

```typescript
type Includes<T, U> = never;
```

Before we actually can find something in a tuple, it is easier to “convert” it to union, instead of a tuple.
To do so, we can use indexed types.
If we access the `T[number]`, TypeScript will return a union of all elements from `T`.
E.g. if you have a `T = [1, 2, 3]`, accessing it via `T[number]` will return `1 | 2 | 3`.
Before we actually can find something in a tuple, it is easier to “convert” it
to union, instead of a tuple. To do so, we can use indexed types. If we access
the `T[number]`, TypeScript will return a union of all elements from `T`. E.g.
if you have a `T = [1, 2, 3]`, accessing it via `T[number]` will return
`1 | 2 | 3`.

```typescript
type Includes<T, U> = T[number];
```

But, there is an error “Type ‘number’ cannot be used to index type ‘T’”.
It is because we don’t have a constraint over `T`.
We need to tell TypeScript that `T` is an array.
But, there is an error “Type ‘number’ cannot be used to index type ‘T’”. It is
because we don’t have a constraint over `T`. We need to tell TypeScript that `T`
is an array.

```typescript
type Includes<T extends unknown[], U> = T[number];
```

We have a union of elements.
How do we check if the element exists in a union?
Distributive conditional types!
We can write a conditional type for a union, and TypeScript will automatically apply the condition to each element of a union.
We have a union of elements. How do we check if the element exists in a union?
Distributive conditional types! We can write a conditional type for a union, and
TypeScript will automatically apply the condition to each element of a union.

E.g. if you write `2 extends 1 | 2`, what TypeScript will do is actually replace it with two conditionals `2 extends 1` and `2 extends 2`.
E.g. if you write `2 extends 1 | 2`, what TypeScript will do is actually replace
it with two conditionals `2 extends 1` and `2 extends 2`.

We can use it to check if `U` is in `T[number]` and if so, return true.

Expand Down
22 changes: 12 additions & 10 deletions en/easy-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,33 @@ Implement the built-in `Parameters<T>` generic without using it.

## Solution

This challenge requires us to get part of the information from the function.
To be more precise, parameters of the function.
Let’s start with declaring a type that accepts a generic type `T` that we will use to get the parameters:
This challenge requires us to get part of the information from the function. To
be more precise, parameters of the function. Let’s start with declaring a type
that accepts a generic type `T` that we will use to get the parameters:

```typescript
type MyParameters<T> = any;
```

Now, what is the proper way of “getting the type we don’t know about yet”?
By using inferring!
But before using it, let’s start with a simple conditional type to match the function:
Now, what is the proper way of “getting the type we don’t know about yet”? By
using inferring! But before using it, let’s start with a simple conditional type
to match the function:

```typescript
type MyParameters<T> = T extends (...args: any[]) => any ? never : never;
```

Here, we check if the type `T` matches the function with any arguments and any return type.
Now, we can replace `any[]` in our parameters list with the inferring:
Here, we check if the type `T` matches the function with any arguments and any
return type. Now, we can replace `any[]` in our parameters list with the
inferring:

```typescript
type MyParameters<T> = T extends (...args: infer P) => any ? never : never;
```

That way, the TypeScript compiler infers the parameters list of the function and will assign it to the type `P`.
What’s left is to return the type from the branch:
That way, the TypeScript compiler infers the parameters list of the function and
will assign it to the type `P`. What’s left is to return the type from the
branch:

```typescript
type MyParameters<T> = T extends (...args: infer P) => any ? P : never;
Expand Down
Loading

0 comments on commit 191b018

Please sign in to comment.