Readonly 2
Challenge
Implement a generic MyReadonly2<T, K> which takes two type argument T and K.
K specify the set of properties of T that should set to Readonly. When K is not provided, it should make all properties readonly just like the normal Readonly<T>.
For example
interface Todo {
title: string;
description: string;
completed: boolean;
}
const todo: MyReadonly2<Todo, "title" | "description"> = {
title: "Hey",
description: "foobar",
completed: false,
};
todo.title = "Hello"; // Error: cannot reassign a readonly property
todo.description = "barFoo"; // Error: cannot reassign a readonly property
todo.completed = true; // OK
Solution
Auch zur Lösung dieses Problems benötigen wir wieder einen Mapped Typ. Als Konditionen für die Eingabewerte
definieren wir einmal ein Objekt sowie Attribute des Objektes.
// T muss ein Objekt sein, K muss ein Attribut oder eine Vereinigung von Attributen aus T sein.
type MyReadonly2<T extends Record<string, any>, K extends keyof T>
In dieser Form des Typs ist die Eingabe von zwei Typen Pflicht. Da es aber in der Aufgabe heißt, man solle auch auf das zweite Argument verzichten können, ist ein Default-Wert notwendig. Dieser wird durch die Zuweisung direkt beim Eingabetyp definiert, also:
// K extends keyof T = keyof T bedeutet, dass, falls das zweite Argument fehlt, einfach alle Attribute von T als Wert für diesen Parameter genutzt werden
type MyReadonly2<T extends Record<string, any>, K extends keyof T = keyof T>
Nun können wir den finalen Typ definieren, indem wir die übergebenen Attribute auf readonly setzen und die beiden Objekttypen anschließend vereinen.
type MyReadonly2<T, K extends keyof T = keyof T> = {
readonly [Key in K]: T[Key];
} & Omit<T, K>;