diff --git a/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java b/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java index e9bc91bc..33f4e946 100644 --- a/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java +++ b/src/main/java/com/google/javascript/clutz/DeclarationGenerator.java @@ -2564,14 +2564,18 @@ private boolean handleSpecialTTEFunctions( private String getSignatureForInstanceTTEFn( String propName, List classTemplateTypeNames, FunctionType ftype) { - // If ftype is foo.bar.Promise.prototype.then, extract className as Promise. - String className = getUnqualifiedName(getNamespace(getNamespace(ftype.getDisplayName()))); + // If ftype is foo.bar.Promise.prototype.then, extract className as ಠ_ಠ.clutz.foo.bar.Promise. + String className = + Constants.INTERNAL_NAMESPACE + "." + getNamespace(getNamespace(ftype.getDisplayName())); Iterator templateTypeNames = classTemplateTypeNames.iterator(); if (!templateTypeNames.hasNext()) return null; String templateVarName = templateTypeNames.next(); - String classTemplatizedType = className + " < RESULT >"; + // TODO(lucassloan): goog.Promise has bad types (caused by an inconsistent number of generic type + // params) that are coerced to any, so explicitly emit any and fix when the callers have been fixed. + String classTemplatizedType = + className.equals("ಠ_ಠ.clutz.goog.Promise") ? " any" : className + " < RESULT >"; if (propName.equals("then")) { return "then < RESULT > (opt_onFulfilled ? : ( (a : " + templateVarName @@ -2595,8 +2599,8 @@ private String getSignatureForInstanceTTEFn( } private String getSignatureForStaticTTEFn(String propName, FunctionType ftype) { - // If ftype is foo.bar.Promise.all, extract className as Promise. - String className = getUnqualifiedName(getNamespace(ftype.getDisplayName())); + // If ftype is foo.bar.Promise.all, extract className as ಠ_ಠ.clutz.foo.bar.Promise. + String className = Constants.INTERNAL_NAMESPACE + "." + getNamespace(ftype.getDisplayName()); String maybePromiseMethod = getPromiseMethod(propName, className); if (maybePromiseMethod != null) { return "static " + maybePromiseMethod; @@ -2607,7 +2611,14 @@ private String getSignatureForStaticTTEFn(String propName, FunctionType ftype) { private String getPromiseMethod(String propName, String className) { switch (propName) { case "resolve": - return "resolve < T >(value: " + className + " < T > | T): " + className + " < T >;"; + // TODO(lucassloan): goog.Promise has bad types that are coerced to any, so explicitly emit any + // and change to the proper type `(value: googPromise< T , any > | T): googPromise` + // when the callers have been fixed. + if (className.equals("ಠ_ಠ.clutz.goog.Promise")) { + return "resolve < T >(value: " + className + " < T , any > | T): any;"; + } else { + return "resolve < T >(value: " + className + " < T > | T): " + className + " < T >;"; + } case "race": return "race < T > (values : T [] ) : " + className + " < T > ;"; // TODO(rado): angular.d.ts has improved types for .all, replace with all overrides from diff --git a/src/test/java/com/google/javascript/clutz/DeclarationSyntaxTest.java b/src/test/java/com/google/javascript/clutz/DeclarationSyntaxTest.java index d7aeba47..8ae7fc73 100644 --- a/src/test/java/com/google/javascript/clutz/DeclarationSyntaxTest.java +++ b/src/test/java/com/google/javascript/clutz/DeclarationSyntaxTest.java @@ -85,7 +85,10 @@ public boolean accept(File dir, String name) { "--lib", "es5,dom,es2015.iterable", "--noImplicitAny", - "--strictNullChecks"); + "--strictNullChecks", + // TODO(lucassloan): Necessary to allow promise like things that extend other promise like + // things. Turn off when turned off in g3 + "--noStrictGenericChecks"); @Test public void testDeclarationSyntax() throws Exception { diff --git a/src/test/java/com/google/javascript/clutz/goog_promise_with_platform.d.ts b/src/test/java/com/google/javascript/clutz/goog_promise_with_platform.d.ts new file mode 100644 index 00000000..33c3b2ed --- /dev/null +++ b/src/test/java/com/google/javascript/clutz/goog_promise_with_platform.d.ts @@ -0,0 +1,30 @@ +declare namespace ಠ_ಠ.clutz.goog { + class Promise < TYPE , RESOLVER_CONTEXT > extends Promise_Instance < TYPE , RESOLVER_CONTEXT > { + static all < TYPE > (promises : any [] ) : ಠ_ಠ.clutz.goog.Promise < TYPE [] , any > ; + static race < TYPE > (promises : any [] ) : ಠ_ಠ.clutz.goog.Promise < TYPE , any > ; + static resolve < T >(value: ಠ_ಠ.clutz.goog.Promise < T , any > | T): any; + } + class Promise_Instance < TYPE , RESOLVER_CONTEXT > implements ಠ_ಠ.clutz.goog.Thenable < TYPE > { + private noStructuralTyping_: any; + constructor (resolver : (a : (a ? : TYPE | PromiseLike < TYPE > | null | { then : any } ) => any , b : (a ? : any ) => any ) => void , opt_context ? : RESOLVER_CONTEXT ) ; + then < RESULT > (opt_onFulfilled ? : ( (a : TYPE ) => any | RESULT ) | null , opt_onRejected ? : ( (a : any ) => any ) | null) : any ; + } +} +declare module 'goog:goog.Promise' { + import alias = ಠ_ಠ.clutz.goog.Promise; + export default alias; +} +declare namespace ಠ_ಠ.clutz.goog { + interface Thenable < TYPE > extends PromiseLike < TYPE > { + then < RESULT > (opt_onFulfilled ? : ( (a : TYPE ) => ಠ_ಠ.clutz.goog.Thenable < RESULT > | RESULT ) | null , opt_onRejected ? : ( (a : any ) => any ) | null) : ಠ_ಠ.clutz.goog.Thenable < RESULT > ; + } +} +declare namespace ಠ_ಠ.clutz.goog.Thenable { + var IMPLEMENTED_BY_PROP : string ; + function addImplementation (ctor : { new ( ...a : any [] ) : ಠ_ಠ.clutz.goog.Thenable < any > } ) : void ; + function isImplementedBy (object : any ) : boolean ; +} +declare module 'goog:goog.Thenable' { + import alias = ಠ_ಠ.clutz.goog.Thenable; + export default alias; +} diff --git a/src/test/java/com/google/javascript/clutz/goog_promise_with_platform.js b/src/test/java/com/google/javascript/clutz/goog_promise_with_platform.js new file mode 100644 index 00000000..9edb3c9e --- /dev/null +++ b/src/test/java/com/google/javascript/clutz/goog_promise_with_platform.js @@ -0,0 +1,132 @@ +//!! goog.Promise (https://github.com/google/closure-library/blob/master/closure/goog/promise/promise.js), +//!! unlike other implementations of Promise, has 2 template params this test ensures +//!! that the specially handled TTE methods work for it as well +goog.provide('goog.Promise'); +goog.provide('goog.Thenable'); + +/** + * @interface + * @extends {IThenable} + * @template TYPE + */ +goog.Thenable = function() {}; + + +/** + * @param {?(function(this:THIS, TYPE): VALUE)=} opt_onFulfilled A + * function that will be invoked with the fulfillment value if the Promise + * is fulfilled. + * @param {?(function(this:THIS, *): *)=} opt_onRejected A function that will + * be invoked with the rejection reason if the Promise is rejected. + * @param {THIS=} opt_context An optional context object that will be the + * execution context for the callbacks. By default, functions are executed + * with the default this. + * + * @return {RESULT} A new Promise that will receive the result + * of the fulfillment or rejection callback. + * @template VALUE + * @template THIS + * + * When a Promise (or thenable) is returned from the fulfilled callback, + * the result is the payload of that promise, not the promise itself. + * + * @template RESULT := type('goog.Promise', + * cond(isUnknown(VALUE), unknown(), + * mapunion(VALUE, (V) => + * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'), + * templateTypeOf(V, 0), + * cond(sub(V, 'Thenable'), + * unknown(), + * V))))) + * =: + * + */ +goog.Thenable.prototype.then = function( + opt_onFulfilled, opt_onRejected, opt_context) {}; + + +/** + * @const + */ +goog.Thenable.IMPLEMENTED_BY_PROP = '$goog_Thenable'; + + +/** + * @param {function(new:goog.Thenable,...?)} ctor The class constructor. The + * corresponding class must have already implemented the interface. + */ +goog.Thenable.addImplementation = function(ctor) {}; + + +/** + * @param {?} object + * @return {boolean} Whether a given instance implements `goog.Thenable`. + * The class/superclass of the instance must call `addImplementation`. + */ +goog.Thenable.isImplementedBy = function(object) {}; + +/** + * @param {function( + * this:RESOLVER_CONTEXT, + * function((TYPE|IThenable|Thenable)=), + * function(*=)): void} resolver + * Initialization function that is invoked immediately with `resolve` + * and `reject` functions as arguments. The Promise is resolved or + * rejected with the first argument passed to either function. + * @param {RESOLVER_CONTEXT=} opt_context An optional context for executing the + * resolver function. If unspecified, the resolver function will be executed + * in the default scope. + * @constructor + * @struct + * @final + * @implements {goog.Thenable} + * @template TYPE,RESOLVER_CONTEXT + */ +goog.Promise = function(resolver, opt_context) {}; + +/** + * @param {VALUE=} opt_value + * @return {RESULT} A new Promise that is immediately resolved + * with the given value. If the input value is already a goog.Promise, it + * will be returned immediately without creating a new instance. + * @template VALUE + * @template RESULT := type('goog.Promise', + * cond(isUnknown(VALUE), unknown(), + * mapunion(VALUE, (V) => + * cond(isTemplatized(V) && sub(rawTypeOf(V), 'IThenable'), + * templateTypeOf(V, 0), + * cond(sub(V, 'Thenable'), + * unknown(), + * V))))) + * =: + */ +goog.Promise.resolve = function(opt_value) {}; + +/** + * @param {!Array|goog.Thenable|Thenable|*)>} + * promises + * @return {!goog.Promise} A Promise that receives the result of the + * first Promise (or Promise-like) input to settle immediately after it + * settles. + * @template TYPE + */ +goog.Promise.race = function(promises) {}; + + +/** + * @param {!Array|goog.Thenable|Thenable|*)>} + * promises + * @return {!goog.Promise>} A Promise that receives a list of + * every fulfilled value once every input Promise (or Promise-like) is + * successfully fulfilled, or is rejected with the first rejection reason + * immediately after it is rejected. + * @template TYPE + */ +goog.Promise.all = function(promises) {}; + +/** + * @override + */ +goog.Promise.prototype.then = function( + opt_onFulfilled, opt_onRejected, opt_context) {}; +goog.Thenable.addImplementation(goog.Promise); \ No newline at end of file diff --git a/src/test/java/com/google/javascript/clutz/partial/tte_promise_partial.d.ts b/src/test/java/com/google/javascript/clutz/partial/tte_promise_partial.d.ts index 6930ab7f..a33b377f 100644 --- a/src/test/java/com/google/javascript/clutz/partial/tte_promise_partial.d.ts +++ b/src/test/java/com/google/javascript/clutz/partial/tte_promise_partial.d.ts @@ -3,7 +3,7 @@ declare namespace ಠ_ಠ.clutz.module$exports$tte$Promise$Partial { } class PartialDeferred_Instance < VALUE = any > extends ಠ_ಠ.clutz.Base < VALUE > { constructor ( ) ; - then < RESULT > (opt_onFulfilled ? : ( (a : VALUE ) => PartialDeferred < RESULT > | RESULT ) | null , opt_onRejected ? : ( (a : any ) => any ) | null) : PartialDeferred < RESULT > ; + then < RESULT > (opt_onFulfilled ? : ( (a : VALUE ) => ಠ_ಠ.clutz.module$exports$tte$Promise$Partial.PartialDeferred < RESULT > | RESULT ) | null , opt_onRejected ? : ( (a : any ) => any ) | null) : ಠ_ಠ.clutz.module$exports$tte$Promise$Partial.PartialDeferred < RESULT > ; } } declare module 'goog:tte.Promise.Partial' { diff --git a/src/test/java/com/google/javascript/clutz/tte_promise.d.ts b/src/test/java/com/google/javascript/clutz/tte_promise.d.ts index d6ed5f75..a45f1240 100644 --- a/src/test/java/com/google/javascript/clutz/tte_promise.d.ts +++ b/src/test/java/com/google/javascript/clutz/tte_promise.d.ts @@ -1,13 +1,13 @@ declare namespace ಠ_ಠ.clutz.tte { class Promise < T > extends Promise_Instance < T > { - static all(promises : Promise < any > [] ) : Promise < any [] > ; - static race < T > (values : T [] ) : Promise < T > ; - static resolve < T >(value: Promise < T > | T): Promise < T >; + static all(promises : ಠ_ಠ.clutz.tte.Promise < any > [] ) : ಠ_ಠ.clutz.tte.Promise < any [] > ; + static race < T > (values : T [] ) : ಠ_ಠ.clutz.tte.Promise < T > ; + static resolve < T >(value: ಠ_ಠ.clutz.tte.Promise < T > | T): ಠ_ಠ.clutz.tte.Promise < T >; } class Promise_Instance < T > { private noStructuralTyping_: any; - then < RESULT > (opt_onFulfilled ? : ( (a : T ) => Promise < RESULT > | RESULT ) | null , opt_onRejected ? : ( (a : any ) => any ) | null) : Promise < RESULT > ; - when < RESULT, T > (value: T, successCallback: (promiseValue: T) => Promise < RESULT >|RESULT, errorCallback: null | undefined | ((reason: any) => any), notifyCallback?: (state: any) => any): Promise < RESULT >; + then < RESULT > (opt_onFulfilled ? : ( (a : T ) => ಠ_ಠ.clutz.tte.Promise < RESULT > | RESULT ) | null , opt_onRejected ? : ( (a : any ) => any ) | null) : ಠ_ಠ.clutz.tte.Promise < RESULT > ; + when < RESULT, T > (value: T, successCallback: (promiseValue: T) => ಠ_ಠ.clutz.tte.Promise < RESULT >|RESULT, errorCallback: null | undefined | ((reason: any) => any), notifyCallback?: (state: any) => any): ಠ_ಠ.clutz.tte.Promise < RESULT >; } } declare module 'goog:tte.Promise' { @@ -19,7 +19,7 @@ declare namespace ಠ_ಠ.clutz.tte { } class PromiseService_Instance < T > { private noStructuralTyping_: any; - all(promises : PromiseService.Promise < any > [] ) : PromiseService.Promise < any [] > ; + all(promises : ಠ_ಠ.clutz.tte.PromiseService.Promise < any > [] ) : ಠ_ಠ.clutz.tte.PromiseService.Promise < any [] > ; } } declare namespace ಠ_ಠ.clutz.tte.PromiseService {