From bbf1adeeacd4ac881e7d8546abc7101cd3de597d Mon Sep 17 00:00:00 2001 From: Leonard Thieu Date: Tue, 16 Oct 2018 17:42:42 -0400 Subject: [PATCH] [jquery] Add `preDispatch` and `postDispatch` methods. Fix weak type issues. (#29750) * [jquery] Add `preDispatch` and `postDispatch` methods. See https://github.com/jquery/api.jquery.com/issues/752. * [jquery] Fix weak type issue for `SpecialEventHook`. * [jquery] Fix weak type issue for `FixHook`. * [jquery] Add interface for `jQuery.valHooks`. * [jquery] Fix weak type issue for `ValHook`. * [jquery] Add interface for `jQuery.cssHooks`. * [jquery] Properties on `CSSHook` are optional. * [jquery] Use polymorphic `this` for `settings` parameter. * [jquery] Wrap `StatusCodeCallbacks` in a region. * [jquery] Split `@since` tag for consistency. --- types/jquery/index.d.ts | 136 +++++++++++++-------- types/jquery/jquery-tests.ts | 176 +++++++++++++++++++++++++++- types/jquery/test/longdesc-tests.ts | 2 +- 3 files changed, 265 insertions(+), 49 deletions(-) diff --git a/types/jquery/index.d.ts b/types/jquery/index.d.ts index 344194e5af..3c10e8294d 100644 --- a/types/jquery/index.d.ts +++ b/types/jquery/index.d.ts @@ -60,8 +60,7 @@ interface JQueryStatic { * @see \`{@link https://api.jquery.com/jQuery.cssHooks/ }\` * @since 1.4.3 */ - // Set to HTMLElement to minimize breaks but should probably be Element. - cssHooks: JQuery.PlainObject>; + cssHooks: JQuery.CSSHooks; /** * An object containing all CSS properties that may be used without a unit. The .css() method uses this * object to see if it may append px to unitless values. @@ -118,8 +117,7 @@ $.when( support: JQuery.PlainObject; timers: Array>; Tween: JQuery.TweenStatic; - // Set to HTMLElement to minimize breaks but should probably be Element. - valHooks: JQuery.PlainObject>; + valHooks: JQuery.ValHooks; // HACK: This is the factory function returned when importing jQuery without a DOM. Declaring it separately breaks using the type parameter on JQueryStatic. // HACK: The discriminator parameter handles the edge case of passing a Window object to JQueryStatic. It doesn't actually exist on the factory function. (window: Window, discriminator: boolean): JQueryStatic; @@ -2021,7 +2019,8 @@ $.getScript( "myplugin.js", function() { * * @param html The HTML string on which to operate. * @see \`{@link https://api.jquery.com/jQuery.htmlPrefilter/ }\` - * @since 1.12/2.2 + * @since 1.12 + * @since 2.2 */ htmlPrefilter(html: JQuery.htmlString): JQuery.htmlString; /** @@ -26376,14 +26375,6 @@ declare namespace JQuery { * A string containing the URL to which the request is sent. */ url?: string; - /** - * A pre-request callback function that can be used to modify the jqXHR (in jQuery 1.4.x, - * XMLHTTPRequest) object before it is sent. Use this to set custom headers, etc. The jqXHR and - * settings objects are passed as arguments. This is an Ajax Event. Returning false in the beforeSend - * function will cancel the request. As of jQuery 1.5, the beforeSend option will be called regardless - * of the type of request. - */ - beforeSend?(this: TContext, jqXHR: jqXHR, settings: AjaxSettings): false | void; } interface UrlAjaxSettings extends Ajax.AjaxSettingsBase { @@ -26391,14 +26382,6 @@ declare namespace JQuery { * A string containing the URL to which the request is sent. */ url: string; - /** - * A pre-request callback function that can be used to modify the jqXHR (in jQuery 1.4.x, - * XMLHTTPRequest) object before it is sent. Use this to set custom headers, etc. The jqXHR and - * settings objects are passed as arguments. This is an Ajax Event. Returning false in the beforeSend - * function will cancel the request. As of jQuery 1.5, the beforeSend option will be called regardless - * of the type of request. - */ - beforeSend?(this: TContext, jqXHR: jqXHR, settings: UrlAjaxSettings): false | void; } namespace Ajax { @@ -26443,7 +26426,7 @@ declare namespace JQuery { * function will cancel the request. As of jQuery 1.5, the beforeSend option will be called regardless * of the type of request. */ - beforeSend?(this: TContext, jqXHR: jqXHR, settings: AjaxSettingsBase): false | void; + beforeSend?(this: TContext, jqXHR: jqXHR, settings: this): false | void; /** * If set to false, it will force requested pages not to be cached by the browser. Note: Setting cache * to false will only work correctly with HEAD and GET requests. It works by appending "_={timestamp}" @@ -26673,6 +26656,9 @@ declare namespace JQuery { xhrFields?: XHRFields; } + // region StatusCodeCallbacks + // #region StatusCodeCallbacks + type StatusCodeCallbacks = { // region Success Status Codes // #region Success Status Codes @@ -27092,6 +27078,8 @@ declare namespace JQuery { [index: number]: SuccessCallback | ErrorCallback; }; + // #endregion + // Writable properties on XMLHttpRequest interface XHRFields extends Partial> { msCaching?: string; @@ -27575,12 +27563,24 @@ callbacks.fire( "world" ); // #endregion - // region CSS - // #region CSS + // region CSS hooks + // #region CSS hooks - interface CSSHook { - get(this: this, elem: TElement, computed: any, extra: any): any; - set(this: this, elem: TElement, value: any): void; + // Workaround for TypeScript 2.3 which does not have support for weak types handling. + type CSSHook = + Partial<_CSSHook> & ( + Pick<_CSSHook, 'get'> | + Pick<_CSSHook, 'set'> + ); + + interface _CSSHook { + get(elem: TElement, computed: any, extra: any): any; + set(elem: TElement, value: any): void; + } + + interface CSSHooks { + // Set to HTMLElement to minimize breaks but should probably be Element. + [propertyName: string]: CSSHook; } // #endregion @@ -31342,13 +31342,18 @@ $( "ul" ).click( handler ).find( "ul" ).hide(); special: SpecialEventHooks; } - interface FixHook { + // region Fix hooks + // #region Fix hooks + + // Workaround for TypeScript 2.3 which does not have support for weak types handling. + type FixHook = { /** * Strings representing properties that should be copied from the browser's event object to the jQuery * event object. If omitted, no additional properties are copied beyond the standard ones that jQuery * copies and normalizes (e.g. `event.target` and `event.relatedTarget`). */ - props?: string[]; + props: string[]; + } | { /** * jQuery calls this function after it constructs the `jQuery.Event` object, copies standard properties * from `jQuery.event.props`, and copies the `fixHooks`-specific props (if any) specified above. The @@ -31399,8 +31404,10 @@ if ( !existingHook ) { } ``` */ - filter?(event: Event, originalEvent: _Event): void; - } + filter(event: Event, originalEvent: _Event): void; + } | { + [key: string]: never; + }; /** * The `fixHooks` interface provides a per-event-type way to extend or normalize the event object that @@ -31412,6 +31419,8 @@ if ( !existingHook ) { [event: string]: FixHook; } + // #endregion + // region Special event hooks // #region Special event hooks @@ -31428,7 +31437,8 @@ if ( !existingHook ) { * * @see \`{@link https://learn.jquery.com/events/event-extensions/#special-event-hooks }\` */ - interface SpecialEventHook { + // Workaround for TypeScript 2.3 which does not have support for weak types handling. + type SpecialEventHook = { /** * Indicates whether this event type should be bubbled when the `.trigger()` method is called; by * default it is `false`, meaning that a triggered event will bubble to the element's parents up to the @@ -31437,7 +31447,8 @@ if ( !existingHook ) { * * @see \`{@link https://learn.jquery.com/events/event-extensions/#nobubble-boolean }\` */ - noBubble?: boolean; + noBubble: boolean; + } | { /** * When defined, these string properties specify that a special event should be handled like another * event type until the event is delivered. The `bindType` is used if the event is attached directly, @@ -31446,7 +31457,8 @@ if ( !existingHook ) { * * @see \`{@link https://learn.jquery.com/events/event-extensions/#bindtype-string-delegatetype-string }\` */ - bindType?: string; + bindType: string; + } | { /** * When defined, these string properties specify that a special event should be handled like another * event type until the event is delivered. The `bindType` is used if the event is attached directly, @@ -31455,7 +31467,8 @@ if ( !existingHook ) { * * @see \`{@link https://learn.jquery.com/events/event-extensions/#bindtype-string-delegatetype-string }\` */ - delegateType?: string; + delegateType: string; + } | { /** * The setup hook is called the first time an event of a particular type is attached to an element; * this provides the hook an opportunity to do processing that will apply to all events of this type on @@ -31474,7 +31487,8 @@ if ( !existingHook ) { * * @see \`{@link https://learn.jquery.com/events/event-extensions/#setup-function-data-object-namespaces-eventhandle-function }\` */ - setup?(this: TTarget, data: TData, namespaces: string, eventHandle: EventHandler): void | false; + setup(this: TTarget, data: TData, namespaces: string, eventHandle: EventHandler): void | false; + } | { /** * The teardown hook is called when the final event of a particular type is removed from an element. * The `this` keyword will be a reference to the element where the event is being cleaned up. This hook @@ -31489,7 +31503,8 @@ if ( !existingHook ) { * * @see \`{@link https://learn.jquery.com/events/event-extensions/#teardown-function }\` */ - teardown?(this: TTarget): void | false; + teardown(this: TTarget): void | false; + } | { /** * Each time an event handler is added to an element through an API such as `.on()`, jQuery calls this * hook. The `this` keyword will be the element to which the event handler is being added, and the @@ -31497,7 +31512,8 @@ if ( !existingHook ) { * * @see \`{@link https://learn.jquery.com/events/event-extensions/#add-function-handleobj }\` */ - add?(this: TTarget, handleObj: HandleObject): void; + add(this: TTarget, handleObj: HandleObject): void; + } | { /** * When an event handler is removed from an element using an API such as `.off()`, this hook is called. * The `this` keyword will be the element where the handler is being removed, and the `handleObj` @@ -31505,7 +31521,8 @@ if ( !existingHook ) { * * @see \`{@link https://learn.jquery.com/events/event-extensions/#remove-function-handleobj }\` */ - remove?(this: TTarget, handleObj: HandleObject): void; + remove(this: TTarget, handleObj: HandleObject): void; + } | { /** * Called when the `.trigger()` or `.triggerHandler()` methods are used to trigger an event for the * special type from code, as opposed to events that originate from within the browser. The `this` @@ -31524,7 +31541,8 @@ if ( !existingHook ) { * * @see \`{@link https://learn.jquery.com/events/event-extensions/#trigger-function-event-jquery-event-data-object }\` */ - trigger?(this: TTarget, event: Event, data: TData): void | false; + trigger(this: TTarget, event: Event, data: TData): void | false; + } | { /** * When the `.trigger()` method finishes running all the event handlers for an event, it also looks for * and runs any method on the target object by the same name unless of the handlers called `event.preventDefault()`. @@ -31535,7 +31553,8 @@ if ( !existingHook ) { * * @see \`{@link https://learn.jquery.com/events/event-extensions/#_default-function-event-jquery-event-data-object }\` */ - _default?(event: Event, data: TData): void | false; + _default(event: Event, data: TData): void | false; + } | { /** * jQuery calls a handle hook when the event has occurred and jQuery would normally call the user's event * handler specified by `.on()` or another event binding method. If the hook exists, jQuery calls it @@ -31555,8 +31574,14 @@ if ( !existingHook ) { * * @see \`{@link https://learn.jquery.com/events/event-extensions/#handle-function-event-jquery-event-data-object }\` */ - handle?(this: TTarget, event: Event & { handleObj: HandleObject; }, ...data: TData[]): void; - } + handle(this: TTarget, event: Event & { handleObj: HandleObject; }, ...data: TData[]): void; + } | { + preDispatch(this: TTarget, event: Event): false | void; + } | { + postDispatch(this: TTarget, event: Event): void; + } | { + [key: string]: never; + }; interface SpecialEventHooks { [event: string]: SpecialEventHook; @@ -31615,6 +31640,9 @@ if ( !existingHook ) { value: string; } + // region Coordinates + // #region Coordinates + interface Coordinates { left: number; top: number; @@ -31626,11 +31654,27 @@ if ( !existingHook ) { Pick | { [key: string]: never; }; - interface ValHook { - get?(elem: TElement): any; - set?(elem: TElement, value: any): any; + // #endregion + + // region Val hooks + // #region Val hooks + + // Workaround for TypeScript 2.3 which does not have support for weak types handling. + type ValHook = { + get(elem: TElement): any; + } | { + set(elem: TElement, value: any): any; + } | { + [key: string]: never; + }; + + interface ValHooks { + // Set to HTMLElement to minimize breaks but should probably be Element. + [nodeName: string]: ValHook; } + // #endregion + type _Falsy = false | null | undefined | 0 | '' | typeof document.all; } diff --git a/types/jquery/jquery-tests.ts b/types/jquery/jquery-tests.ts index 31518d8d84..1fb00ad9ab 100644 --- a/types/jquery/jquery-tests.ts +++ b/types/jquery/jquery-tests.ts @@ -19,7 +19,7 @@ function JQueryStatic() { } function cssHooks() { - // $ExpectType PlainObject> + // $ExpectType CSSHooks $.cssHooks; } @@ -84,7 +84,7 @@ function JQueryStatic() { } function valHooks() { - // $ExpectType PlainObject> + // $ExpectType ValHooks $.valHooks; } @@ -7119,6 +7119,51 @@ function JQuery_Callbacks() { } } +function JQuery_CSSHooks() { + $.cssHooks.borderRadius = { + get(elem, computed, extra) { + // $ExpectedType HTMLElement + elem; + // $ExpectedType any + computed; + // $ExpectedType any + extra; + }, + set(elem, value) { + // $ExpectedType HTMLElement + elem; + // $ExpectedType any + value; + } + }; + + $.cssHooks.borderRadius = { + get(elem, computed, extra) { + // $ExpectedType HTMLElement + elem; + // $ExpectedType any + computed; + // $ExpectedType any + extra; + } + }; + + $.cssHooks.borderRadius = { + set(elem, value) { + // $ExpectedType HTMLElement + elem; + // $ExpectedType any + value; + } + }; + + // Weak type test. This may be removed if the TypeScript requirement is increased to 2.4+. + // $ExpectError + $.cssHooks.borderRadius = function get(elem: HTMLElement, computed: any, extra: any) { + return 1; + }; +} + function JQuery_Promise3() { interface I1 { kind: 'I1'; } interface I2 { kind: 'I2'; } @@ -8143,3 +8188,130 @@ function JQuery_Event() { $(window).trigger(event); } } + +function JQuery_EventExtensions() { + function fixHooks() { + jQuery.event.fixHooks.drop = { + props: ['dataTransfer'], + filter(event, originalEvent) { + // $ExpectType Event + event; + // $ExpectType Event + originalEvent; + }, + }; + + // Weak type test. This may be removed if the TypeScript requirement is increased to 2.4+. + // $ExpectError + jQuery.event.fixHooks.drop = ['dataTransfer']; + } + + function special() { + jQuery.event.special.multiclick = { + noBubble: true, + bindType: 'click', + delegateType: 'click', + setup(data, namespaces, eventHandle) { + // $ExpectType EventTarget + this; + // $ExpectType any + data; + // $ExpectType string + namespaces; + // $ExpectType EventHandler + eventHandle; + + return false; + }, + teardown() { + // $ExpectType EventTarget + this; + + return false; + }, + add(handleObj) { + // $ExpectType EventTarget + this; + // $ExpectType HandleObject + handleObj; + }, + remove(handleObj) { + // $ExpectType EventTarget + this; + // $ExpectType HandleObject + handleObj; + }, + trigger(event, data) { + // $ExpectType EventTarget + this; + // $ExpectType Event + event; + // $ExpectType any + data; + + return false; + }, + _default(event, data) { + // $ExpectType Event + event; + // $ExpectType any + data; + + return false; + }, + handle(event, data) { + // $ExpectType Event & { handleObj: HandleObject; } + event; + // $ExpectType any + data; + }, + preDispatch(event) { + // $ExpectType EventTarget + this; + // $ExpectType Event + event; + + return false; + }, + postDispatch(event) { + // $ExpectType EventTarget + this; + // $ExpectType Event + event; + } + }; + + // Weak type test. This may be removed if the TypeScript requirement is increased to 2.4+. + // $ExpectError + jQuery.event.special.multiclick = 1; + } +} + +declare namespace JQuery { + interface ValHooks { + textarea: ValHook; + } +} + +function JQuery_ValHooks() { + jQuery.valHooks.textarea = { + get(elem) { + // $ExpectType HTMLTextAreaElement + elem; + + return elem.value.replace(/\r?\n/g, "\r\n"); + }, + set(elem) { + // $ExpectType HTMLTextAreaElement + elem; + + return elem.value; + } + }; + + // Weak type test. This may be removed if the TypeScript requirement is increased to 2.4+. + // $ExpectError + jQuery.valHooks.textarea = function get(elem: HTMLTextAreaElement) { + return elem.value.replace(/\r?\n/g, "\r\n"); + }; +} diff --git a/types/jquery/test/longdesc-tests.ts b/types/jquery/test/longdesc-tests.ts index 4390ae014a..35d7f61646 100644 --- a/types/jquery/test/longdesc-tests.ts +++ b/types/jquery/test/longdesc-tests.ts @@ -1341,7 +1341,7 @@ function longdesc() { function jquery_css_hooks_6() { $.fx.step.someCSSProp = function(fx) { - $.cssHooks.someCSSProp.set(fx.elem as HTMLElement, fx.now + fx.unit); + $.cssHooks.someCSSProp.set!(fx.elem as HTMLElement, fx.now + fx.unit); }; }