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 property
This 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]
}