diff --git a/mw/Api.d.ts b/mw/Api.d.ts index 00547e3..ffbe4d0 100644 --- a/mw/Api.d.ts +++ b/mw/Api.d.ts @@ -11,6 +11,8 @@ import { } from "../api_params"; import { TitleLike } from "./Title"; +type Tail = T extends [] ? T : T extends [any?, ...infer R] ? R : T; + type TypeOrArray = T extends any ? T | T[] : never; // T[] would be a mixed array type ReplaceValue = T extends U[] ? V[] : V; @@ -54,10 +56,14 @@ interface EditChangedResult extends EditSuccessResult { } // type alias to fix #45 -type AssertUser = { - assert: "anon" | "user"; - assertUser: string; -}; +type AssertUser = + | { + assert: "anon"; + } + | { + assert: "user"; + assertUser: string; + }; interface RollbackInfo { /** @@ -79,9 +85,12 @@ interface FinishUpload { * Call this function to finish the upload. * * @param {ApiUploadParams} data Additional data for the upload. - * @returns {JQuery.Promise} API promise for the final upload. + * @returns {mw.Api.Promise<[ApiResponse], mw.Api.RejectArgTuple | [string, ApiResponse]>} API promise for the final upload. */ - (data?: ApiUploadParams): JQuery.Promise; + (data?: ApiUploadParams): mw.Api.Promise< + [ApiResponse], + mw.Api.RejectArgTuple | [string, ApiResponse] + >; } declare global { @@ -139,7 +148,7 @@ declare global { * * @param {UnknownApiParams} parameters Parameters to the API. See also {@link mw.Api.Options.parameters}. * @param {JQuery.AjaxSettings} [ajaxOptions] Parameters to pass to jQuery.ajax. See also {@link mw.Api.Options.ajax}. - * @returns {JQuery.Promise} A promise that settles when the API response is processed. + * @returns {Api.Promise} A promise that settles when the API response is processed. * Has an 'abort' method which can be used to abort the request. * * - On success, resolves to `( result, jqXHR )` where `result` is the parsed API response. @@ -163,10 +172,7 @@ declare global { * {@link JSON.parse}. * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#ajax */ - ajax( - parameters: UnknownApiParams, - ajaxOptions?: JQuery.AjaxSettings - ): JQuery.Promise; + ajax(parameters: UnknownApiParams, ajaxOptions?: JQuery.AjaxSettings): Api.Promise; /** * Extend an API parameter object with an assertion that the user won't change. @@ -222,7 +228,7 @@ declare global { * @param {ApiUploadParams} data Other upload options, see `action=upload` API docs for more * @param {number} [chunkSize] Size (in bytes) per chunk (default: 5MB) * @param {number} [chunkRetries] Amount of times to retry a failed chunk (default: 1) - * @returns {JQuery.Promise} + * @returns {Api.Promise.Upload} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#chunkedUpload */ chunkedUpload( @@ -230,7 +236,7 @@ declare global { data: ApiUploadParams, chunkSize?: number, chunkRetries?: number - ): JQuery.Promise; + ): Api.Promise.Upload; /** * Upload a file to the stash, in chunks. @@ -242,7 +248,7 @@ declare global { * @param {ApiUploadParams} [data] * @param {number} [chunkSize] Size (in bytes) per chunk (default: 5MB) * @param {number} [chunkRetries] Amount of times to retry a failed chunk (default: 1) - * @returns {JQuery.Promise} Promise that resolves with a + * @returns {Api.Promise.Upload<[FinishUpload]>} Promise that resolves with a * function that should be called to finish the upload. * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#chunkedUploadToStash */ @@ -251,7 +257,7 @@ declare global { data?: ApiUploadParams, chunkSize?: number, chunkRetries?: number - ): JQuery.Promise; + ): Api.Promise.Upload<[FinishUpload]>; /** * Create a new page. @@ -267,14 +273,14 @@ declare global { * @param {TitleLike} title Page title * @param {ApiEditPageParams} params Edit API parameters * @param {string} content Page content - * @returns {JQuery.Promise} API response + * @returns {Api.Promise<[EditResult]>} API response * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#create */ create( title: TitleLike, params: ApiEditPageParams, content: string - ): JQuery.Promise; + ): Api.Promise<[EditResult]>; /** * Edit an existing page. @@ -334,32 +340,29 @@ declare global { * @since 1.28 * @param {TitleLike} title Page title * @param {Api.EditTransform} transform Callback that prepares the edit - * @returns {JQuery.Promise} Edit API response + * @returns {Api.Promise<[EditResult]>} Edit API response * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#edit */ - edit(title: TitleLike, transform: Api.EditTransform): JQuery.Promise; + edit(title: TitleLike, transform: Api.EditTransform): Api.Promise<[EditResult]>; /** * Perform API get request. See {@link ajax()} for details. * * @param {UnknownApiParams} parameters * @param {JQuery.AjaxSettings} [ajaxOptions] - * @returns {JQuery.Promise} + * @returns {Api.Promise} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#get */ - get( - parameters: UnknownApiParams, - ajaxOptions?: JQuery.AjaxSettings - ): JQuery.Promise; + get(parameters: UnknownApiParams, ajaxOptions?: JQuery.AjaxSettings): Api.Promise; /** * Get the categories that a particular page on the wiki belongs to. * * @param {TitleLike} title - * @returns {JQuery.Promise} Promise that resolves with an array of category titles, or with false if the title was not found. + * @returns {Api.Promise<[false|Title[]]>} Promise that resolves with an array of category titles, or with false if the title was not found. * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#getCategories */ - getCategories(title: TitleLike): JQuery.Promise; + getCategories(title: TitleLike): Api.Promise<[false | Title[]]>; /** * Get a list of categories that match a certain prefix. @@ -367,18 +370,18 @@ declare global { * E.g. given "Foo", return "Food", "Foolish people", "Foosball tables"... * * @param {string} prefix Prefix to match. - * @returns {JQuery.Promise} Promise that resolves with an array of matched categories + * @returns {Api.Promise<[string[]]>} Promise that resolves with an array of matched categories * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#getCategoriesByPrefix */ - getCategoriesByPrefix(prefix: string): JQuery.Promise; + getCategoriesByPrefix(prefix: string): Api.Promise<[string[]]>; /** * API helper to grab a csrf token. * - * @returns {JQuery.Promise} Received token. + * @returns {Api.Promise<[string]>} Received token. * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#getEditToken */ - getEditToken(): JQuery.Promise; + getEditToken(): Api.Promise<[string]>; /** * Given an API response indicating an error, get a jQuery object containing a human-readable @@ -423,13 +426,13 @@ declare global { * @since 1.37 - accepts a single string message as parameter. * @param {string|string[]} messages Messages to retrieve * @param {ApiQueryAllMessagesParams} [options] Additional parameters for the API call - * @returns {JQuery.Promise>} + * @returns {Api.Promise<[Object.]>} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#getMessages */ getMessages( messages: T | T[], options?: ApiQueryAllMessagesParams - ): JQuery.Promise>>; + ): Api.Promise<[Partial>]>; /** * Get a token for a certain action from the API. @@ -439,40 +442,40 @@ declare global { * @since 1.35 - additional parameters can be passed as an object instead of `assert`. * @param {string} type Token type * @param {ApiQueryTokensParams|ApiAssert} [additionalParams] Additional parameters for the API. When given a string, it's treated as the `assert` parameter. - * @returns {JQuery.Promise} Received token. + * @returns {Api.Promise<[string]>} Received token. * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#getToken */ getToken( type: ApiTokenType, additionalParams?: ApiQueryTokensParams | ApiAssert - ): JQuery.Promise; + ): Api.Promise<[string]>; /** @deprecated Use `getToken('csrf')` instead */ getToken( type: ApiLegacyTokenType, additionalParams?: ApiQueryTokensParams | ApiAssert - ): JQuery.Promise; + ): Api.Promise<[string]>; getToken( type: string, additionalParams?: ApiQueryTokensParams | ApiAssert - ): JQuery.Promise; + ): Api.Promise<[string]>; /** * Get the current user's groups and rights. * * @since 1.27 - * @returns {JQuery.Promise} + * @returns {Api.Promise<[Api.UserInfo], Api.RejectArgTuple | []>} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#getUserInfo */ - getUserInfo(): JQuery.Promise; + getUserInfo(): Api.Promise<[Api.UserInfo], Api.RejectArgTuple | []>; /** * Determine if a category exists. * * @param {TitleLike} title - * @returns {JQuery.Promise} Promise that resolves with a boolean indicating whether the category exists. + * @returns {Api.Promise<[boolean]>} Promise that resolves with a boolean indicating whether the category exists. * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#isCategory */ - isCategory(title: TitleLike): JQuery.Promise; + isCategory(title: TitleLike): Api.Promise<[boolean]>; /** * Load a set of messages and add them to {@link mw.messages}. @@ -480,13 +483,13 @@ declare global { * @since 1.37 - accepts a single string message as parameter. * @param {string|string[]} messages Messages to retrieve * @param {ApiQueryAllMessagesParams} [options] Additional parameters for the API call - * @returns {JQuery.Promise} + * @returns {Api.Promise<[boolean]>} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#loadMessages */ loadMessages( messages: string | string[], options?: ApiQueryAllMessagesParams - ): JQuery.Promise; + ): Api.Promise<[boolean]>; /** * Load a set of messages and add them to {@link mw.messages}. Only messages that are not already known @@ -496,21 +499,21 @@ declare global { * @since 1.42 - accepts a single string message as parameter. * @param {string|string[]} messages Messages to retrieve * @param {ApiQueryAllMessagesParams} [options] Additional parameters for the API call - * @returns {JQuery.Promise} + * @returns {Api.Promise<[] | [boolean]>} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#loadMessagesIfMissing */ loadMessagesIfMissing( messages: string | string[], options?: ApiQueryAllMessagesParams - ): JQuery.Promise; + ): Api.Promise<[] | [boolean]>; /** * @param {string} username * @param {string} password - * @returns {JQuery.Promise} See {@link post()} + * @returns {Api.Promise<[ApiResponse]>} See {@link post()} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#login */ - login(username: string, password: string): JQuery.Promise; + login(username: string, password: string): Api.Promise<[ApiResponse]>; /** * Post a new section to the page. @@ -519,7 +522,7 @@ declare global { * @param {string} header * @param {string} message Wikitext message * @param {ApiEditPageParams} additionalParams Additional API parameters - * @returns {JQuery.Promise} See {@link postWithEditToken} + * @returns {Api.Promise} See {@link postWithEditToken} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#newSection */ newSection( @@ -527,7 +530,7 @@ declare global { header: string, message: string, additionalParams?: ApiEditPageParams - ): JQuery.Promise; + ): Api.Promise; /** * Convenience method for `action=parse`. @@ -535,36 +538,33 @@ declare global { * @param {TitleLike} content Content to parse, either as a wikitext string or a {@link mw.Title} * @param {ApiParseParams} [additionalParams] Parameters object to set custom settings, e.g. * `redirects`, `sectionpreview`. `prop` should not be overridden. - * @returns {JQuery.Promise} Promise that resolves with the parsed HTML of `wikitext` + * @returns {Api.Promise<[string]>} Promise that resolves with the parsed HTML of `wikitext` * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#parse */ - parse(content: TitleLike, additionalParams?: ApiParseParams): JQuery.Promise; + parse(content: TitleLike, additionalParams?: ApiParseParams): Api.Promise<[string]>; /** * Perform API post request. See {@link ajax()} for details. * * @param {UnknownApiParams} parameters * @param {JQuery.AjaxSettings} [ajaxOptions] - * @returns {JQuery.Promise} + * @returns {Api.Promise} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#post */ - post( - parameters: UnknownApiParams, - ajaxOptions?: JQuery.AjaxSettings - ): JQuery.Promise; + post(parameters: UnknownApiParams, ajaxOptions?: JQuery.AjaxSettings): Api.Promise; /** * Post to API with csrf token. If we have no token, get one and try to post. If we have a cached token try using that, and if it fails, blank out the cached token and start over. * * @param {UnknownApiParams} params API parameters * @param {JQuery.AjaxSettings} [ajaxOptions] - * @returns {JQuery.Promise} See {@link post} + * @returns {Api.Promise} See {@link post} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#postWithEditToken */ postWithEditToken( params: UnknownApiParams, ajaxOptions?: JQuery.AjaxSettings - ): JQuery.Promise; + ): Api.Promise; /** * Post to API with the specified type of token. If we have no token, get one and try to post. @@ -583,25 +583,25 @@ declare global { * @param {string} tokenType The name of the token, like `options` or `edit`. * @param {UnknownApiParams} params API parameters * @param {JQuery.AjaxSettings} [ajaxOptions] - * @returns {JQuery.Promise} See {@link post()} + * @returns {Api.Promise} See {@link post()} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#postWithToken */ postWithToken( tokenType: ApiTokenType, params: UnknownApiParams, ajaxOptions?: JQuery.AjaxSettings - ): JQuery.Promise; + ): Api.Promise; /** @deprecated Use `postWithToken('csrf', params)` instead */ postWithToken( tokenType: ApiLegacyTokenType, params: UnknownApiParams, ajaxOptions?: JQuery.AjaxSettings - ): JQuery.Promise; + ): Api.Promise; postWithToken( tokenType: string, params: UnknownApiParams, ajaxOptions?: JQuery.AjaxSettings - ): JQuery.Promise; + ): Api.Promise; /** * Convenience method for `action=rollback`. @@ -610,14 +610,14 @@ declare global { * @param {TitleLike} page * @param {string} user * @param {ApiRollbackParams} [params] Additional parameters - * @returns {JQuery.Promise} + * @returns {Api.Promise<[RollbackInfo]>} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#rollback */ rollback( page: TitleLike, user: string, params?: ApiRollbackParams - ): JQuery.Promise; + ): Api.Promise<[RollbackInfo]>; /** * Asynchronously save the value of a single user option using the API. @@ -625,10 +625,10 @@ declare global { * * @param {string} name * @param {string|null} value - * @returns {JQuery.Promise} + * @returns {Api.Promise} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#saveOption */ - saveOption(name: string, value: string | null): JQuery.Promise; + saveOption(name: string, value: string | null): Api.Promise; /** * Asynchronously save the values of user options using the {@link https://www.mediawiki.org/wiki/Special:MyLanguage/API:Options Options API}. @@ -646,10 +646,12 @@ declare global { * would fail anyway. See T214963. * * @param {Object.} options Options as a `{ name: value, … }` object - * @returns {JQuery.Promise} + * @returns {Api.Promise<[] | [ApiResponse, JQuery.jqXHR]>} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#saveOptions */ - saveOptions(options: Record): JQuery.Promise; + saveOptions>( + options: T + ): Api.Promise<({} extends T ? [] : never) | [ApiResponse, JQuery.jqXHR]>; /** * Convenience method for `action=watch&unwatch=1`. @@ -657,14 +659,14 @@ declare global { * @param {TypeOrArray} pages Full page name or instance of {@link mw.Title}, or an * array thereof. If an array is passed, the return value passed to the promise will also be an * array of appropriate objects. - * @returns {JQuery.Promise>} A promise that resolves + * @returns {Api.Promise<[TypeOrArray]>} A promise that resolves * with an object (or array of objects) describing each page that was passed in and its * current watched/unwatched status. * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#unwatch */ unwatch

