Chainable Options
Challenge
Chainable options are commonly used in Javascript. But when we switch to TypeScript, can you properly type it?
In this challenge, you need to type an object or a class - whatever you like - to provide two function option(key, value) and get(). In option, you can extend the current config type by the given key and value. We should about to access the final result via get.
For example
declare const config: Chainable;
const result = config
.option("foo", 123)
.option("name", "type-challenges")
.option("bar", { value: "Hello World" })
.get();
// expect the type of result to be:
interface Result {
foo: number;
name: string;
bar: {
value: string;
};
}
You don’t need to write any js/ts logic to handle the problem - just in type level.
You can assume that key only accepts string and the value can be anything - just leave it as-is. Same key won’t be passed twice.
Solution
Die Lösung der Herausforderungen besteht darin, erstmal die Funktion ‘option’ mit generischen Typen zu verstehen:
option<K extends string, V>(
key: K,
value: V
)
Der Rückgabewert der Funktion Option ist wieder vom gleichen Typ Chainable, wobei hier das letzte Attribut überschrieben wird, indem man erst das Attribut aus dem Typ entfernt,
und danach wieder mittels einer Vereinigung (Intersection) zusammengeführt.
// Aus { foo: 123} mit K = 'foo' und V = 7 wird
// { foo: 7 }
Omit<T, K> & Record<K, V>;
Abschließend muss man noch ein leeres Objekt definieren, das man anschließend immer in der Vereinigung nutzt und an die weiteren Aufrufe
von Chainable weitergibt. Dieses hält somit die Typ-Information über mehrere Funktionsaufrufe.
type Chainable<T = {}> = {
option<K extends string, V>(
key: K,
value: V
): Chainable<Omit<T, K> & Record<K, V>>;
get(): T;
};