Total TypeScript - Beginner
===

---
###### tags: `TypeScript`
> The course was made by Matt Popcock, you can refer to this [repo](https://github.com/total-typescript/beginners-typescript-tutorial) to clone it or open [GitPod](https://gitpod.io/#https://github.com/total-typescript/beginners-typescript)
> Once you downloaded it, go into the folder and run `npm install` to install dependencies, if you use GitPod, just run `npm install`.
### 1. The Implicit ‘Any’ Type Error
Visit `src` folder and you will see `[topic]-problem.ts`, here we are at exercise 01, it is a function which is used for adding numbers, run `npm run exercise 01` to run the test.
#### Exercies 01

#### Test result
The functionality is okay.

#### Checking type
Looks like it's faied when it comes to check types for parameters `a` and `b`.

---
#### Solution
Let's take a look at the error, it said `Parameter a / b has an any type`. What it can't be `any` type?
First of all, what is `any`, and in which situation we can use `any` as an type?
- We can use whenever we don’t want a particular value to cause typechecking errors, but why it caused error in exercise 01?
- The function is meant to add numbers, so if type of `a` or `b` has `any` type, even if the functionality was passed, TS expected us to specify the types for `a` and `b`, and **we should always be specific for argument of types**.
- Since the functionality was to add `numbers`, thereofore we should specify both `a` and `b` as type `number`.
#### Amendment

#### Test result

---
> References:
> [TS Handbook - any](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html?#any)
> [TS Don't and Do's for any](https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#any)
> Reminder:
> ❌ Don’t use `any` as a type unless you are in the process of migrating a JavaScript project to TypeScript.
---
### 2. Working With Object Params
#### Exercise 02

Here we have the same function `addTwoNumbers`, but this is time, it needs to accept an `object`,
#### Checking type

It showed just like the exercise 01, `params` implicitly has an `any` type, as we can see the code above, it needs to accept an `object`, let's fix type of `params` from an `any` type to `object`.
We can define type of `object` in three ways.
##### Solution - 1 : Define types inline directly(anonymously)

##### Solution - 2 : Use `Type alias`

#### Solution - 3 : Use `interface`

Let's breakdown for `Type alias` and `interface`.
- When we want to use the same type more than once and refer to it by a single name, `type alias` and `interface` come in handy.
- Almost all features of an `interface` are available in `type alias`.
- Key distinction is that a type **cannot be re-opened to add new properties** vs an **interface which is always extendable**.
```typescript!
// Valid if using interface
interface Dog {
name: string;
}
interface Dog {
age: number;
}
// Invalid if using type alias
type Dog = {
name:string;
}
type Dog = {
age:number;
}
```
---
> Reference:
> [TS Handbook - Object](https://www.typescriptlang.org/docs/handbook/2/objects.html#handbook-content)
---
### 3. Set Properties as Optional
#### Exercise 03

#### Test result

Here let's make the `last` as **optional**. If we want a value to be shown optional, we can add `?` in front of `:`, for example: `const name?: string;`, it's acutally says `const name = string | undefined;`
#### Solution - 1 : Use optional parameter

#### Solution - 2 : undefined

With solution 2, we still need to assign `undefined` to param `last`, so it would be easier to use optional parameter.
---
> Reference:
> [TS Handbook - optional parameter](https://www.typescriptlang.org/docs/handbook/2/functions.html#optional-parameters)
> [TS Don't and Do's for optional parameter](https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#callback-types)
---
### 4. Optional Parameters
#### Exercise 04

Like the exercise 03, this time we need to find the way to make `last` optional.
#### Checking type

#### Solution - 1 : Use optional parameter

#### Solution - 2 : Define directly and assign `undefined` as second argument

With solution 2, we still need to assign `undefined` to param `last`, so it would be easier to use optional parameter.
---
> Reference:
> [TS Handbook - optional parameter](https://www.typescriptlang.org/docs/handbook/2/functions.html#optional-parameters)
> [TS Don't and Do's for optional parameter](https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#callback-types)
---
### 5. Assign Type to a Variable
#### Exercise 05

#### Checking type

The error message:
`Argument of type '{}' is not assignable to parameter of type 'User'.
Type '{}' is missing the following properties from type 'User': id, firstName, lastName, isAdmin`
#### Solution

With solution, if we don't specify `const defualtUser: User = {...}`, there won't be any errores. but if we miss or misspell a property, the error will show down below like the image below, and this is not what we want, a better way is to let TS show error at the variable so that we can clarify the issue.
#### Misspell `isAdmin` without specifying type for variable defaultUSer
Error msg: Not clarify the issue enough.
`Argument of type '{ id: number; firstName: string; lastName: string; isadmin: boolean; }' is not assignable to parameter of type 'User'.
Property 'isAdmin' is missing in type '{ id: number; firstName: string; lastName: string; isadmin: boolean; }' but required in type 'User'`

#### Misspell `isAdmin` with specifying type for variable defaultUSer
Error msg: Clarify the issue plaintly.
`Type '{ id: number; firstName: string; lastName: string; isadmin: boolean; }' is not assignable to type 'User'.
Object literal may only specify known properties, but 'isadmin' does not exist in type 'User'. Did you mean to write 'isAdmin'?`

---
> Reference:
> [TS Handbook - Defining types](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html#defining-types)
---
### 6. Constraining Value Type
#### Exercise 06

In this exercise, we want to make our `role` to be either `admin`, `user` or `super-admin`.
#### Solution - 1 : Use Union type

We use `|` to separate different values, just like we specify an `union type`, for example: `const age = string | number`.
#### Solution - 2 : Use type alias
In this case, we can also use `Type alias` to define `role's` value and assign to it.
In this case, `interface` is not sutiable due to we can't assign value to an `interface`.

---
> Reference:
> [TS Handbook - Union type](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types)
> [TS Don't & Do's - Union type](https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#use-union-types)
> [TS 新手指南](https://willh.gitbook.io/typescript-tutorial/basics/union-types)
---
### 7. Working With Array
#### Exercise 07

The error msg:
`Type '{ id: number; title: string; }[]' is missing the following properties from type 'Post': id, title`.
The issue occurres because of `posts` is an `array`, but in our `User` interface, it the type of `posts` is not correct, it has to be an `array`.
Both solutions can resolve the issue.
#### Solution:

#### Use `Generic` (Advanced topic)

We can specify array of only contains specifc value, for example:
`string[]` means the array should only contains `string`; `number[]` should only contains `number`.
---
> Reference:
> [TS Handbook-array](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#arrays)
> [TS - Generic](https://www.typescriptlang.org/docs/handbook/2/generics.html#handbook-content)
---
### 8. Function Return Type Annotation
#### Exercise 08

#### Checking type

#### Solution

We assign type `User` after brakets, and inside the `return`, we provide values that are required by type `User`, now if we misspell or miss one property, the error will occur within the scope and clarify the issue.
Error msg:
`Type '{ id: number; firstName: string; lastName: string; role: "admin"; post: { id: number; title: string; }[]; }' is not assignable to type 'User'.
Object literal may only specify known properties, but 'post' does not exist in type 'User'. Did you mean to write 'posts'?`

---
### 9. Typing Promises and Async Requests
#### Exercise 9

#### Checking type

#### Solution 1: Promise<>
Hover over to the error message, TS will recommand we to use `Promise<LukeSkywalker>` below to fix the error.
```
interface LukeSkywalker
The return type of an async function or method must be the global Promise<T> type.
Did you mean to write `Promise<LukeSkywalker>?ts(1064)
```

#### Solution 2: Assign type to data
Assign type to data that we are going to fetch directly.

#### Solution 3: Cast Data as a Type
- [Type Casting](https://www.typescripttutorial.net/typescript-tutorial/type-casting/)
- In TypeScript, you can use the `as` keyword or `<>` operator for type castings.

---
### 10. Passing Type Arguments
#### Exercise 10

#### Checking type

#### Solution
- [TyppScript Set](https://howtodoinjava.com/typescript/sets/)
- Use `new` keyword to create a `Set`
```typescript=
// Create a new Set
const total = new Set();
// Example 1: Add type number
// Can only accept value of number
const total = new Set<number>();
// Example 2: Add type string
// Can only accept value of string
const direction = new Set<string>(["East", "West", "South", "North"]);
```
```typescript=
// If we pass wrong type of value, TS will show error
const total = new Set<number>();
total.add("Hello world");
```

- Methods of `Set`
- `set.add(v)` – adds values into the Set.
- `set.has(v)` – checks the existence of a value in the Set.
- `set.delete(v)` – deletes a value from the Set.
- `set.clear()` – clear all values from the Set.
- `set.size` – ‘size‘ property will return size of Set.
Let's take a look at the code below.
```typescript=
const guitarists = new Set();
guitarists.add("Jimi Hendrix");
guitarists.add("Eric Clapton");
```
It takes values of `string`, therefore we should add type of `string` for it.

---
### 11. Assigning Dynamic Keys to an Object
#### Exercise 11

#### Checking type

#### Solution 1: Using `Record<Keys, Type>`
>Whenever you see index in a type error, it's usually referring to the key of an object.
- [Utility Types: Record<Keys, Type>](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type)

#### Solution 2: Assign type directly

#### Solution 3: Using interface || type

If we did not pass the correct type, TS will show error.
```typescript=
// interface ICahce {
// [id: string]: string;
// }
type TCahce = {
[id: string]: string;
}
const createCache = () => {
const cache: TCahce = {};
// const cache: Record<string, string> = {}
// const cache: {
// [id: string]: string
// } = {};
const add = (id: string, value: string) => {
cache[id] = value;
}
return {add};
}
const cache = createCache();
// Change string to a number
cache.add(2, "Matt");
```

---
### 12. Narrowing Down Union Types
#### Exercise 12

#### Checking type

#### Solution : Using `typeof` to narrow down
- [typeof type operator](https://www.typescriptlang.org/docs/handbook/2/typeof-types.html#the-typeof-type-operator)

---
### 13. Typing Errors in a Try-Catch
#### Exercise 13

#### Checking type

#### Solution 1: Using `any`(Not recommended)
If we use `any` as a type of `e`, it will fix the error, but this has several downsides, one is that it won't check if there's a typo or it won't autocomplete on it.

With a typo, there's no error.

#### Solution 2: Using data casting `as`
If we use `as` here, it won't show error as well, but it has some downsides too.

In real world, it contains many codes inside of a try-catch block, what if someone forgot to add `Error`,which means `e` might not be an `Error`, here `e` is `undefined`, but TS won't show any error to us until we execute some testing.

#### Solution 3: Using `instanceof`
- [Narrowing - instanceof](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#instanceof-narrowing)

---
### 14. Inheriting Interface Properties
#### Exercise 14

#### Solution: Using `extends`
- [Extending types](https://www.typescriptlang.org/docs/handbook/2/objects.html#extending-types)
We can pull out same property and create another `interface` as a base, and let other interface to extend it.
Since those `interfaces` have same property - `id`, we can something like this.

**Note: Can't use `type` to inheriting types, `extends` can be used for interfaces and classes only.**
Using `type`

Using `interface`

---
### 15. Combining Types to Create New Types
#### Exercise 15

#### Checking type

#### Solution: Using `&` as intersaction types
- [Intersaction Types](https://www.typescriptlang.org/docs/handbook/2/objects.html#intersection-types)
We can use `&` to define a new type that represents both `User` and `Post`, one thing that needs to be cautious is that `posts` is an object array.

Or we can define directly.

---
### Selectively construct Types from other Types
#### Exercise 16

#### Checking type

#### Solution 1: Using `omit<Type, keys>`
Constructs a type by picking all properties from Type and then **removing** Keys (string literal or union of string literals).
Code below shows that includes all properites from type `User`, except `id`.

> Reference
> [TS docs - Omit](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys)
#### Solution 2: Using `Pick<Type, keys>`
Constructs a type by picking the set of properties Keys (string literal or union of string literals) from Type.
Code below shows that we only want `firstName` and `lastname` from type `User`

> Reference
> [Utility Types](https://www.typescriptlang.org/docs/handbook/utility-types.html)
---
### Function Types
#### Exercise 17

#### Solutoin 1: Declare type directly.

#### Solution 2: Declare a type outside

---
### Typing Async Functions
#### Exercise 18
