TypeScript Type Challenge Readonly Walkthrough
The goal of the challenge is to reimplement the built in Readonly generic type that exists in TypeScript, without using Readonly itself.
The challenge: https://github.com/type-challenges/type-challenges/blob/main/questions/00007-easy-readonly/README.md
Watch the video
An example case
Given the following TypeScript:
interface Todo {
  title: string
  description: string
}
const todo: MyReadonly<Todo> = {
  title: "Hey",
  description: "foobar"
}We need to create a new type called MyReadonly that will result in the following operations failing,
todo.title = "Hello" // Error: cannot reassign a readonly property
todo.description = "barFoo" // Error: cannot reassign a readonly propertyThis means we need to create a generic type that accepts a type as a parameter, and returns a new type with the readonly property set for each property in that type.
type MyReadonly<T> = ?Approach
We need to create a new type based off an existing type, this means we need to use mapped types in TypeScript.
The basic syntax for a mapped type is:
type MyReadonly<T> = {
  [Property in keyof T]: T[Property]
}Here we use keyof T to generate a union type of all the properties of T, which we then use to iterate over and generate a new type from. The type that the above returns is exactly the same as the type T passed in.
The solution
Mapped types allow you to add or remove modifiers when you create new types. These modifiers are ? to control optionality and readonly to control mutability.
You can add them with + and remove them with -.
Therefore we can add the readonly by prefixing each entry in our new type with the +readonly modifier.
type MyReadonly<T> = {
  +readonly [Property in keyof T]: T[Property]
}However, when adding modifiers, the + is optional, so we can simplify this type to:
type MyReadonly<T> = {
  readonly [Property in keyof T]: T[Property]
}