TypeScript: Type Assertions and Const Assertions
Tags: typescript, frontend, learning, javascript
Published: 2023-04-23 19:48:07

I've been digging into TypeScript lately, and I got a bit confused between Type Assertions and Const Assertions. So, I decided to provide a concise overview of each concept and then dive deeper into the details. If you've faced similar challenges, I hope this article serves as a valuable resource for you.

By the end of this read, you'll gain a clearer understanding of:

1. Type Assertions
2. Const Assertions
  - `const` for primitive vs object
  - Compile time vs runtime

**Type Assertions**
`as (_type_)` or `<_type_>`

Type Assertions in TypeScript allow us to specify the type of a value. They can be done using either the "as" syntax or the angle-bracket syntax, except in .tsx files.

```
let id: any = 100;                    
let idNum = id as number;   // let idNum: number
let idNum = <number> id;    // let idNum: number
```

Use cases for Type Assertions include:

- When parsing JSON data with JSON.parse which intentionally returns the type "any".
- When retrieving an element using document.getElementById which returns some kind of HTMLElement.
- When handling caught errors in a "catch" block.
- When using non-null assertions (!) to ensure that a type is not null or undefined.

By using Type Assertions effectively, you can improve the accuracy and safety of your TypeScript code.

**Const Assertions**
`as const`

In TypeScript, Const Assertions imply that all values should be immutable, meaning they cannot be reassigned or modified after their initial assignment.

Here's a quick summary of `const`: 
When declaring a variable with `const` instead of `var` or `let`, there are two rules: it must be initialized during declaration, and it must remain immutable, or constant, throughout its lifetime.
By utilizing Const Assertions and adhering to the rules of const, you can ensure that your variables remain immutable in TypeScript, contributing to safer and more predictable code.

Example:

```
const animal: string = "dogs";

animal = "cats"  //Compiler Error


const ids = {
  person1: 10,
  person2: 20,
  person3: 30
};

ids = {
  person1: 100,
  person2: 200,
  person3: 300
}
//Compiler Error

//This is OK
ids.person2 = 200;
```

`as const` applies the following rules:

- Arrays are treated as readonly tuples, not mutable arrays
- Literals are treated as literals, not their general primitive equivalents
- Properties on objects are considered `readonly`

("Learning TypeScript" by Josh Goldburg)

Use Cases:
Narrowing primitive types to literal types

```
interface Breakfast {
  bread: string;
  drink: 'coffee' | 'tea';
}

function order (breakfast:Breakfast){
  if(breakfast.drink === 'coffee'){
    console.log('Milk or sugar?')
  } else {
    console.log('Milk or lemon?')
  }
}

// Wrong
const myOrder = {
  bread: 'wheat',
  drink: 'coffee'
}

order(myOrder)

// Argument of type '{ bread: string; drink: string; }' 
is not assignable to parameter of type 'Breakfast'.


const myOrder = {
  bread: 'wheat',
  drink: 'coffee'
} as const

order(myOrder)

// const myOrder: {
 readonly bread: 'wheat';
 readonly drink: 'coffee';
}
```

**Compile-time vs Runtime:**
When hovering over a variable, we get its 'Type'. 
When using 'typeof' to log the type, we get a result.

```
let x = "hello" as const;
const y = "hello";

// When hovering over these, 
we get 'let x: "hello"' and 'const y: "hello"'.

console.log(typeof x);
console.log(typeof y);

// When using 'console.log' with these, 
we get 'string' and 'string' respectively.
```

I got confused with them because I didn't understand the difference between compile-time and runtime.

**compile-time**: TypeScript performs type checking and type inference during compilation

**runtime**: Once TypeScript is successfully compiled into JavaScript, it is executed at runtime in JavaScript.

So, we get string literal types because TypeScript ensures that variables have specific, unchangeable values.
On the other hand, TypeScript treats 'typeof' as a regular string type because 'typeof' is a runtime operator in JavaScript.

In summary, Type Assertions are utilized to explicitly specify the type of a value, whereas Const Assertions are used to make values immutable and set all properties of an object literal expression as readonly properties.