From e47e9ae4cc932950314dc54e75902419391f7022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20LES=C3=89N=C3=89CHAL?= Date: Fri, 9 Feb 2024 15:26:10 +0100 Subject: [PATCH] Fix `mw.Map` autocompletion when `V` may include unknown additional keys We may want to use `Map & Map>`, but this alternative does not allow mixed autocompletion & typeckecking when passing a key array to `get` or an object to `set`. --- mw/Map.d.ts | 79 ++++++++++++++++++++++++++++++++++++++++++++------ mw/config.d.ts | 5 ++-- 2 files changed, 73 insertions(+), 11 deletions(-) diff --git a/mw/Map.d.ts b/mw/Map.d.ts index adfb0c3..cd1bfd5 100644 --- a/mw/Map.d.ts +++ b/mw/Map.d.ts @@ -1,10 +1,68 @@ -type KeyOrArray = keyof T | Array; -type GetOrDefault = V extends Required> - ? V[K] - : Required[K] | T; -type PickOrDefault, T> = S extends Array - ? { [K in SS & keyof V]-?: GetOrDefault } - : GetOrDefault; +type TypeOrArray = T | T[]; + +// Get/PickOrDefault extracts values from V using key selection S +// - TD is the value type of missing properties +// - TX is the value type of unknown properties + +type GetOrDefault = K extends keyof V + ? V extends Required> + ? V[K] + : Required[K] | TD + : TX | TD; + +type PickOrDefault, TD, TX = unknown> = S extends Array< + infer K +> + ? { [P in K & PropertyKey]-?: GetOrDefault } + : GetOrDefault; + +// `ExtensibleMap` is an alternative to `Map` +// but unlike the latter, ExtensibleMap provides additional overloads to improve selection +// autocompletion and type checking. + +export interface ExtensibleMap, TX = unknown> extends mw.Map { + /** + * Check if a given key exists in the map. + * + * @param selection Key to check + * @returns True if the key exists + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Map-method-exists + */ + exists(selection: S): selection is S; + exists(selection: S): selection is S; + + /** + * Get the value of one or more keys. + * + * If called with no arguments, all values are returned. + * + * @param selection Key or array of keys to retrieve values for. + * @param fallback Value for keys that don't exist. + * @returns If selection was a string, returns the value. If selection was an array, returns + * an object of key/values. If no selection is passed, a new object with all key/values is returned. + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Map-method-get + */ + get, TD>( + selection: S, + fallback: TD + ): PickOrDefault; + get, TD>(selection: S, fallback: TD): PickOrDefault; + get>(selection: S): PickOrDefault; + get>(selection: S): PickOrDefault; + get = V | Record>(): T; + + /** + * Set the value of one or more keys. + * + * @param selection Key to set value for, or object mapping keys to values + * @param value Value to set (optional, only in use when key is a string) + * @returns True on success, false on failure + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Map-method-set + */ + set(selection: S, value: V[S]): boolean; + set(selection: S, value: TX): boolean; + set & Record>(selection: S): boolean; +} declare global { namespace mw { @@ -40,8 +98,11 @@ declare global { * an object of key/values. If no selection is passed, a new object with all key/values is returned. * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Map-method-get */ - get, T>(selection: S, fallback: T): PickOrDefault; - get>(selection: S): PickOrDefault; + get, TD>( + selection: S, + fallback: TD + ): PickOrDefault; + get>(selection: S): PickOrDefault; get(): T; /** diff --git a/mw/config.d.ts b/mw/config.d.ts index 9cbef8a..ba74e7f 100644 --- a/mw/config.d.ts +++ b/mw/config.d.ts @@ -1,3 +1,5 @@ +import { ExtensibleMap } from "./Map"; + declare global { namespace mw { /** @@ -11,7 +13,7 @@ declare global { * * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw-property-config */ - const config: Map<{ + const config: ExtensibleMap<{ /** * Since MediaWiki 1.36+, 0 means debug mode is off, and a positive non-zero number means debug mode is on (e.g. 1 or 2). * @@ -358,7 +360,6 @@ declare global { * @see https://www.mediawiki.org/wiki/Manual:Interface/JavaScript#wgDiffNewId */ wgDiffNewId?: number; - [key: string]: unknown; // more config keys can be added by extensions }>; } }