From a8486a33eababbecc2acd6fa62cf6c6e63947ba9 Mon Sep 17 00:00:00 2001 From: Geoff Cameron Date: Wed, 1 Aug 2018 15:24:43 -0400 Subject: [PATCH 1/3] Make typings compatible with ES6 promises Since ES6 promises don't implement finally, trying to $q.when a third party promises fails typings. This introduces a new type that is compatible with this, to allow for typings to work when taking an ES6 promise. Totally willing to change the name to something more sensible here --- types/angular/index.d.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/types/angular/index.d.ts b/types/angular/index.d.ts index 450d381b6d..42200cf69f 100644 --- a/types/angular/index.d.ts +++ b/types/angular/index.d.ts @@ -1137,12 +1137,12 @@ declare namespace angular { * * @param value Value or a promise */ - resolve(value: IPromise|T): IPromise; + resolve(value: IPromiseGlobal|T): IPromise; /** * @deprecated Since TS 2.4, inference is stricter and no longer produces the desired type when T1 !== T2. * To use resolve with two different types, pass a union type to the single-type-argument overload. */ - resolve(value: IPromise|T2): IPromise; + resolve(value: IPromiseGlobal|T2): IPromise; /** * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted. */ @@ -1152,11 +1152,11 @@ declare namespace angular { * * @param value Value or a promise */ - when(value: IPromise|T): IPromise; - when(value: IPromise|T2): IPromise; - when(value: IPromise|T, successCallback: (promiseValue: T) => IPromise|TResult): IPromise; + when(value: IPromiseGlobal|T): IPromise; + when(value: IPromiseGlobal|T2): IPromise; + when(value: IPromiseGlobal|T, successCallback: (promiseValue: T) => IPromise|TResult): IPromise; when(value: T, successCallback: (promiseValue: T) => IPromise|TResult, errorCallback: null | undefined | ((reason: any) => any), notifyCallback?: (state: any) => any): IPromise; - when(value: IPromise, successCallback: (promiseValue: T) => IPromise|TResult, errorCallback: (reason: any) => TResult2 | IPromise, notifyCallback?: (state: any) => any): IPromise; + when(value: IPromiseGlobal, successCallback: (promiseValue: T) => IPromise|TResult, errorCallback: (reason: any) => TResult2 | IPromise, notifyCallback?: (state: any) => any): IPromise; /** * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted. */ @@ -1182,7 +1182,11 @@ declare namespace angular { errorOnUnhandledRejections(value: boolean): IQProvider; } - interface IPromise { + /** + * Spec that all promises should adhere to, for typing reasons -- so when and resolve can take + * 3rd party promises as arguments (ES6 Promises don't have finally) + */ + interface IPromiseGlobal { /** * Regardless of when the promise was or will be resolved or rejected, then calls one of * the success or error callbacks asynchronously as soon as the result is available. The @@ -1214,7 +1218,9 @@ declare namespace angular { | ((reason: any) => IPromise | IPromise | TResult) | null ): IPromise; + } + interface IPromise extends IPromiseGlobal { /** * Allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful to release resources or do some clean-up that needs to be done whether the promise was rejected or resolved. See the full specification for more information. * From 322c5e6cad5425511f721159d79b88c291b1cdb0 Mon Sep 17 00:00:00 2001 From: Geoff Cameron Date: Fri, 3 Aug 2018 19:34:11 -0400 Subject: [PATCH 2/3] Use real promise base class --- types/angular/index.d.ts | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/types/angular/index.d.ts b/types/angular/index.d.ts index 42200cf69f..1f7437a02d 100644 --- a/types/angular/index.d.ts +++ b/types/angular/index.d.ts @@ -1137,12 +1137,12 @@ declare namespace angular { * * @param value Value or a promise */ - resolve(value: IPromiseGlobal|T): IPromise; + resolve(value: PromiseLike|T): IPromise; /** * @deprecated Since TS 2.4, inference is stricter and no longer produces the desired type when T1 !== T2. * To use resolve with two different types, pass a union type to the single-type-argument overload. */ - resolve(value: IPromiseGlobal|T2): IPromise; + resolve(value: PromiseLike|T2): IPromise; /** * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted. */ @@ -1152,11 +1152,11 @@ declare namespace angular { * * @param value Value or a promise */ - when(value: IPromiseGlobal|T): IPromise; - when(value: IPromiseGlobal|T2): IPromise; - when(value: IPromiseGlobal|T, successCallback: (promiseValue: T) => IPromise|TResult): IPromise; + when(value: PromiseLike|T): IPromise; + when(value: PromiseLike|T2): IPromise; + when(value: PromiseLike|T, successCallback: (promiseValue: T) => IPromise|TResult): IPromise; when(value: T, successCallback: (promiseValue: T) => IPromise|TResult, errorCallback: null | undefined | ((reason: any) => any), notifyCallback?: (state: any) => any): IPromise; - when(value: IPromiseGlobal, successCallback: (promiseValue: T) => IPromise|TResult, errorCallback: (reason: any) => TResult2 | IPromise, notifyCallback?: (state: any) => any): IPromise; + when(value: PromiseLike, successCallback: (promiseValue: T) => IPromise|TResult, errorCallback: (reason: any) => TResult2 | IPromise, notifyCallback?: (state: any) => any): IPromise; /** * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted. */ @@ -1182,11 +1182,7 @@ declare namespace angular { errorOnUnhandledRejections(value: boolean): IQProvider; } - /** - * Spec that all promises should adhere to, for typing reasons -- so when and resolve can take - * 3rd party promises as arguments (ES6 Promises don't have finally) - */ - interface IPromiseGlobal { + interface IPromise { /** * Regardless of when the promise was or will be resolved or rejected, then calls one of * the success or error callbacks asynchronously as soon as the result is available. The @@ -1218,9 +1214,7 @@ declare namespace angular { | ((reason: any) => IPromise | IPromise | TResult) | null ): IPromise; - } - interface IPromise extends IPromiseGlobal { /** * Allows you to observe either the fulfillment or rejection of a promise, but to do so without modifying the final value. This is useful to release resources or do some clean-up that needs to be done whether the promise was rejected or resolved. See the full specification for more information. * From ddb90ccfe1024100a686b271b7a27a300a0a9041 Mon Sep 17 00:00:00 2001 From: Geoff Cameron Date: Mon, 13 Aug 2018 14:18:26 -0400 Subject: [PATCH 3/3] Add tests --- types/angular/angular-tests.ts | 14 ++++++++++++++ types/angular/index.d.ts | 6 +++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/types/angular/angular-tests.ts b/types/angular/angular-tests.ts index f2aad6bb59..44f8539af2 100644 --- a/types/angular/angular-tests.ts +++ b/types/angular/angular-tests.ts @@ -311,8 +311,10 @@ namespace TestQ { } const abcObject: AbcObject = null; const abcObjectPromise: angular.IPromise = null; + const abcObjectPromiseLike: PromiseLike = null; const efObject: EfObject = null; const efObjectPromise: angular.IPromise = null; + const efObjectPromiseLike: PromiseLike = null; const ghObject: GhObject = null; const ghObjectPromise: angular.IPromise = null; @@ -404,6 +406,7 @@ namespace TestQ { result = $q.when(abcObject); result = $q.when(abcObjectPromise); + result = $q.when(abcObjectPromiseLike); result = $q.when(efObject, (result: EfObject) => abcObject); result = $q.when(efObject, (result: EfObject) => abcObject, (any) => any); @@ -416,10 +419,21 @@ namespace TestQ { resultOther = $q.when(efObjectPromise, (result: EfObject) => abcObject, (any) => ghObjectPromise); resultOther = $q.when(efObjectPromise, (result: EfObject) => abcObject, (any) => ghObjectPromise, (any) => any); + result = $q.when(efObjectPromiseLike, (result: EfObject) => abcObject); + resultOther = $q.when(efObjectPromiseLike, (result: EfObject) => abcObject, (any) => ghObject); + resultOther = $q.when(efObjectPromiseLike, (result: EfObject) => abcObject, (any) => ghObject); + resultOther = $q.when(efObjectPromiseLike, (result: EfObject) => abcObject, (any) => ghObject, (any) => any); + resultOther = $q.when(efObjectPromiseLike, (result: EfObject) => abcObject, (any) => ghObjectPromise); + resultOther = $q.when(efObjectPromiseLike, (result: EfObject) => abcObject, (any) => ghObjectPromise, (any) => any); + result = $q.when(efObject, (result: EfObject) => abcObjectPromise); result = $q.when(efObject, (result: EfObject) => abcObjectPromise, (any) => any); result = $q.when(efObject, (result: EfObject) => abcObjectPromise, (any) => any, (any) => any); + result = $q.when(efObject, (result: EfObject) => abcObjectPromiseLike); + result = $q.when(efObject, (result: EfObject) => abcObjectPromiseLike, (any) => any); + result = $q.when(efObject, (result: EfObject) => abcObjectPromiseLike, (any) => any, (any) => any); + result = $q.when(efObjectPromise, (result: EfObject) => abcObjectPromise); resultOther = $q.when(efObjectPromise, (result: EfObject) => abcObjectPromise, (any) => ghObject); resultOther = $q.when(efObjectPromise, (result: EfObject) => abcObjectPromise, (any) => ghObject, (any) => any); diff --git a/types/angular/index.d.ts b/types/angular/index.d.ts index 1f7437a02d..99977b57b5 100644 --- a/types/angular/index.d.ts +++ b/types/angular/index.d.ts @@ -1154,9 +1154,9 @@ declare namespace angular { */ when(value: PromiseLike|T): IPromise; when(value: PromiseLike|T2): IPromise; - when(value: PromiseLike|T, successCallback: (promiseValue: T) => IPromise|TResult): IPromise; - when(value: T, successCallback: (promiseValue: T) => IPromise|TResult, errorCallback: null | undefined | ((reason: any) => any), notifyCallback?: (state: any) => any): IPromise; - when(value: PromiseLike, successCallback: (promiseValue: T) => IPromise|TResult, errorCallback: (reason: any) => TResult2 | IPromise, notifyCallback?: (state: any) => any): IPromise; + when(value: PromiseLike|T, successCallback: (promiseValue: T) => PromiseLike|TResult): IPromise; + when(value: T, successCallback: (promiseValue: T) => PromiseLike|TResult, errorCallback: null | undefined | ((reason: any) => any), notifyCallback?: (state: any) => any): IPromise; + when(value: PromiseLike, successCallback: (promiseValue: T) => PromiseLike|TResult, errorCallback: (reason: any) => TResult2 | PromiseLike, notifyCallback?: (state: any) => any): IPromise; /** * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted. */