Skip to content

Commit 9bcb146

Browse files
authored
Added --trust-js-types flag
Added the --trust-js-types flag to emit @anonymous tags on classes that have neither constructors nor static methods * Started emitting @anonymous tags on classes that have neither constructors nor static methods * Added the --trust-js-types flag to control this behavior * Updated the --trust-js-types flag description in the README
1 parent c3c05e7 commit 9bcb146

File tree

6 files changed

+86
-16
lines changed

6 files changed

+86
-16
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Dart interop facade file is written to stdout.
2222
`--base-path=<input d.ts file directory>`: specify the directory that contains the input d.ts files<br/>
2323
`--generate-html`: generate facades for dart:html types rather than importing them<br/>
2424
`--explicit-static`: disables default assumption that properties declared on the anonymous types of top level variable declarations are static
25+
`--trust-js-types`: Emits @anonymous tags on classes that have neither constructors nor static members. This prevents the Dart Dev Compiler from checking whether or not objects are truly instances of those classes. This flag should be used if the input JS/TS library has structural types, or is otherwise claiming that types match in cases where the correct JS prototype is not there for DDC to check against.
2526

2627
### Example
2728
`dart_js_facade_gen --destination=/usr/foo/tmp/chartjs/lib --base-path=/usr/foo/git/DefinitelyTyped/chartjs /usr/foo/git/DefinitelyTyped/chartjs/chart.d.ts`

index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ const main = require('./build/lib/main.js');
44

55
var args = require('minimist')(process.argv.slice(2), {
66
base: 'string',
7-
boolean: ['semantic-diagnostics', 'generate-html', 'explicit-static'],
7+
boolean: ['semantic-diagnostics', 'generate-html', 'explicit-static', 'trust-js-types'],
88
alias: {
99
'base-path': 'basePath',
1010
'semantic-diagnostics': 'semanticDiagnostics',
1111
'generate-html': 'generateHTML',
12-
'explicit-static': 'explicitStatic'
12+
'explicit-static': 'explicitStatic',
13+
'trust-js-types': 'trustJSTypes'
1314
}
1415
});
1516
try {

lib/declaration.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -99,20 +99,30 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
9999
return path.join('.');
100100
}
101101

