[jquery] Fix return types for JQuery.map, JQueryStatic.map, and JQueryStatic(). (#26524)

* [jquery] Fix return type of `JQuery.map`.

The type parameter of the JQuery object returned from `JQuery.map` depends on the return values of the callback. Previously, the type parameter was based on the object it was called on.

This fix requires dropping constraints on `JQuery`, `JQueryStatic`, `EventHandler`, and `EventHandlerBase` as `JQuery.map` may return `JQuery` objects that contain non-`Node` values.

* [jquery] Fix return type of `JQueryStatic.map`.

* [jquery] Declare type for `this` in callback for `JQueryStatic.map`.

* [jquery] Fix return type for `JQueryStatic()`.

* [bootstrap] Match change to `JQuery` interface.

* [flight] Fix test failure due to change in `@types/jquery`.

* [materialize-css] Fix test failure due to change in `@types/jquery`.

* [select2] Match changes to `@types/jquery` interfaces.

* [jquery] Fix error due to breaking change in TypeScript lib declarations.

(cherry picked from commit 2506245)
This commit is contained in:
Leonard Thieu
2018-06-17 23:02:23 +01:00
committed by John Reilly
parent 10fe633823
commit bac2d1db56
10 changed files with 273 additions and 41 deletions
+1 -1
View File
@@ -354,7 +354,7 @@ export type TooltipEvent = "show.bs.tooltip" | "shown.bs.tooltip" | "hide.bs.too
// --------------------------------------------------------------------------------------
declare global {
interface JQuery<TElement extends Node = HTMLElement> extends Iterable<TElement> {
interface JQuery<TElement = HTMLElement> {
alert(action?: "close" | "dispose"): this;
button(action: "toggle" | "dispose"): this;
+1 -1
View File
@@ -1,6 +1,6 @@
declare var el: Element;
declare var els: Element[];
declare var els: HTMLElement[];
declare var mixinFn: Function;
function TestComponent() {
+47 -15
View File
@@ -40,7 +40,7 @@ type _Event = Event;
// Used by JQuery.Promise3 and JQuery.Promise
type _Promise<T> = Promise<T>;
interface JQueryStatic<TElement extends Node = HTMLElement> {
interface JQueryStatic<TElement = HTMLElement> {
/**
* @see {@link http://api.jquery.com/jquery.ajax/#jQuery-ajax1}
* @deprecated Use jQuery.ajaxSetup(options)
@@ -137,23 +137,53 @@ interface JQueryStatic<TElement extends Node = HTMLElement> {
// HACK: The discriminator parameter handles the edge case of passing a Window object to JQueryStatic. It doesn't actually exist on the factory function.
<FElement extends Node = HTMLElement>(window: Window, discriminator: boolean): JQueryStatic<FElement>;
/**
* Return a collection of matched elements either found in the DOM based on passed argument(s) or created
* by passing an HTML string.
*
* @param element_elementArray A DOM element to wrap in a jQuery object.
* An array containing a set of DOM elements to wrap in a jQuery object.
* @see {@link https://api.jquery.com/jQuery/}
* @since 1.0
*/
<T extends Element>(element_elementArray: T | ArrayLike<T>): JQuery<T>;
/**
* Return a collection of matched elements either found in the DOM based on passed argument(s) or created
* by passing an HTML string.
*
* @param selection An existing jQuery object to clone.
* @see {@link https://api.jquery.com/jQuery/}
* @since 1.0
*/
<T>(selection: JQuery<T>): JQuery<T>;
/**
* Accepts a string containing a CSS selector which is then used to match a set of elements.
*
* Creates DOM elements on the fly from the provided string of raw HTML.
*
* Binds a function to be executed when the DOM has finished loading.
*
* @param selector_object_callback A string containing a selector expression
* A DOM element to wrap in a jQuery object.
* An array containing a set of DOM elements to wrap in a jQuery object.
* A plain object to wrap in a jQuery object.
* An existing jQuery object to clone.
* A string of HTML to create on the fly. Note that this parses HTML, not XML.
* The function to execute when the DOM is ready.
* @see {@link https://api.jquery.com/jQuery/}
* @since 1.0
*/
(selector_object_callback: JQuery.Selector | JQuery.htmlString | ((this: Document, $: JQueryStatic<TElement>) => void)): JQuery<TElement>; // tslint:disable-line:unified-signatures
/**
* Return a collection of matched elements either found in the DOM based on passed argument(s) or created by passing an HTML string.
*
* @param object A plain object to wrap in a jQuery object.
* @see {@link https://api.jquery.com/jQuery/}
* @since 1.0
*/
<T extends JQuery.PlainObject>(object: T): JQuery<T>;
/**
* Returns an empty jQuery set.
*
* @see {@link https://api.jquery.com/jQuery/}
* @since 1.4
*/
(selector_object_callback?: JQuery.Selector | JQuery.htmlString | JQuery.TypeOrArray<Element> | JQuery |
JQuery.PlainObject | Window |
((this: Document, $: JQueryStatic<TElement>) => void)): JQuery<TElement>;
(): JQuery<TElement>;
/**
* A multi-purpose callbacks list object that provides a powerful way to manage callback lists.
*
@@ -659,7 +689,7 @@ interface JQueryStatic<TElement extends Node = HTMLElement> {
* @see {@link https://api.jquery.com/jQuery.map/}
* @since 1.0
*/
map<T, R>(array: T[], callback: (elementOfArray: T, indexInArray: number) => R): R[];
map<T, TReturn>(array: T[], callback: (this: Window, elementOfArray: T, indexInArray: number) => JQuery.TypeOrArray<TReturn> | null | undefined): TReturn[];
/**
* Translate all items in an array or object to new array of items.
*
@@ -671,7 +701,7 @@ interface JQueryStatic<TElement extends Node = HTMLElement> {
* @see {@link https://api.jquery.com/jQuery.map/}
* @since 1.6
*/
map<T, K extends keyof T, R>(obj: T, callback: (propertyOfObject: T[K], key: K) => R): R[];
map<T, K extends keyof T, TReturn>(obj: T, callback: (this: Window, propertyOfObject: T[K], key: K) => JQuery.TypeOrArray<TReturn> | null | undefined): TReturn[];
/**
* Merge the contents of two arrays together into the first array.
*
@@ -3098,7 +3128,7 @@ interface JQueryStatic<TElement extends Node = HTMLElement> {
when(...deferreds: any[]): JQuery.Promise<any, any, never>;
}
interface JQuery<TElement extends Node = HTMLElement> extends Iterable<TElement> {
interface JQuery<TElement = HTMLElement> extends Iterable<TElement> {
/**
* A string containing the jQuery version number.
*
@@ -4253,7 +4283,7 @@ interface JQuery<TElement extends Node = HTMLElement> extends Iterable<TElement>
* @see {@link https://api.jquery.com/map/}
* @since 1.2
*/
map(callback: (this: TElement, index: number, domElement: TElement) => any | any[] | null | undefined): this;
map<TReturn>(callback: (this: TElement, index: number, domElement: TElement) => JQuery.TypeOrArray<TReturn> | null | undefined): JQuery<TReturn>;
/**
* Bind an event handler to the "mousedown" JavaScript event, or trigger that event on an element.
*
@@ -6363,7 +6393,9 @@ declare namespace JQuery {
};
// Writable properties on XMLHttpRequest
interface XHRFields extends Partial<Pick<XMLHttpRequest, 'onreadystatechange' | 'responseType' | 'timeout' | 'withCredentials' | 'msCaching'>> { }
interface XHRFields extends Partial<Pick<XMLHttpRequest, 'onreadystatechange' | 'responseType' | 'timeout' | 'withCredentials'>> {
msCaching?: string;
}
}
interface Transport {
@@ -7922,9 +7954,9 @@ declare namespace JQuery {
// endregion
interface EventHandler<TCurrentTarget extends EventTarget, TData = null> extends EventHandlerBase<TCurrentTarget, JQuery.Event<TCurrentTarget, TData>> { }
interface EventHandler<TCurrentTarget, TData = null> extends EventHandlerBase<TCurrentTarget, JQuery.Event<TCurrentTarget, TData>> { }
interface EventHandlerBase<TContext extends object, T> {
interface EventHandlerBase<TContext, T> {
// Extra parameters can be passed from trigger()
(this: TContext, t: T, ...args: any[]): void | false | any;
}
+216 -17
View File
@@ -42,7 +42,7 @@ function JQueryStatic() {
// $ExpectType JQuery<HTMLElement>
$([new HTMLElement()]);
// $ExpectType JQuery<HTMLElement>
// $ExpectType JQuery<{ foo: string; hello: string; }>
$({ foo: 'bar', hello: 'world' });
// $ExpectType JQuery<HTMLElement>
@@ -58,6 +58,39 @@ function JQueryStatic() {
// $ExpectType JQuery<HTMLElement>
$();
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/19597#issuecomment-378218432
function issue_19597_378218432() {
let myDiv = $(document.createElement('div')); // gets default jQuery<HTMLElement> 8-/
// $ExpectType JQuery<HTMLDivElement>
myDiv;
myDiv.on('click', (evt) => {
let target = evt.target; // HTMLElement
// $ExpectType HTMLDivElement
target;
});
let myDiv1 = $<HTMLDivElement>(document.createElement('div')); // expected 0-2 Arguments but got 1. huh?
// let myDiv2 = $<HTMLDivElement, null>(document.createElement('div')); // expected 0-1 Arguments but got 2. huh?
let myForcedDiv: JQuery<HTMLDivElement> = $(document.createElement('div')) as any;
myForcedDiv.on('click', (evt) => {
let target = evt.target; // HTMLDivElement
// $ExpectType HTMLDivElement
target;
});
let myDoc = $(document); // gets default jQuery<HTMLElement>
// $ExpectType JQuery<Document>
myDoc;
myDoc.on('click', (evt) => {
let target = evt.target; // HTMLElement
// $ExpectType Document
target;
});
let myDocForced: JQuery<Document> = $(document); // type HTMLElement is not assignable to Type Document
let myWindow = $(window); // gets default jQuery<HTMLElement>
// $ExpectType JQuery<Window>
myWindow;
let myWindowForced: JQuery<Window> = $(window); // type Window does not satisfy contraint Node
}
}
function ajaxSettings() {
@@ -699,7 +732,9 @@ function JQueryStatic() {
function map() {
// $ExpectType number[]
$.map([1, 2, 3], (elementOfArray, indexInArray) => {
$.map([1, 2, 3], function (elementOfArray, indexInArray) {
// $ExpectType Window
this;
// $ExpectType number
elementOfArray;
// $ExpectType number
@@ -708,11 +743,49 @@ function JQueryStatic() {
return 200 + 10;
});
// $ExpectType number[]
$.map([1, 2, 3], function (elementOfArray, indexInArray) {
// $ExpectType Window
this;
// $ExpectType number
elementOfArray;
// $ExpectType number
indexInArray;
return [200, 10];
});
// $ExpectType (number | null)[]
$.map([1, 2, 3], function (elementOfArray, indexInArray) {
// $ExpectType Window
this;
// $ExpectType number
elementOfArray;
// $ExpectType number
indexInArray;
return [200, 10, null];
});
// $ExpectType (number | undefined)[]
$.map([1, 2, 3], function (elementOfArray, indexInArray) {
// $ExpectType Window
this;
// $ExpectType number
elementOfArray;
// $ExpectType number
indexInArray;
return [200, 10, undefined];
});
// $ExpectType (false | 1)[]
$.map({
myProp: true,
name: 'Rogers',
}, (propertyOfObject, key) => {
}, function (propertyOfObject, key) {
// $ExpectType Window
this;
// $ExpectType string | boolean
propertyOfObject;
// $ExpectType "myProp" | "name"
@@ -725,6 +798,67 @@ function JQueryStatic() {
return false;
}
});
// $ExpectType (string | number | boolean)[]
$.map({
myProp: true,
name: 'Rogers',
}, function (propertyOfObject, key) {
// $ExpectType Window
this;
// $ExpectType string | boolean
propertyOfObject;
// $ExpectType "myProp" | "name"
key;
return [propertyOfObject, 24];
});
// $ExpectType (false | 1)[]
$.map({
myProp: true,
name: 'Rogers',
anotherProp: 70,
}, function (propertyOfObject, key) {
// $ExpectType Window
this;
// $ExpectType string | number | boolean
propertyOfObject;
// $ExpectType "myProp" | "name" | "anotherProp"
key;
switch (key) {
case 'myProp':
return 1;
case 'name':
return false;
}
return null;
});
// $ExpectType (false | 1)[]
$.map({
myProp: true,
name: 'Rogers',
anotherProp: 70,
}, function (propertyOfObject, key) {
// $ExpectType Window
this;
// $ExpectType string | number | boolean
propertyOfObject;
// $ExpectType "myProp" | "name" | "anotherProp"
key;
switch (key) {
case 'myProp':
return 1;
case 'name':
return false;
}
return undefined;
});
}
function merge() {
@@ -2041,7 +2175,7 @@ function JQuery() {
function ajax() {
function ajaxComplete() {
// $ExpectType JQuery<HTMLElement>
// $ExpectType JQuery<Document>
$(document).ajaxComplete(function(event, jqXHR, ajaxOptions) {
// $ExpectType Document
this;
@@ -2057,7 +2191,7 @@ function JQuery() {
}
function ajaxError() {
// $ExpectType JQuery<HTMLElement>
// $ExpectType JQuery<Document>
$(document).ajaxError(function(event, jqXHR, ajaxSettings, thrownError) {
// $ExpectType Document
this;
@@ -2075,7 +2209,7 @@ function JQuery() {
}
function ajaxSend() {
// $ExpectType JQuery<HTMLElement>
// $ExpectType JQuery<Document>
$(document).ajaxSend(function(event, jqXHR, ajaxOptions) {
// $ExpectType Document
this;
@@ -2091,7 +2225,7 @@ function JQuery() {
}
function ajaxStart() {
// $ExpectType JQuery<HTMLElement>
// $ExpectType JQuery<Document>
$(document).ajaxStart(function() {
// $ExpectType Document
this;
@@ -2101,7 +2235,7 @@ function JQuery() {
}
function ajaxStop() {
// $ExpectType JQuery<HTMLElement>
// $ExpectType JQuery<Document>
$(document).ajaxStop(function() {
// $ExpectType Document
this;
@@ -2111,7 +2245,7 @@ function JQuery() {
}
function ajaxSuccess() {
// $ExpectType JQuery<HTMLElement>
// $ExpectType JQuery<Document>
$(document).ajaxSuccess(function(event, jqXHR, ajaxOptions, data) {
// $ExpectType Document
this;
@@ -5898,8 +6032,9 @@ function JQuery() {
}
function contents() {
// $ExpectType JQuery<HTMLElement | Comment | Text>
$('p').contents();
// TODO: Flaky test due to type ordering.
// // $ExpectType JQuery<HTMLElement | Comment | Text>
// $('p').contents();
}
function end() {
@@ -6131,7 +6266,7 @@ function JQuery() {
}
function map() {
// $ExpectType JQuery<HTMLElement>
// $ExpectType JQuery<string>
$('p').map(function(index, domElement) {
// $ExpectType HTMLElement
this;
@@ -6143,7 +6278,7 @@ function JQuery() {
return 'myVal';
});
// $ExpectType JQuery<HTMLElement>
// $ExpectType JQuery<string>
$('p').map(function(index, domElement) {
// $ExpectType HTMLElement
this;
@@ -6155,7 +6290,7 @@ function JQuery() {
return ['myVal1', 'myVal2'];
});
// $ExpectType JQuery<HTMLElement>
// $ExpectType JQuery<string | null>
$('p').map(function(index, domElement) {
// $ExpectType HTMLElement
this;
@@ -6164,10 +6299,10 @@ function JQuery() {
// $ExpectType HTMLElement
domElement;
return null;
return ['myVal1', 'myVal2', null];
});
// $ExpectType JQuery<HTMLElement>
// $ExpectType JQuery<string | undefined>
$('p').map(function(index, domElement) {
// $ExpectType HTMLElement
this;
@@ -6176,8 +6311,72 @@ function JQuery() {
// $ExpectType HTMLElement
domElement;
return undefined;
return ['myVal1', 'myVal2', undefined];
});
// $ExpectType JQuery<string>
$('p').map(function(index, domElement) {
// $ExpectType HTMLElement
this;
// $ExpectType number
index;
// $ExpectType HTMLElement
domElement;
let value: string;
if (index % 2 === 0) {
return null;
}
value = 'myVal';
return value;
});
// $ExpectType JQuery<string>
$('p').map(function(index, domElement) {
// $ExpectType HTMLElement
this;
// $ExpectType number
index;
// $ExpectType HTMLElement
domElement;
let value: string;
if (index % 2 === 0) {
return undefined;
}
value = 'myVal';
return value;
});
// // $ExpectType JQuery<never>
// $('p').map(function(index, domElement) {
// // $ExpectType HTMLElement
// this;
// // $ExpectType number
// index;
// // $ExpectType HTMLElement
// domElement;
//
// return null;
// });
// // $ExpectType JQuery<never>
// $('p').map(function(index, domElement) {
// // $ExpectType HTMLElement
// this;
// // $ExpectType number
// index;
// // $ExpectType HTMLElement
// domElement;
//
// return undefined;
// });
}
function slice() {
+1 -1
View File
@@ -3428,7 +3428,7 @@ function examples() {
function map_0() {
$('p')
.append($('input').map(function() {
return $(this).val();
return $(this).val() as string;
})
.get()
.join(', '));
@@ -1,5 +1,5 @@
import jq = require('jquery/dist/jquery.slim');
const $window = jq(window);
// $ExpectType JQuery<HTMLElement>
// $ExpectType JQuery<Window>
$window;
@@ -1,7 +1,7 @@
import jq = require('jquery');
const $window = jq(window);
// $ExpectType JQuery<HTMLElement>
// $ExpectType JQuery<Window>
$window;
class CanvasLayersDirective {
+1
View File
@@ -14,6 +14,7 @@
"no-empty-interface": false,
"no-misused-new": false,
"no-object-literal-type-assertion": false,
"no-redundant-jsdoc-2": false,
"no-unnecessary-generics": false,
"no-unnecessary-qualifier": false,
"no-unnecessary-type-assertion": false,
@@ -1,6 +1,6 @@
import * as materialize from "materialize-css";
const elem = document.querySelector('.whatever')!;
const elem = document.querySelector('.whatever') as HTMLElement;
M.textareaAutoResize(elem);
M.textareaAutoResize($(elem));
+3 -3
View File
@@ -26,7 +26,7 @@ export type JQueryAjaxSettingsBase =
/**
* Same as jQuery v3 `JQuery.EventHandlerBase`.
*/
export type JQueryEventHandlerBase<TContext extends object, T> =
export type JQueryEventHandlerBase<TContext, T> =
(this: TContext, t: T, ...args: any[]) => void | false;
/**
@@ -221,7 +221,7 @@ export interface Options<Result = DataFormat | GroupedDataFormat, RemoteResult =
// jQuery And Select2 Plugin
// --------------------------------------------------------------------------
export interface Select2Plugin<TElement extends Node = HTMLElement> {
export interface Select2Plugin<TElement = HTMLElement> {
amd: { require: Require; };
defaults: {
@@ -252,7 +252,7 @@ export interface Select2Plugin<TElement extends Node = HTMLElement> {
}
declare global {
interface JQuery<TElement extends Node = HTMLElement> {
interface JQuery<TElement = HTMLElement> {
select2: Select2Plugin<TElement>;
data(key: "select2"): Select2;