>( pages: P - ): JQuery.Promise>; + ): Api.Promise<[ReplaceValue]>; /** * Upload a file to MediaWiki. @@ -673,23 +675,23 @@ declare global { * * @param {File|Blob|HTMLInputElement} file HTML `input type=file` element with a file already inside of it, or a File object. * @param {ApiUploadParams} data Other upload options, see `action=upload` API docs for more - * @returns {JQuery.Promise} + * @returns {Api.Promise.Upload} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#upload */ - upload( - file: File | Blob | HTMLInputElement, - data: ApiUploadParams - ): JQuery.Promise; + upload(file: File | Blob | HTMLInputElement, data: ApiUploadParams): Api.Promise.Upload; /** * Finish an upload in the stash. * * @param {string} filekey * @param {ApiUploadParams} data - * @returns {JQuery.Promise} + * @returns {Api.Promise<[ApiResponse], Api.RejectArgTuple | [string, ApiResponse]>} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#uploadFromStash */ - uploadFromStash(filekey: string, data: ApiUploadParams): JQuery.Promise; + uploadFromStash( + filekey: string, + data: ApiUploadParams + ): Api.Promise<[ApiResponse], Api.RejectArgTuple | [string, ApiResponse]>; /** * Upload a file to the stash. @@ -711,14 +713,14 @@ declare global { * ``` * @param {File|HTMLInputElement} file * @param {ApiUploadParams} [data] - * @returns {JQuery.Promise} Promise that resolves with a + * @returns {Api.Promise.Upload<[FinishUpload]>} Promise that resolves with a * function that should be called to finish the upload. * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#uploadToStash */ uploadToStash( file: File | HTMLInputElement, data?: ApiUploadParams - ): JQuery.Promise; + ): Api.Promise.Upload<[FinishUpload]>; /** * Convenience method for `action=watch`. @@ -729,7 +731,7 @@ declare global { * array of appropriate objects. * @param {string} [expiry] When the page should expire from the watchlist. If omitted, the * page will not expire. - * @returns {JQuery.Promise>} A promise that resolves + * @returns {Api.Promise<[TypeOrArray]>} A promise that resolves * with an object (or array of objects) describing each page that was passed in and its * current watched/unwatched status. * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#watch @@ -737,7 +739,7 @@ declare global { watch

>( pages: P, expiry?: string - ): JQuery.Promise>; + ): Api.Promise<[ReplaceValue]>; /** * Massage parameters from the nice format we accept into a format suitable for the API. @@ -814,6 +816,62 @@ declare global { */ watched: boolean; } + + type Arg< + T extends ArgTuple, + N extends number, + TAcc extends never[] = [] + > = false extends (T extends [] ? true : false) + ? TAcc["length"] extends N + ? T[0] + : Arg, N, [...TAcc, never]> + : never; + + interface PromiseBase< + TResolve extends ArgTuple, + TReject extends ArgTuple, + TNotify extends ArgTuple + > extends JQuery.PromiseBase< + Arg, + Arg, + Arg, + Arg, + Arg, + Arg, + Arg, + Arg, + Arg, + Arg, + Arg, + Arg + >, + Pick {} + + type ArgTuple = [any?, any?, any?, any?]; + + type Promise< + TResolve extends Api.ArgTuple = [ApiResponse, JQuery.jqXHR], + TReject extends Api.ArgTuple = RejectArgTuple, + TNotify extends Api.ArgTuple = [] + > = PromiseBase; + + type RejectArgTuple = + | Rest.RejectArgTuple + | [ + "ok-but-empty", + "OK response but empty result (check HTTP headers?)", + "" | null | undefined, + JQuery.jqXHR<"" | null | undefined> + ] + | [string, ApiResponse, ApiResponse, JQuery.jqXHR]; + + namespace Promise { + type Upload = PromiseBase< + TResolve, + [RejectArgTuple[0], RejectArgTuple[1]], + [number] + >; + } } } } diff --git a/mw/Rest.d.ts b/mw/Rest.d.ts index 8b05063..9a955a4 100644 --- a/mw/Rest.d.ts +++ b/mw/Rest.d.ts @@ -50,10 +50,10 @@ declare global { * * @param {string} path * @param {JQuery.AjaxSettings} [ajaxOptions] - * @returns {JQuery.Promise} Done: API response data and the jqXHR object. + * @returns {Rest.Promise} Done: API response data and the jqXHR object. * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html#ajax */ - ajax(path: string, ajaxOptions?: JQuery.AjaxSettings): JQuery.Promise; + ajax(path: string, ajaxOptions?: JQuery.AjaxSettings): Rest.Promise; /** * Perform REST API DELETE request. @@ -64,14 +64,14 @@ declare global { * @param {string} path * @param {Object.} body * @param {Object.} [headers] - * @returns {JQuery.Promise} + * @returns {Rest.Promise} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Rest.html#delete */ delete( path: string, body: Record, headers?: Record - ): JQuery.Promise; + ): Rest.Promise; /** * Perform REST API get request. @@ -79,14 +79,14 @@ declare global { * @param {string} path * @param {Object.} query * @param {Object.} [headers] - * @returns {JQuery.Promise} + * @returns {Rest.Promise} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Rest.html#get */ get( path: string, query: Record, headers?: Record - ): JQuery.Promise; + ): Rest.Promise; /** * Perform REST API post request. @@ -97,14 +97,14 @@ declare global { * @param {string} path * @param {Object.} [body] * @param {Object.} [headers] - * @returns {JQuery.Promise} + * @returns {Rest.Promise} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Rest.html#post */ post( path: string, body?: Record, headers?: Record - ): JQuery.Promise; + ): Rest.Promise; /** * Perform REST API PUT request. @@ -115,14 +115,14 @@ declare global { * @param {string} path * @param {Object.} body * @param {Object.} [headers] - * @returns {JQuery.Promise} + * @returns {Rest.Promise} * @see https://doc.wikimedia.org/mediawiki-core/master/js/mw.Rest.html#put */ put( path: string, body: Record, headers?: Record - ): JQuery.Promise; + ): Rest.Promise; } namespace Rest { @@ -136,6 +136,20 @@ declare global { */ ajax?: JQuery.AjaxSettings; } + + type Promise< + TResolve extends Api.ArgTuple = [RestResponse, JQuery.jqXHR], + TReject extends Api.ArgTuple = RejectArgTuple, + TNotify extends Api.ArgTuple = [] + > = Api.PromiseBase; + + type RejectArgTuple = ["http", HttpErrorData]; + + interface HttpErrorData { + exception: string; + textStatus: JQuery.Ajax.ErrorTextStatus; + xhr: JQuery.jqXHR; + } } } }