102-
private isAnonymousInterface(node: ts.Node): boolean {
103-
if (!ts.isInterfaceDeclaration(node)) return false;
104-
let interfaceDecl = node as base.ExtendedInterfaceDeclaration;
105-
// If we were able to associate a variable declaration with the interface definition then
106-
// the interface isn't actually anonymous.
107-
return !interfaceDecl.classLikeVariableDeclaration;
102+
private isAnonymous(node: ts.Node): boolean {
103+
if (ts.isInterfaceDeclaration(node)) {
104+
const extendedInterfaceDecl = node as base.ExtendedInterfaceDeclaration;
105+
// If we were able to associate a variable declaration with the interface definition then the
106+
// interface isn't actually anonymous.
107+
return !extendedInterfaceDecl.classLikeVariableDeclaration;
108+
} else if (this.trustJSTypes && ts.isClassLike(node)) {
109+
// If the trust-js-types flag is set, @anonymous tags are emitted on all classes that don't
110+
// have any constructors or any static members.
111+
const hasConstructor = node.members.some((member: ts.TypeElement|ts.ClassElement) => {
112+
return ts.isConstructorDeclaration(member) || ts.isConstructSignatureDeclaration(member);
113+
});
114+
const hasStatic = node.members.some(base.isStatic);
115+
return !hasConstructor && !hasStatic;
116+
}
117+
return false;
108118
}
109119

110120
maybeEmitJsAnnotation(node: ts.Node, {suppressUnneededPaths}: {suppressUnneededPaths: boolean}) {
111121
// No need to emit the annotations as an entity outside the code comment
112122
// will already have the same annotation.
113123
if (this.insideCodeComment) return;
114124

115-
if (this.isAnonymousInterface(node)) {
125+
if (this.isAnonymous(node)) {
116126
this.emit('@anonymous');
117127
this.emit('@JS()');
118128
return;
@@ -433,7 +443,7 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
433443

434444
constructor(
435445
tr: Transpiler, private fc: FacadeConverter, private enforceUnderscoreConventions: boolean,
436-
private promoteFunctionLikeMembers: boolean) {
446+
private promoteFunctionLikeMembers: boolean, private trustJSTypes: boolean) {
437447
super(tr);
438448
}
439449

@@ -680,11 +690,11 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
680690
} break;
681691
case ts.SyntaxKind.Constructor:
682692
case ts.SyntaxKind.ConstructSignature: {
683-
let ctorDecl = <ts.ConstructorDeclaration>node;
693+
const ctorDecl = <ts.ConstructorDeclaration>node;
684694
// Find containing class name.
685695
let classDecl = base.getEnclosingClass(ctorDecl);
686696
if (!classDecl) this.reportError(ctorDecl, 'cannot find outer class node');
687-
let isAnonymous = this.isAnonymousInterface(classDecl);
697+
const isAnonymous = this.isAnonymous(classDecl);
688698
if (isAnonymous) {
689699
this.emit('// Constructors on anonymous interfaces are not yet supported.\n');
690700
this.enterCodeComment();

lib/main.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ export interface TranspilerOptions {
5050
* declarations are static.
5151
*/
5252
explicitStatic?: boolean;
53+
/**
54+
* Emit anonymous tags on all classes that have neither constructors nor static members.
55+
*/
56+
trustJSTypes?: boolean;
5357

5458
/**
5559
* Experimental JS Interop specific option to promote properties with function
@@ -109,7 +113,8 @@ export class Transpiler {
109113
this.options = this.options || {};
110114
this.fc = new FacadeConverter(this, options.typingsRoot, options.generateHTML);
111115
this.declarationTranspiler = new DeclarationTranspiler(
112-
this, this.fc, options.enforceUnderscoreConventions, options.promoteFunctionLikeMembers);
116+
this, this.fc, options.enforceUnderscoreConventions, options.promoteFunctionLikeMembers,
117+
options.trustJSTypes);
113118
this.transpilers = [
114119
new ModuleTranspiler(this, this.fc, options.moduleName),
115120
this.declarationTranspiler,

test/declaration_test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,60 @@ abstract class MyCache implements CacheBase {
950950
}`);
951951
});
952952
});
953+
954+
describe('--trust-js-types', () => {
955+
const trustJSTypesOpts = {failFast: true, trustJSTypes: true};
956+
it('makes classes that have neither constructors nor static members anonymous when set', () => {
957+
expectTranslate(
958+
`declare class X {
959+
a: number;
960+
b: string;
961+
}`,
962+
trustJSTypesOpts)
963+
.to.equal(`@anonymous
964+
@JS()
965+
class X {
966+
// @Ignore
967+
X.fakeConstructor$();
968+
external num get a;
969+
external set a(num v);
970+
external String get b;
971+
external set b(String v);
972+
}`);
973+
expectTranslate(
974+
`declare class X {
975+
constructor();
976+
a: number;
977+
b: string;
978+
}`,
979+
trustJSTypesOpts)
980+
.to.equal(`@JS()
981+
class X {
982+
// @Ignore
983+
X.fakeConstructor$();
984+
external factory X();
985+
external num get a;
986+
external set a(num v);
987+
external String get b;
988+
external set b(String v);
989+
}`);
990+
});
991+
expectTranslate(
992+
`declare class X {
993+
static a: number;
994+
static b: string;
995+
}`,
996+
trustJSTypesOpts)
997+
.to.equal(`@JS()
998+
class X {
999+
// @Ignore
1000+
X.fakeConstructor$();
1001+
external static num get a;
1002+
external static set a(num v);
1003+
external static String get b;
1004+
external static set b(String v);
1005+
}`);
1006+
});
9531007
});
9541008

9551009
describe('single call signature interfaces', () => {

test/facade_converter_test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,8 @@ external Thing get t;`);
128128
external get x;`);
129129
expectWithTypes('const x = [];').to.equal(`@JS()
130130
external get x;`);
131-
expectWithTypes(
132-
'class Person {}' +
133-
'const x = new Person();')
131+
expectWithTypes(`class Person {}
132+
const x = new Person();`)
134133
.to.equal(`@JS()
135134
class Person {
136135
// @Ignore

0 commit comments

Comments
 (0)