Skip to content

Commit d82a3d4

Browse files
committed
Made properties of top level members with anonymous types static
This also fixed a bug where properties were being emitted twice when declared on both an interface and a variable of the same name
1 parent 27b0a69 commit d82a3d4

File tree

3 files changed

+89
-3
lines changed

3 files changed

+89
-3
lines changed

lib/base.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export interface ExtendedInterfaceDeclaration extends ts.InterfaceDeclaration {
8787
}
8888

8989
export function ident(n: ts.Node): string {
90+
if (!n) return null;
9091
if (ts.isIdentifier(n)) return n.text;
9192
if (n.kind === ts.SyntaxKind.FirstLiteralToken) return (n as ts.LiteralLikeNode).text;
9293
if (ts.isQualifiedName(n)) {

lib/merge.ts

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -374,9 +374,28 @@ export function normalizeSourceFile(f: ts.SourceFile, fc: FacadeConverter) {
374374
if (base.ident(member.name) === 'prototype') {
375375
break;
376376
}
377-
addModifier(member, ts.createNode(ts.SyntaxKind.StaticKeyword));
378-
member.parent = existing;
379-
Array.prototype.push.call(members, member);
377+
378+
// Finds all existing declarations of this property in the inheritance
379+
// hierarchy of this class
380+
const existingDeclarations =
381+
findPropertyInHierarchy(base.ident(member.name), existing, classes);
382+
383+
if (existingDeclarations.size) {
384+
// TODO(derekx): For dom.d.ts it makes sense to make all properties that are
385+
// declared on the anonymous types of top level variable declarations
386+
// static, but this may not always be correct
387+
for (const existingDecl of existingDeclarations) {
388+
addModifier(existingDecl, ts.createModifier(ts.SyntaxKind.StaticKeyword));
389+
}
390+
}
391+
392+
// If needed, add declaration of property to the interface that we are
393+
// currently handling
394+
if (!findPropertyInClass(base.ident(member.name), existing)) {
395+
addModifier(member, ts.createModifier(ts.SyntaxKind.StaticKeyword));
396+
member.parent = existing;
397+
Array.prototype.push.call(members, member);
398+
}
380399
break;
381400
case ts.SyntaxKind.IndexSignature:
382401
member.parent = existing.parent;
@@ -402,6 +421,37 @@ export function normalizeSourceFile(f: ts.SourceFile, fc: FacadeConverter) {
402421
}
403422
}
404423

424+
function findPropertyInClass(propName: string, classLike: base.ClassLike): ts.ClassElement|
425+
undefined {
426+
const members = classLike.members as ts.NodeArray<ts.ClassElement>;
427+
return members.find((member: ts.ClassElement) => {
428+
if (base.ident(member.name) === propName) {
429+
return true;
430+
}
431+
});
432+
}
433+
434+
function findPropertyInHierarchy(
435+
propName: string, classLike: base.ClassLike,
436+
classes: Map<string, base.ClassLike>): Set<ts.ClassElement> {
437+
const propertyDeclarations = new Set<ts.ClassElement>();
438+
const declaration = findPropertyInClass(propName, classLike);
439+
if (declaration) propertyDeclarations.add(declaration);
440+
441+
const heritageClauses = classLike.heritageClauses || ts.createNodeArray();
442+
for (const clause of heritageClauses) {
443+
if (clause.token !== ts.SyntaxKind.ExtendsKeyword) {
444+
continue;
445+
}
446+
const name = base.ident(clause.types[0].expression);
447+
const declarationsInAncestors = findPropertyInHierarchy(propName, classes.get(name), classes);
448+
if (declarationsInAncestors.size) {
449+
declarationsInAncestors.forEach(decl => propertyDeclarations.add(decl));
450+
}
451+
}
452+
return propertyDeclarations;
453+
}
454+
405455
function removeFromArray(nodes: ts.NodeArray<ts.Node>, v: ts.Node) {
406456
for (let i = 0, len = nodes.length; i < len; ++i) {
407457
if (nodes[i] === v) {

test/declaration_test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,41 @@ abstract class AbstractRange {
519519
external num get endOffset;
520520
external num get startOffset;
521521
external factory AbstractRange();
522+
}`);
523+
});
524+
525+
it('makes properties of top level variables with anonymous types static', () => {
526+
expectTranslate(`
527+
declare interface CacheBase {
528+
readonly CHECKING: number;
529+
readonly DOWNLOADING: number;
530+
readonly IDLE: number;
531+
}
532+
533+
declare interface MyCache extends CacheBase {}
534+
535+
declare var MyCache: {
536+
prototype: MyCache;
537+
new (): MyCache;
538+
readonly CHECKING: number;
539+
readonly DOWNLOADING: number;
540+
readonly IDLE: number;
541+
};
542+
543+
`).to.equal(`@anonymous
544+
@JS()
545+
abstract class CacheBase {
546+
external static num get CHECKING;
547+
external static num get DOWNLOADING;
548+
external static num get IDLE;
549+
}
550+
551+
@JS("MyCache")
552+
abstract class MyCache implements CacheBase {
553+
external factory MyCache();
554+
external static num get CHECKING;
555+
external static num get DOWNLOADING;
556+
external static num get IDLE;
522557
}`);
523558
});
524559
});

0 commit comments

Comments
 (0)