From e11977bf3c2cbb94443d746908a0c7b2a407bb61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20LES=C3=89N=C3=89CHAL?= Date: Mon, 22 Jan 2024 21:44:35 +0100 Subject: [PATCH] Add missing, improve & fix types - use shared interfaces for similar types (i.e. ClientNavigator, UserInfo) - add types to some "any" values (i.e. JQuery.client.test) - fix various type issues --- jquery/client.d.ts | 47 ++++++--- jquery/textSelection.d.ts | 72 +++++++++---- mw/Api.d.ts | 8 +- mw/Map.d.ts | 5 +- mw/Rest.d.ts | 4 +- mw/Title.d.ts | 8 +- mw/Uri.d.ts | 58 +++++------ mw/global.d.ts | 6 +- mw/html.d.ts | 2 +- mw/index.d.ts | 23 ++++- mw/language.d.ts | 57 +++++++---- mw/loader.d.ts | 207 +++++++++++++++++++++++++++++++------- mw/log.d.ts | 73 ++++++++++++-- mw/message.d.ts | 17 +++- mw/notification.d.ts | 119 +++++++++++----------- mw/storage.d.ts | 8 +- mw/user.d.ts | 39 +++++-- mw/util.d.ts | 70 +++++++++++-- 18 files changed, 597 insertions(+), 226 deletions(-) diff --git a/jquery/client.d.ts b/jquery/client.d.ts index 774d2b2..6f5fb73 100644 --- a/jquery/client.d.ts +++ b/jquery/client.d.ts @@ -78,7 +78,7 @@ interface Client { * Defaults to the global `navigator` object. * @return {Object} The client object */ - profile(nav?: { userAgent: string; platform: string }): ClientProfile; + profile(nav?: ClientNavigator): ClientProfile; /** * Checks the current browser against a support map object. @@ -122,23 +122,40 @@ interface Client { * otherwise returns true if the browser is not found. * @return {boolean} The current browser is in the support map */ - test(map: any, profile?: ClientProfile, exactMatchOnly?: boolean): boolean; + test( + map: ClientSupportMap | { ltr: ClientSupportMap; rtl: ClientSupportMap }, + profile?: ClientProfile, + exactMatchOnly?: boolean + ): boolean; } +export interface ClientNavigator { + userAgent: string; + platform: string; +} + +type ClientProfileName = + | "android" + | "chrome" + | "crios" + | "edge" + | "firefox" + | "fxios" + | "konqueror" + | "msie" + | "opera" + | "rekong" + | "safari" + | "silk"; + +type ClientSupportMap = Partial>; +type ClientSupportCondition = [ + "==" | "===" | "!=" | "!==" | "<" | "<=" | ">" | ">=", + string | number +]; + interface ClientProfile { - name: - | "android" - | "chrome" - | "crios" - | "edge" - | "firefox" - | "fxios" - | "konqueror" - | "msie" - | "opera" - | "rekong" - | "safari" - | "silk"; + name: ClientProfileName; layout: "edge" | "gecko" | "khtml" | "presto" | "trident" | "webkit"; layoutVersion: number; platform: "ipad" | "iphone" | "linux" | "mac" | "solaris" | "win"; diff --git a/jquery/textSelection.d.ts b/jquery/textSelection.d.ts index 903257a..6635b86 100644 --- a/jquery/textSelection.d.ts +++ b/jquery/textSelection.d.ts @@ -16,7 +16,7 @@ declare global { * @return {JQuery} * @chainable */ - textSelection(command: "setContents"): JQuery; + textSelection(command: "setContents", content: string): this; /** * Get the currently selected text in this textarea. @@ -34,7 +34,7 @@ declare global { * @return {JQuery} * @chainable */ - textSelection(command: "replaceSelection"): JQuery; + textSelection(command: "replaceSelection", value: string): this; /** * Insert text at the beginning and end of a text selection, optionally @@ -49,18 +49,8 @@ declare global { */ textSelection( command: "encapsulateSelection", - options: { - pre?: string; - peri?: string; - post?: string; - ownline?: boolean; - replace?: boolean; - selectPeri?: boolean; - splitlines?: boolean; - selectionStart?: number; - selectionEnd?: number; - } - ): JQuery; + options?: Partial + ): this; /** * Get the current cursor position (in UTF-16 code units) in a textarea. @@ -95,7 +85,7 @@ declare global { * @return {JQuery} * @chainable */ - textSelection(command: "setSelection", options: { start?: number; end?: number }): JQuery; + textSelection(command: "setSelection", options: { start: number; end?: number }): this; /** * Scroll a textarea to the current cursor position. You can set the cursor @@ -108,7 +98,7 @@ declare global { * @return {JQuery} * @chainable */ - textSelection(command: "scrollToCaretPosition", options: { force?: boolean }): JQuery; + textSelection(command: "scrollToCaretPosition", options: { force?: boolean }): this; /** * Register an alternative textSelection API for this element. @@ -120,7 +110,7 @@ declare global { */ textSelection( command: "register", - functions: Record any> + functions: Record any> ): void; /** @@ -129,7 +119,55 @@ declare global { * @param {string} command Command to execute */ textSelection(command: "unregister"): void; + + /** + * Do things to the selection in the textarea, using a command from the alternative textSelection API for this element. + * + * @param {string} command Command to execute + * @param {Mixed} [commandOptions] Options to pass to the command + * @return {Mixed} Depending on the command + */ + textSelection(command: string, commandOptions?: any): void; } } +interface TextSelectionEncapsulateOptions { + /** + * Text to insert before the cursor/selection. + */ + pre: string; + /** + * Text to insert between pre and post and select afterwards. + */ + peri: string; + /** + * Text to insert after the cursor/selection. + */ + post: string; + /** + * Put the inserted text on a line of its own. Defaults to false. + */ + ownline: boolean; + /** + * If there is a selection, replace it with peri instead of leaving it alone. Defaults to false. + */ + replace: boolean; + /** + * Select the peri text if it was inserted (but not if there was a selection and replace==false, or if splitlines==true). Defaults to true. + */ + selectPeri: boolean; + /** + * If multiple lines are selected, encapsulate each line individually. Defaults to false. + */ + splitlines: boolean; + /** + * Position to start selection at. + */ + selectionStart: number; + /** + * Position to end selection at. Defaults to the position to start setection at. + */ + selectionEnd: number; +} + export {}; diff --git a/mw/Api.d.ts b/mw/Api.d.ts index 086e0d1..a53af3e 100644 --- a/mw/Api.d.ts +++ b/mw/Api.d.ts @@ -5,6 +5,7 @@ import { ApiRollbackParams, ApiUploadParams, } from "../api_params"; +import { UserInfo } from "./user"; type TitleLike = string | mw.Title; type TitleLikeArray = string[] | mw.Title[]; // TitleLike[] would be a mixed array @@ -355,13 +356,10 @@ declare global { /** * Get the current user's groups and rights. * - * @returns {JQuery.Promise<{ groups: string[], rights: string[] }>} + * @returns {JQuery.Promise} * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Api.plugin.user-method-getUserInfo */ - getUserInfo(): JQuery.Promise<{ - groups: string[]; - rights: string[]; - }>; + getUserInfo(): JQuery.Promise; /** * Extend an API parameter object with an assertion that the user won't change. diff --git a/mw/Map.d.ts b/mw/Map.d.ts index 790067e..0944f4d 100644 --- a/mw/Map.d.ts +++ b/mw/Map.d.ts @@ -24,10 +24,7 @@ declare global { * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Map-method-get */ get(): V; - get>( - selection: S, - fallback?: any - ): Pick ? SS : never>; + get(selection: S[], fallback?: any): Pick; get(selection: S, fallback?: V[S]): V[S]; /** diff --git a/mw/Rest.d.ts b/mw/Rest.d.ts index db1b8d5..3026533 100644 --- a/mw/Rest.d.ts +++ b/mw/Rest.d.ts @@ -38,12 +38,12 @@ declare global { * @param {RestOptions} [options] * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Rest-method-constructor */ - constructor(options?: RestOptions); + constructor(options?: Partial); /** * @private */ - defaultOptions: RestOptions; + defaults: RestOptions; /** * Abort all unfinished requests issued by this Api object. diff --git a/mw/Title.d.ts b/mw/Title.d.ts index 28efe24..172c91e 100644 --- a/mw/Title.d.ts +++ b/mw/Title.d.ts @@ -1,3 +1,5 @@ +import { QueryParams } from "./Uri"; + type title = string | mw.Title; declare global { @@ -260,7 +262,7 @@ declare global { * @return {string} * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Title-method-getUrl */ - getUrl(params?: any): string; + getUrl(params?: QueryParams): string; /** * Check if the title is in a talk namespace @@ -337,7 +339,7 @@ declare global { * @return {Title|null} A valid Title object or null if the title is invalid * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Title-static-method-newFromFileName */ - static newFromFileName(uncleanName: string): Title; + static newFromFileName(uncleanName: string): Title | null; /** * Get the file title from an image element @@ -385,7 +387,7 @@ declare global { title: string, defaultNamespace?: number, options?: { forUploading: boolean } - ): Title; + ): Title | null; /** * Normalize a file extension to the common form, making it lowercase and checking some synonyms, diff --git a/mw/Uri.d.ts b/mw/Uri.d.ts index c32499f..8372f9f 100644 --- a/mw/Uri.d.ts +++ b/mw/Uri.d.ts @@ -1,10 +1,21 @@ -type Options = - | { - strictMode?: boolean; - overrideKeys?: boolean; - arrayParams?: boolean; - } - | boolean; +export type QueryParams = Record; + +interface UriOptions { + /** + * Trigger strict mode parsing of the url. + */ + strictMode: boolean; + /** + * Whether to let duplicate query parameters override each other (`true`) or automagically convert them to an array (`false`). + */ + overrideKeys: boolean; + /** + * Whether to parse array query parameters (e.g. `&foo[0]=a&foo[1]=b` or `&foo[]=a&foo[]=b`) or leave them alone. + * Currently this does not handle associative or multi-dimensional arrays, but that may be improved in the future. + * Implies `overrideKeys: true` (query parameters without `[...]` are not parsed as arrays). + */ + arrayParams: boolean; +} declare global { namespace mw { @@ -19,7 +30,7 @@ declare global { * @return {Function} An mw.Uri class constructor * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw-method-UriRelative */ - function UriRelative(documentLocation: string | ((...args: any[]) => string)): Uri; + function UriRelative(documentLocation: string | (() => string)): typeof Uri; /** * Library for simple URI parsing and manipulation. @@ -111,7 +122,7 @@ declare global { * @property {Object} query For example `{ a: '0', b: '', c: 'value' }` (always present) * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Uri-property-query */ - query: any; + query: QueryParams; /** * @property {string|undefined} user For example `usr` * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Uri-property-user @@ -163,31 +174,12 @@ declare global { * other values for other instances -- see mw.UriRelative for details). * @param {Object|boolean} [options] Object with options, or (backwards compatibility) a boolean * for strictMode - * @param {boolean} [options.strictMode=false] Trigger strict mode parsing of the url. - * @param {boolean} [options.overrideKeys=false] Whether to let duplicate query parameters - * override each other (`true`) or automagically convert them to an array (`false`). - * @param {boolean} [options.arrayParams=false] Whether to parse array query parameters (e.g. - * `&foo[0]=a&foo[1]=b` or `&foo[]=a&foo[]=b`) or leave them alone. Currently this does not - * handle associative or multi-dimensional arrays, but that may be improved in the future. - * Implies `overrideKeys: true` (query parameters without `[...]` are not parsed as arrays). * @throws {Error} when the query string or fragment contains an unknown % sequence * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Uri-method-constructor */ constructor( - uri?: - | string - | Uri - | Partial<{ - fragment: string; - host: string; - password: string; - path: string; - port: string; - protocol: string; - query: any; - user: string; - }>, - options?: Options + uri?: string | Uri | Partial>, + options?: Partial | boolean ); /** @@ -206,7 +198,7 @@ declare global { * @return {Uri} This URI object * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Uri-method-extend */ - extend(parameters: Record): Uri; + extend(parameters: QueryParams): Uri; /** * Get the userInfo, host and port section of the URI. @@ -266,7 +258,7 @@ declare global { * @return {string} The URI string * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Uri-method-toString */ - toString(): string; + toString(): `${string}://${string}`; /** * Parse a string and set our properties accordingly. @@ -277,7 +269,7 @@ declare global { * @throws {Error} when the query string or fragment contains an unknown % sequence * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Uri-method-parse */ - parse(str: string, options: Options): void; + parse(str: string, options: Partial): void; /** * Decode a url encoded value. diff --git a/mw/global.d.ts b/mw/global.d.ts index 4c20dc8..4600a5b 100644 --- a/mw/global.d.ts +++ b/mw/global.d.ts @@ -13,7 +13,7 @@ declare global { * @param {Function} fn * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/global-method-addOnloadHook */ - function addOnloadHook(fn: (...args: any[]) => any): void; + function addOnloadHook(fn: () => void): void; /** * Import a local JS content page, for use by user scripts and site-wide scripts. @@ -48,7 +48,7 @@ declare global { * @return {HTMLLinkElement} Link tag * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/global-method-importStylesheet */ - function importStylesheet(title: string): HTMLLinkElement | null; + function importStylesheet(title: string): HTMLLinkElement; /** * @since 1.12.2 @@ -58,7 +58,7 @@ declare global { * @return {HTMLLinkElement} Link tag * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/global-method-importStylesheetURI */ - function importStylesheetURI(url: string, media: string): HTMLLinkElement | null; + function importStylesheetURI(url: string, media: string): HTMLLinkElement; } export {}; diff --git a/mw/html.d.ts b/mw/html.d.ts index fe52a43..6267200 100644 --- a/mw/html.d.ts +++ b/mw/html.d.ts @@ -31,7 +31,7 @@ declare global { */ function element( name: string, - attrs?: Record, + attrs?: Record, contents?: string | Raw | null ): string; diff --git a/mw/index.d.ts b/mw/index.d.ts index d32c7d1..f7f300e 100644 --- a/mw/index.d.ts +++ b/mw/index.d.ts @@ -26,9 +26,28 @@ declare global { * Base library for MediaWiki. * * Exposed globally as `mw`, with `mediaWiki` as alias. + * + * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw + */ + const mediaWiki: typeof mw; + + /** + * Base library for MediaWiki. + * + * Exposed globally as `mw`, with `mediaWiki` as alias. + * * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw */ namespace mw { + /** + * Empty object for third-party libraries, for cases where you don't + * want to add a new global, or the global is bad and needs containment + * or wrapping. + * + * @property {Object} + */ + const libs: any; + // types for mw.widgets are out of scope! const widgets: any; @@ -142,7 +161,7 @@ declare global { */ function trackSubscribe( topic: string, - callback: (topic: string, data: object) => any + callback: (topic: string, data: object) => void ): void; /** @@ -150,7 +169,7 @@ declare global { * * @param {Function} callback */ - function trackUnsubscribe(callback: (topic: string, data: object) => any): void; + function trackUnsubscribe(callback: (topic: string, data: object) => void): void; } } diff --git a/mw/language.d.ts b/mw/language.d.ts index e62ab68..32bbe5e 100644 --- a/mw/language.d.ts +++ b/mw/language.d.ts @@ -1,3 +1,5 @@ +type FlipObject> = { [K in keyof T as T[K]]: K }; + declare global { namespace mw { /** @@ -47,25 +49,43 @@ declare global { * @property {Object} * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.language-property-data */ - const data: Record; + const data: Record; /** * Information about month names in current UI language. * - * Object keys: - * - * - `names`: array of month names (in nominative case in languages which have the distinction), - * zero-indexed - * - `genitive`: array of month names in genitive case, zero-indexed - * - `abbrev`: array of three-letter-long abbreviated month names, zero-indexed - * - `keys`: object with three keys like the above, containing zero-indexed arrays of message keys - * for appropriate messages which can be passed to mw.msg. - * * @property {Object} * @member mw.language * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.language-property-months */ - const months: Record; + const months: { + /** + * Array of month names (in nominative case in languages which have the distinction), + * zero-indexed. + */ + abbrev: string[]; + + /** + * Object containing zero-indexed arrays of message keys for appropriate messages + * which can be passed to {@link mw.msg}. + */ + keys: { + abbrev: string[]; + genitive: string[]; + names: string[]; + }; + + /** + * Array of month names in genitive case, zero-indexed. + */ + genitive: string[]; + + /** + * Array of month names (in nominative case in languages which have the distinction), + * zero-indexed. + */ + names: string[]; + }; /** * Formats language tags according the BCP 47 standard. @@ -113,7 +133,7 @@ declare global { function convertPlural( count: number, forms: string[], - explicitPluralForms?: Record + explicitPluralForms?: Record ): string; /** @@ -123,7 +143,9 @@ declare global { * @return {Object} * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.language-method-flipTransform */ - function flipTransform(...tables: Array>): Record; + function flipTransform>( + ...tables: T[] + ): FlipObject; /** * Provides an alternative text depending on specified gender. @@ -138,7 +160,7 @@ declare global { * @return {string} * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.language-method-gender */ - function gender(gender: string, forms: string[]): string; + function gender(gender: string, forms: [T?, T?, T?]): T; /** * Convenience method for retrieving language data. @@ -160,7 +182,7 @@ declare global { * @return {Object|Array} * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.language-method-getDigitTransformTable */ - function getDigitTransformTable(): any; + function getDigitTransformTable(): string[] | Record; /** * Get the language fallback chain for current UI language, including the language itself. @@ -184,7 +206,7 @@ declare global { * @return {Object|Array} * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.language-method-getSeparatorTransformTable */ - function getSeparatorTransformTable(): any; + function getSeparatorTransformTable(): string[] | Record; /** * Turn a list of string into a simple list using commas and 'and'. @@ -207,7 +229,8 @@ declare global { * @param {any} [value] Value for dataKey, omit if dataKey is an object * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.language-method-setData */ - function setData(langCode: string, dataKey: any, value?: any): void; + function setData(langCode: string, dataKey: string, value: any): void; + function setData(langCode: string, dataKey: Record): void; /** * Apply numeric pattern to absolute value using options. Gives no diff --git a/mw/loader.d.ts b/mw/loader.d.ts index 0ea5313..8b1b6eb 100644 --- a/mw/loader.d.ts +++ b/mw/loader.d.ts @@ -1,3 +1,63 @@ +interface Module { + exports: any; +} + +type ModuleKey = `${string}@${string}`; +type ModuleState = + | "error" + | "executing" + | "loaded" + | "loading" + | "missing" + | "ready" + | "registered"; +type ModuleMessages = Record; +type ModuleStyle = Record; +type ModuleTemplates = Record; + +interface ModuleDeclarator { + (): [ + module: string, + script?: ModuleScript | null, + style?: ModuleStyle | null, + messages?: ModuleMessages | null, + templates?: ModuleTemplates | null, + deprecationWarning?: string | null + ]; +} + +interface ModuleRequire { + /** + * Get the exported value of a module. + * + * @param moduleName Module name + * @return Exported value + */ + (moduleName: string): any; +} + +type ModuleScript = + | string[] + | (($: JQuery, jQuery: JQuery, require: ModuleRequire, module: Module) => void) + | { + files: { [key: string]: any }; + main: string; + } + | string; + +interface ModuleRegistryEntry { + declarator?: ModuleDeclarator | null; + dependencies: string[]; + deprecationWarning?: string | null; + group: number | null; + messages?: ModuleMessages | null; + module: Module; + packageExports: any; + skip: string | null; + source: string; + state: "error" | "loaded" | "missing" | "registered" | "ready"; + version: string; +} declare global { namespace mw { /** @@ -75,7 +135,47 @@ declare global { * in the registry. * @see https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.loader-method-getState */ - function getState(module: string): string | null; + function getState(module: string): ModuleState | null; + + /** + * Implement a module given a function which returns the components of the module + * + * @param {Function} declarator + * + * The declarator should return an array with the following keys: + * + * - 0. {string} module Name of module and current module version. Formatted + * as '`[name]@[version]`". This version should match the requested version + * (from #batchRequest and #registry). This avoids race conditions (T117587). + * + * - 1. {Function|Array|string|Object} [script] Module code. This can be a function, + * a list of URLs to load via `