# Type Widening in TypeScript

## What exactly is type widening?

Type widening is the process by which the type checker assigns a type to a variable initialized with a constant value when no type annotations are provided.

Example: Given

```
let x = 'v'
```

When type widening is done x will have an inferred type of 'string'.

## Why is it important to understand type widening?

So that we understand errors that are thrown due to unassignable values caused as a result of type widening.

Example: Given

```
let x = 'banana'

let fruit: 'orange'|'banana' = orange
fruit = x;
> error: cannot assign string to type of 'orange'|'banana'

```

Without an awareness of type widening, we would struggle to understand why a string looking variable, fruit, cannot be assigned the value of another seemingly string looking variable, x. With an awareness of type widening, we can understand that type of fruit is a much more specific type than that of string and following structural typing cannot permit an assignation of type string.

> If you're struggling with structural typing remember it as the process that allows subsets to be assigned to supersets, and be careful in how you determine which the subset is and which the superset is.   For instance, subsets can 'look' bigger than supersets, but typically a subset has more properties defined than a superset, or put another way, {} is a very large set while {x: string} is also a very large set but smaller than {}.

## How does structural typing (subsets to superset types) let typescript widening errors occur?

It only allows narrower to wider type assignations, but not wider to narrower assignations, so in the case where a constant's type in inferred as, say,  string, assigning that type to a narrower type of 'banana' | 'apple' causes an error with respect to structural typing.

## Is there any way to predict the outcome of type widening?

Yes. Although very much an ambiguous process, we can predict the outcome of a widened type depending on the following:

### 1. If we used **let** or **const**
  
Let leads to wider inferences than const which typically only allows for the very specific type of that value.
 
Example:

```
let x = 'x' > type string
const x = 'x' > type 'x'
```

### 2. If we used the **as const** annotation

Different from the **const** keyword which exists in value space to declare constant variables, **as const** chooses the most specific type when used, and is most useful in controlling the types of object properties.

  Example:

  ```
  const obj = {

      a: 'x' as const
      b: 'sugar'
  }
  ```

Without **as const**, property 'a' would have been inferred as 'string.' With **as const**, it is inferred as having type 'x'.


Similarly,

  ```
  const obj = {

      a: 'x',
      b: 'sugar'
  } as const
  ```

Without **as const**, obj would have been inferred as `obj: {a: string, b: string}`. With const it is inferred as `obj: {readonly a: 'x', readonly b: 'sugar'}`.

### 3. If we provided a type declaration annotation
  
Providing a type annotation narrows the type inference to the type provided.

  Example

  ```
  let x: 'orange' = banana > throws an error, string cannot be assigned to type 'orange'
  ```

### 4. If it is an element of an object

  Elements of objects are treated as though having been declared using the let keyword.

### 5. If we declared the object all at once or in bits

  Type widening only allows for inference on elements present in an object at declaration time. It does not allow for new object properties.

## Give an example of the rule: a variable's type shouldn't change after it's declared

```
let x = 'orange'
x = 1
```

Typescript will constrain a constants type to limit it to the set of values that can be assigned to that variable without needing to change the type of the variable. So an assignation like the one above where we're trying to assign an entirely different type of 'number' to a type of 'string' will throw an error.

## Give another rule governing how TS treats properties of objects.

Elements of objects are treated as though having been declared by let

## What is a significant limitation imposed on objects as a result of this rule

You cannot add new properties which were not originally part of the object when it was declared.

## List 4 methods to control the TS widening process.

- Use **const**
- Use **as const**
- Use a type declaration annotation
- Declare objects in full

## Which of these apply to single value variables and which to objects and tuples? Give examples

They all apply to both, except perhaps the last method.
The **as const** method applies to objects and tuples in particular.

Using **as const** on an array like object is great for creating a tuple type.

Example:

```
let tuple = [1,2] as const
> type is inferred as readonly [1 , 2]
```

compared to

```
let tuple = [1,2]
> type is inferred as number[]
```

