Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 0fde1ce

Browse files
committed
fix dedups fields from prototype chain.
Closure @Lends is a way to extend a type with the fields of an object literal. This allows to introduce a field on a prototype object that is already defined in the constructor. In TS the fields are generated in the same class block, so deduping is needed.
1 parent 20a3c09 commit 0fde1ce

File tree

6 files changed

+59
-2
lines changed

6 files changed

+59
-2
lines changed

src/main/java/com/google/javascript/cl2dts/DeclarationGenerator.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -831,8 +831,8 @@ private void visitObjectType(ObjectType type, ObjectType prototype, Boolean proc
831831
emit("[key: string]: any;");
832832
emitBreak();
833833
}
834-
// Methods.
835-
visitProperties(prototype, false);
834+
// Prototype fields (mostly methods).
835+
visitProperties(prototype, false, ((ObjectType) instanceType).getOwnPropertyNames());
836836
// Statics.
837837
if (processStatics) {
838838
visitProperties(type, true);
@@ -843,7 +843,13 @@ private void visitObjectType(ObjectType type, ObjectType prototype, Boolean proc
843843
}
844844

845845
private void visitProperties(ObjectType objType, boolean isStatic) {
846+
visitProperties(objType, isStatic, Collections.<String>emptySet());
847+
}
848+
849+
private void visitProperties(ObjectType objType, boolean isStatic, Set<String> skipNames) {
846850
for (String propName : objType.getOwnPropertyNames()) {
851+
if (skipNames.contains(propName)) continue;
852+
847853
if ("prototype".equals(propName) || "superClass_".equals(propName)
848854
// constructors are handled in #visitObjectType
849855
|| "constructor".equals(propName)) {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
declare namespace ಠ_ಠ.cl2dts_internal.lends {
2+
class A {
3+
a : string ;
4+
c : boolean ;
5+
static b : number ;
6+
}
7+
}
8+
declare module 'goog:lends.A' {
9+
import alias = ಠ_ಠ.cl2dts_internal.lends.A;
10+
export default alias;
11+
}
12+
declare namespace ಠ_ಠ.cl2dts_internal.lends {
13+
}
14+
declare module 'goog:lends' {
15+
import alias = ಠ_ಠ.cl2dts_internal.lends;
16+
export = alias;
17+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
goog.provide('lends');
2+
goog.provide('lends.A');
3+
4+
/**
5+
* @constructor
6+
*/
7+
lends.A = function() {
8+
/** @type {string} */
9+
this.a = "";
10+
};
11+
12+
goog.object.extend(lends.A, /** @lends {lends.A} */ {b: 1});
13+
14+
// lends manages to avoid the deduping of fields, and as far as closure
15+
// is concerned there is a field a on the prototype and on the instance.
16+
// Also the presense of @lends extends the type signature of A.prototype.
17+
goog.object.extend(lends.A.prototype, /** @lends {lends.A.prototype} */ {a: 1, c: true});
18+
19+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import A from "goog:lends.A";
2+
3+
var a: A = new A();

src/test/java/com/google/javascript/cl2dts/provide_single_class.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ declare module 'goog:foo.bar.Baz.NestedEnum' {
1212
declare namespace ಠ_ಠ.cl2dts_internal.foo.bar {
1313
class Baz {
1414
field : string ;
15+
avalue : number ;
1516
equals (b : Baz.NestedClass ) : boolean ;
1617
method (a : string ) : number ;
1718
static staticMethod (a : string ) : number ;

src/test/java/com/google/javascript/cl2dts/provide_single_class.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ goog.provide('foo.bar.Baz.NestedEnum');
66
foo.bar.Baz = function() {
77
/** @type {string} */
88
this.field = 'a';
9+
// Surprisingly, defining the same field on the prototype and in
10+
// the constructor with different signatures doesn't throw a type error.
11+
// The type of the prototype is preferred and as far as types are
12+
// concerned `avalue` lives on the prototype object.
13+
/** @type {string} */
14+
this.avalue = 0;
915
};
1016

1117
/**
@@ -16,6 +22,11 @@ foo.bar.Baz.staticMethod = function(a) {
1622
return Number(a)
1723
};
1824

25+
/**
26+
* @type {number}
27+
*/
28+
foo.bar.Baz.prototype.avalue = 0;
29+
1930
/**
2031
* @param {string} a
2132
* @return {number}

0 commit comments

Comments
 (0)