angular / clutz Goto Github PK
View Code? Open in Web Editor NEWClosure to TypeScript `.d.ts` generator
License: MIT License
Closure to TypeScript `.d.ts` generator
License: MIT License
We are writing eg var VOID_TAGS_ : Object < string , boolean > ;
but this is not valid TS.
Could be var VOID_TAGS_ : { [s: string]: string; };
instead
listenable.js
/**
* @param {string|!goog.events.EventId<EVENTOBJ>} type The type of the
* listeners to fire.
* @param {boolean} capture The capture mode of the listeners to fire.
* @param {EVENTOBJ} eventObject The event object to fire.
* @return {boolean} Whether all listeners succeeded without
* attempting to prevent default behavior. If any listener returns
* false or called goog.events.Event#preventDefault, this returns
* false.
* @template EVENTOBJ
*/
goog.events.Listenable.prototype.fireListeners;
eventtarget.js
/*
* @implements {goog.events.Listenable}
*/
goog.events.EventTarget = function() {}
/** @override */
goog.events.EventTarget.prototype.fireListeners = function(
type, capture, eventObject) {
}
.d.ts:
class EventTarget implements ಠ_ಠ.cl2dts_internal.Unknown {
fireListeners (a : any , b : any , c : any ) : any ;
Instead we should know the types from the interface being implemented.
Currently we output type aliases like this:
type goog.fx.TransitionBase.State = number ;
Will need to check enum branch and type alias branch.
/**
* Creates the composition of the functions passed in.
* For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)).
* @param {function(...?):T} fn The final function.
* @param {...Function} var_args A list of functions.
* @return {function(...?):T} The composition of all inputs.
* @template T
*/
goog.functions.compose = function(fn, var_args) {}
function compose < T > (fn : ( ...a : any [] ) => T , ...var_args : ( ...a : any [] ) => any [] ) : ( ...a : any [] ) => T ;
error:
/ac.jslib.d.ts:194:NaN: A rest parameter must be of an array type.
Get lots of Duplicate identifier still.
/javascript/closure/ui/ac/ac.jslib.d.ts:3202:NaN: Duplicate identifier 'fx'.
/javascript/closure/ui/ac/ac.jslib.d.ts:3220:NaN: Duplicate identifier 'fx'.
goog.provide("goog.fx.dom");
goog.provide("goog.fx.dom.ColorTransform");
/** @type {number} */
goog.fx.dom.a = 1;
/** @constructor */
goog.fx.dom.ColorTransform = function() {}
we visit the ColorTransform symbol twice: once for the first provide (which is non-default so we traverse all the symbols) and again for the second provide, where it is the default.
js:
goog.provide('goog.color.names');
goog.color.names = {
'aliceblue': '#f0f8ff',
'antiquewhite': '#faebd7'
};
error:
java.lang.IllegalArgumentException: goog.provide not defined: goog.color.names
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:145)
at com.google.javascript.cl2dts.DeclarationGenerator.produceDts(DeclarationGenerator.java:153)
in the debugger, can see
topScope.getOwnSlot("goog.color") -> Var goog.color{{names: {aliceblue: string, antiquewhite: string}}}
topScope.getOwnSlot("goog.color.names") -> null
topScope.getOwnSlot("goog.color.names.aliceblue") -> Var goog.color.names.aliceblue{string}
In TS the following is an error
declare namespace foo {
type Foo = number;
}
typeof foo;
clutz should not emit goog.require overloads for such namespace.
Not sure why not yet. It doesn't have any forward declares that I see.
in closure
/**
* @typedef {A}
*/
goog.namespace.foo;
should output
declare namespace internal.goog.namespace {
type foo = A;
}
...
The following simple definitions transpile to classes that extend tempCtor.
/**
* To assert to the compiler that an operation is needed when it would
* otherwise be stripped. For example:
* <code>
* // Force a layout
* goog.reflect.sinkValue(dialog.offsetHeight);
* </code>
* @type {!Function}
*/
goog.reflect.sinkValue = function(x) {
goog.reflect.sinkValue[' '](x);
return x;
};
and
/**
* @type {!Function}
* @throws {Error} when invoked to indicate the method should be overridden.
*/
goog.abstractMethod = function() {
throw Error('unimplemented abstract method');
};
outputs
class sinkValue extends tempCtor
Caused by: java.lang.RuntimeException: Failed to emit type goog.ui.ac.Renderer
at com.google.javascript.cl2dts.DeclarationGenerator$TreeWalker.visitType(DeclarationGenerator.java:725)
at com.google.javascript.cl2dts.DeclarationGenerator$TreeWalker.visitUnionType(DeclarationGenerator.java:773)
at com.google.javascript.cl2dts.DeclarationGenerator$TreeWalker.access$1100(DeclarationGenerator.java:388)
at com.google.javascript.cl2dts.DeclarationGenerator$TreeWalker$1.caseUnionType(DeclarationGenerator.java:606)
at com.google.javascript.cl2dts.DeclarationGenerator$TreeWalker$1.caseUnionType(DeclarationGenerator.java:566)
at com.google.javascript.rhino.jstype.UnionType.visit(UnionType.java:601)
at com.google.javascript.cl2dts.DeclarationGenerator$TreeWalker.visitType(DeclarationGenerator.java:723)
... 24 more
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: -1
at java.lang.String.substring(String.java:1918)
at com.google.javascript.cl2dts.DeclarationGenerator$TreeWalker.getRelativeName(DeclarationGenerator.java:401)
at com.google.javascript.cl2dts.DeclarationGenerator$TreeWalker.access$1000(DeclarationGenerator.java:388)
at com.google.javascript.cl2dts.DeclarationGenerator$TreeWalker$1.caseObjectType(DeclarationGenerator.java:597)
at com.google.javascript.cl2dts.DeclarationGenerator$TreeWalker$1.caseObjectType(DeclarationGenerator.java:566)
at com.google.javascript.rhino.jstype.ObjectType.visit(ObjectType.java:524)
at com.google.javascript.cl2dts.DeclarationGenerator$TreeWalker.visitType(DeclarationGenerator.java:723)
* @const
* @dict
* @private
*/
goog.events.onStringMap_ = {};
produces
var onStringMap_ : null ;
For example IThenable interface:
https://github.com/google/closure-compiler/blob/master/externs/es6.js#L884
We need to produce lib.es6.closure.d.ts which contains the difference.
When running tests on OSX with JDK8, the order of declarations in the .d.ts files differs from the golden files.
in https://github.com/google/closure-library/blob/master/closure/goog/events/wheelevent.js
we see
/**
* @param {goog.events.WheelEvent.DeltaMode} deltaMode The delta mode units of
* the wheel event.
* @constructor
*/
goog.events.WheelEvent = function(
browserEvent, deltaMode, deltaX, deltaY, deltaZ) {
We emit
declare namespace ಠ_ಠ.cl2dts_internal.goog.events {
class WheelEvent extends BrowserEvent {
constructor (browserEvent : Event , deltaMode : WheelEvent.DeltaMode , deltaX : number , deltaY : number , deltaZ : number ) ;
deltaMode : WheelEvent.DeltaMode ;
type : WheelEvent.EventType ;
static PIXELS_PER_PAGE_ : number ;
/* not emitting EventType because it is an enum and it is not provided */
/* not emitting DeltaMode because it is an enum and it is not provided */
static PIXELS_PER_LINE_ : number ;
}
}
declare module 'goog:goog.events.WheelEvent' {
import alias = ಠ_ಠ.cl2dts_internal.goog.events.WheelEvent;
export default alias;
}
And this is broken because DeltaMode and EventType are omitted and don't appear anywhere else.
Similar to the report in #71 and probably broken by our fix to #69
* @const
* @private
*/
goog.userAgent.isVersionOrHigherCache_ = {};
emits a literal null (or fails with NPE with error-checking)
In caseTemplatizedType(DeclarationGenerator.java:364)
, e.g. due to an Array<string>
.
in provide_single_class.js
/**
* @param {foo.bar.Baz} b
* @return {boolean}
*/
foo.bar.Baz.prototype.equals = function(b) {
return false;
};
emits
declare namespace ಠ_ಠ.cl2dts_internal.foo.bar {
class Baz {
field : string ;
equals (b : foo.bar.Baz ) : boolean ;
method (a : string ) : number ;
static staticMethod (a : string ) : number ;
}
}
the Baz name is not correctly qualified, should probably be bare Baz
or maybe fully-qualified with the look of disapproval
like
declare namespace ಠ_ಠ.cl2dts_internal.goog.dom {
type goog.dom.TagName = string ;
var goog.dom.TagName : {
types can't have dotted names
/**
* @param {string|!goog.events.EventId<EVENTOBJ>} type The event type id.
* @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
* method.
* @param {boolean=} opt_useCapture Whether to fire in capture phase
* (defaults to false).
* @param {SCOPE=} opt_listenerScope Object in whose scope to call the
* listener.
* @return {goog.events.ListenableKey} Unique key for the listener.
* @template SCOPE,EVENTOBJ
*/
goog.events.Listenable.prototype.listen;
produces
listen (a : ( string | EventId < EVENTOBJ > ) , b : (a : EVENTOBJ ) => boolean , c ? : boolean , d ? : ( ) ) : goog.events.ListenableKey ;
but the EVENTOBJ
template parameter was undeclared
/**
* Base class for custom error objects.
* @param {*=} opt_msg The message associated with the error.
* @constructor
* @extends {Error}
*/
goog.debug.Error = function(opt_msg) {...}
outputs
declare namespace ಠ_ಠ.cl2dts_internal.goog.debug {
class Error extends Error { ... }
}
which makes TS sad.
Currently the closure code below produces (AFAICT) only {[k: string]: any}
. We should include all other defined constructor properties in the generated type, and only include the property accessor on top of that.
E.g.:
/** @dict @constructor */
X = function() {};
X.prototype.foo = function() {};
Should be:
class X {
[k: string]: any;
foo();
}
/**
* An interface that describes a single registered listener.
* @interface
*/
goog.events.ListenableKey = function() {};
/**
* Reserves a key to be used for ListenableKey#key field.
* @return {number} A number to be used to fill ListenableKey#key
* field.
*/
goog.events.ListenableKey.reserveKey = function() {
return ++goog.events.ListenableKey.counter_;
};
produces
interface ListenableKey {
static reserveKey ( ) : number ;
}
but TS doesn't allow static method declarations on an interface
see 'goog.events.EventWrapper', needs further investigation on why is it skipped. I don't see anything special about it.
see goog.fx.Transition (interface) and goog.fx.Transition.EventType (enum).
Output is
declare namespace ಠ_ಠ.cl2dts_internal.goog.fx {
interface Transition {
play : any ;
stop : any ;
}
var Transition : { EventType : Transition.EventType }
}
declare namespace ಠ_ಠ.cl2dts_internal.goog.fx.Transition {...}
I think it is safe to assume that nested classes, enums, and interfaces will have their own goog.provide and plainly skip them from the properties of the parent classes, enums, interfaces.
In this case Transition.EventType is provided explicitly.
The following pattern while highly suspicious is valid Closure:
goog.provide('goog.A');
goog.provide('goog.A.B');
/** @constructor */
goog.A = function() {}
/** @enum */
goog.A.B = {};
The current code will create a var
for B in goog.A
, and also declare namespace goog.A.B
which is an error.
see: https://github.com/google/closure-library/blob/master/closure/goog/fx/transition.js
/**
* @constructor
* @struct
* @final
* @implements {goog.Thenable<TYPE>}
* @template TYPE,RESOLVER_CONTEXT
*/
goog.Promise = function(resolver, opt_context) {
/**
* @private @final @struct @constructor
*/
goog.Promise.CallbackEntry_ = function() {}
}
produces a static member:
class Promise < TYPE , RESOLVER_CONTEXT > implements Thenable {
static CallbackEntry_ ( ) : void ;
For example if B extends A and takes string instead of number, we generate:
declare namespace f {
class A {
constructor(n: number);
static call(a: A, n: number);
}
class B extends A {
constructor(s: string);
static call(b: B, n: string);
}
}
TS barfs on the static call for B which is required to subtype A's functional type. See 'oog.fx.dom.PredefinedEffect' and 'Animation' for closure lib repro.
Maybe we should not output call
and apply
for constructor functions until someone really needs them. I can't come up with a good need for using those in sane TS code.
/**
* A message value that can be handled by a Logger.
*
* Functions are treated like callbacks, but are only called when the event's
* log level is enabled. This is useful for logging messages that are expensive
* to construct.
*
* @typedef {string|function(): string}
*/
goog.debug.Loggable;
/**
* Logs a message. If the logger is currently enabled for the
* given message level then the given message is forwarded to all the
* registered output Handler objects.
* @param {goog.log.Logger} logger
* @param {goog.log.Level} level One of the level identifiers.
* @param {goog.debug.Loggable} msg The message to log.
* @param {Error|Object=} opt_exception An exception associated with the
* message.
*/
goog.log.log = function(logger, level, msg, opt_exception) {
if (goog.log.ENABLED && logger) {
logger.log(level, msg, opt_exception);
}
};
emits a bad syntax ( )
function log (logger : goog.debug.Logger , level : goog.debug.Logger.Level , msg : ( string | ( ) => string ) , opt_exception ? : Object ) : void ;
To typecheck user code, written in closure module style:
var {trim} = goog.require('goog.string');
we can do so by emitting:
declare namespace ಠ_ಠ.cl2dts_internal.goog {
function require(name: string): any;
function require(name: 'goog.string'): typeof ಠ_ಠ.cl2dts_internal.goog.string;
}
running on base/string/string.js produces
declare module 'goog:goog.string.Unicode' {
type goog.string.Unicode = string ;
export var goog.string.Unicode : {
NBSP : goog.string.Unicode ,
};
}
these aren't intended to be advertised in the API
It's going to be really confusing for developers to see different parameter names (a,b,c...) in the type definitions than were declared. It loses the documentation benefit of seeing the names of the parameters in an API you're trying to call. We should preserve them where possible. (only function literals in typescript require manufacturing names for the parameter types)
this input
goog.provide('a');
/**
* @constructor
*/
a.One = function() {};
a.One.prototype.foo = function(x) {};
/**
* @constructor
* @extends {a.One}
*/
a.Two = function() {};
goog.inherits(a.Two, a.One);
produces a class Two which has an explicit constructor. We have a return type on the result which is invalid syntax.
declare module 'goog:a' {
export class One {
foo (a : any ) : void ;
}
export function foo (a : any ) : void ;
export class Two {
constructor ( ...a : any [] ) : any ;
static superClass_ : Object ;
}
}
With require overloading on strings, we can emulate:
var MyClass = goog.require('MyClass');
However this does not create a type alias for MyClass so the user cannot write:
var c: MyClass = new MyClass();
even though they can do
var c = new MyClass();
or even add their own type alias with type MyClass = typeof MyClass.prototype
.
//cc @mhegazy
/CC @zrlk
When multiple files are presented, and one of them goog.require's a symbol that is goog.provide'd by another, we should
* @param {function(this:SCOPE)|{handleEvent:function()}|null} listener Function
* or object that has a handleEvent method.
* @param {number=} opt_delay Milliseconds to wait; default is 0.
* @param {SCOPE=} opt_handler Object in whose scope to call the listener.
* @return {number} A handle to the timer ID.
* @template SCOPE
*/
goog.Timer.callOnce = function(listener, opt_delay, opt_handler) {
static callOnce < SCOPE > (listener : ( ( ) => any | { handleEvent : ( ) => any } ) , opt_delay ? : number , opt_handler ? : ( ) ) : number ;
the type of opt_handler is ( )
which is not valid
Lots of instance of this javascript/closure/ui/ac/ac.jslib.d.ts:1409:NaN: Generic type 'EventId<T>' requires 1 type argument(s).
and more:
javascript/closure/ui/ac/ac.jslib.d.ts:606:NaN: Generic type 'EventHandler<SCOPE>' requires 1 type argument(s).
javascript/closure/ui/ac/ac.jslib.d.ts:1593:NaN: Generic type 'Promise<TYPE, RESOLVER_CONTEXT>' requires 2 type argument(s).
javascript/closure/ui/ac/ac.jslib.d.ts:1736:NaN: Generic type 'Array<T>' requires 1 type argument(s).
etc.
We might be able to determine that the number of type arguments isn't correct by looking at the type the arguments are passed to, and replace the missing ones with 'any'.
Given the number of occurrences (34 in this particular compile) I don't think we want to try to fix all the usage sites.
/**
* @return {Element} The visible header element.
*/
goog.ui.Zippy.prototype.getVisibleHeaderElement = function() {
}
produces
getVisibleHeaderElement ( ) : ( ) ;
eg
/** @type {function(?):?|{handleEvent:function(?):?}|null} handler */
types.g = null;
closure/events/events.js:
goog.forwardDeclare('goog.debug.ErrorHandler');
/**
* Installs exception protection for the browser event entry point using the
* given error handler.
*
* @param {goog.debug.ErrorHandler} errorHandler Error handler with which to
* protect the entry point.
*/
goog.events.protectBrowserEventEntryPoint = function(errorHandler) {
goog.events.handleBrowserEvent_ = errorHandler.protectEntryPoint(
goog.events.handleBrowserEvent_);
};
produces
function protectBrowserEventEntryPoint (errorHandler : ( ) ) : void ;
I added a bit of debug to show what the provides are, and ran on javascript/closure/events/events.js
// Processing provides [goog.events, goog.events.CaptureSimulationMode, goog.events.Key, goog.events.ListenableType] from input javascript/closure/events/events.js
declare namespace ಠ_ಠ.cl2dts_internal.goog.events {
...
class BrowserEvent extends Event {
But BrowserEvent is not goog.provide'd from this file, and in fact it is in a different file.
eg. from javascript/closure/ui/zippy.js
* @extends {goog.events.EventTarget}
should produce a class goog.ui.Zippy that inherits from EventTarget
/**
* This creates a TwoThumbSlider object.
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper.
* @constructor
* @extends {goog.ui.SliderBase}
*/
goog.ui.TwoThumbSlider = function(opt_domHelper) {
goog.ui.SliderBase.call(this, opt_domHelper);
};
goog.inherits(goog.ui.TwoThumbSlider, goog.ui.SliderBase);
goog.tagUnsealableClass(goog.ui.TwoThumbSlider);
results in a class definition for TwoThumbSlider without the decorate method that comes from SliderBase.
We currently use CommandLineRunner.getDefaultExterns which gives us problems when we import code into google.
After the next release of closure compiler we can pick up my change, we should instead do
AbstractCommandLineRunner.getBuiltinExterns(opts.getCompilerOptions())
Using CommandLineRunner.getDefaultExterns doesn't work well inside google, where the default externs are typically passed into the program.
cc @mprobst
The following:
* @implements {goog.Thenable<TYPE>}
* @template TYPE,RESOLVER_CONTEXT
*/
goog.Promise = function(resolver, opt_context) {...}
outputs
class Promise < TYPE , RESOLVER_CONTEXT > implements Thenable {
But it should be Thenable<TYPE>
.
browserevent.js
goog.provide('goog.events.BrowserEvent');
goog.provide('goog.events.BrowserEvent.MouseButton');
/**
* @constructor
* @extends {goog.events.Event}
*/
goog.events.BrowserEvent = function(opt_e, opt_currentTarget) {
}
/**
* Normalized button constants for the mouse.
* @enum {number}
*/
goog.events.BrowserEvent.MouseButton = {
LEFT: 0,
MIDDLE: 1,
RIGHT: 2
};
we emit
class BrowserEvent extends Event {
isButton (a : BrowserEvent.MouseButton ) : boolean ;
static MouseButton : BrowserEvent.MouseButton ;
}
should be something like (if valid)
declare namespace goog.events {
class BrowserEvent {
isButton (a: goog.events.BrowserEvent.MouseButton) {
}
declare namespace goog.events.BrowserEvent {
enum MouseButton {
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.