@@ -488,12 +488,15 @@ class Annotator extends ClosureRewriter {
488
488
/** Exported symbol names that have been generated by expanding an "export * from ...". */
489
489
private generatedExports = new Set < string > ( ) ;
490
490
491
+ private typeChecker : ts . TypeChecker ;
492
+
491
493
constructor (
492
494
program : ts . Program , file : ts . SourceFile , options : Options ,
493
495
private pathToModuleName : ( context : string , importPath : string ) => string ,
494
496
private host ?: ts . ModuleResolutionHost , private tsOpts ?: ts . CompilerOptions ) {
495
497
super ( program , file , options ) ;
496
498
this . externsWriter = new ExternsWriter ( program , file , options ) ;
499
+ this . typeChecker = program . getTypeChecker ( ) ;
497
500
}
498
501
499
502
annotate ( ) : Output {
@@ -552,7 +555,7 @@ class Annotator extends ClosureRewriter {
552
555
}
553
556
const declNames = this . getExportDeclarationNames ( node ) ;
554
557
for ( let decl of declNames ) {
555
- const sym = this . program . getTypeChecker ( ) . getSymbolAtLocation ( decl ) ;
558
+ const sym = this . typeChecker . getSymbolAtLocation ( decl ) ;
556
559
const isValue = sym . flags & ts . SymbolFlags . Value ;
557
560
const declName = getIdentifierText ( decl ) ;
558
561
if ( node . kind === ts . SyntaxKind . VariableStatement ) {
@@ -599,15 +602,14 @@ class Annotator extends ClosureRewriter {
599
602
this . writeRange ( node . getFullStart ( ) , node . getStart ( ) ) ;
600
603
this . emit ( 'export' ) ;
601
604
let exportedSymbols : NamedSymbol [ ] = [ ] ;
602
- const typeChecker = this . program . getTypeChecker ( ) ;
603
605
if ( ! exportDecl . exportClause && exportDecl . moduleSpecifier ) {
604
606
// It's an "export * from ..." statement.
605
607
// Rewrite it to re-export each exported symbol directly.
606
608
exportedSymbols = this . expandSymbolsFromExportStar ( exportDecl ) ;
607
609
this . emit ( ` {${ exportedSymbols . map ( e => unescapeName ( e . name ) ) . join ( ',' ) } }` ) ;
608
610
} else {
609
611
if ( exportDecl . exportClause ) {
610
- exportedSymbols = this . getNamedSymbols ( exportDecl . exportClause . elements , typeChecker ) ;
612
+ exportedSymbols = this . getNamedSymbols ( exportDecl . exportClause . elements ) ;
611
613
this . visit ( exportDecl . exportClause ) ;
612
614
}
613
615
}
@@ -677,8 +679,7 @@ class Annotator extends ClosureRewriter {
677
679
case ts . SyntaxKind . GetAccessor :
678
680
case ts . SyntaxKind . SetAccessor :
679
681
let fnDecl = < ts . FunctionLikeDeclaration > node ;
680
- let tags =
681
- hasExportingDecorator ( node , this . program . getTypeChecker ( ) ) ? [ { tagName : 'export' } ] : [ ] ;
682
+ let tags = hasExportingDecorator ( node , this . typeChecker ) ? [ { tagName : 'export' } ] : [ ] ;
682
683
683
684
if ( ! fnDecl . body ) {
684
685
if ( hasModifierFlag ( fnDecl , ts . ModifierFlags . Abstract ) ) {
@@ -732,7 +733,7 @@ class Annotator extends ClosureRewriter {
732
733
return true ;
733
734
case ts . SyntaxKind . NonNullExpression :
734
735
const nnexpr = node as ts . NonNullExpression ;
735
- let type = this . program . getTypeChecker ( ) . getTypeAtLocation ( nnexpr . expression ) ;
736
+ let type = this . typeChecker . getTypeAtLocation ( nnexpr . expression ) ;
736
737
if ( type . flags & ts . TypeFlags . Union ) {
737
738
const nonNullUnion =
738
739
( type as ts . UnionType )
@@ -751,7 +752,7 @@ class Annotator extends ClosureRewriter {
751
752
case ts . SyntaxKind . PropertyDeclaration :
752
753
case ts . SyntaxKind . VariableStatement :
753
754
let docTags = this . getJSDoc ( node ) || [ ] ;
754
- if ( hasExportingDecorator ( node , this . program . getTypeChecker ( ) ) ) {
755
+ if ( hasExportingDecorator ( node , this . typeChecker ) ) {
755
756
docTags . push ( { tagName : 'export' } ) ;
756
757
}
757
758
@@ -762,6 +763,21 @@ class Annotator extends ClosureRewriter {
762
763
return true ;
763
764
}
764
765
break ;
766
+ case ts . SyntaxKind . PropertyAccessExpression :
767
+ // Convert dotted accesses to types that have an index type declared to quoted accesses, to
768
+ // avoid Closure renaming one access but not the other.
769
+ // This can happen because TS allows dotted access to string index types.
770
+ const pae = node as ts . PropertyAccessExpression ;
771
+ const t = this . typeChecker . getTypeAtLocation ( pae . expression ) ;
772
+ if ( ! t . getStringIndexType ( ) ) return false ;
773
+ this . debugWarn (
774
+ pae ,
775
+ this . typeChecker . typeToString ( t ) +
776
+ ` has a string index type but is accessed using dotted access. ` +
777
+ `Quoting the access.` ) ;
778
+ this . writeNode ( pae . expression ) ;
779
+ this . emit ( `['${ getIdentifierText ( pae . name ) } ']` ) ;
780
+ return true ;
765
781
default :
766
782
break ;
767
783
}
@@ -850,7 +866,6 @@ class Annotator extends ClosureRewriter {
850
866
private expandSymbolsFromExportStar ( exportDecl : ts . ExportDeclaration ) : NamedSymbol [ ] {
851
867
// You can't have an "export *" without a module specifier.
852
868
const moduleSpecifier = exportDecl . moduleSpecifier ! ;
853
- let typeChecker = this . program . getTypeChecker ( ) ;
854
869
855
870
// Gather the names of local exports, to avoid reexporting any
856
871
// names that are already locally exported.
@@ -860,7 +875,7 @@ class Annotator extends ClosureRewriter {
860
875
// import {foo} from ...
861
876
// so the latter is filtered below.
862
877
let locals =
863
- typeChecker . getSymbolsInScope ( this . file , ts . SymbolFlags . Export | ts . SymbolFlags . Alias ) ;
878
+ this . typeChecker . getSymbolsInScope ( this . file , ts . SymbolFlags . Export | ts . SymbolFlags . Alias ) ;
864
879
let localSet = new Set < string > ( ) ;
865
880
for ( let local of locals ) {
866
881
if ( local . declarations &&
@@ -872,7 +887,8 @@ class Annotator extends ClosureRewriter {
872
887
873
888
874
889
// Expand the export list, then filter it to the symbols we want to reexport.
875
- let exports = typeChecker . getExportsOfModule ( typeChecker . getSymbolAtLocation ( moduleSpecifier ) ) ;
890
+ let exports =
891
+ this . typeChecker . getExportsOfModule ( this . typeChecker . getSymbolAtLocation ( moduleSpecifier ) ) ;
876
892
const reexports = new Set < ts . Symbol > ( ) ;
877
893
for ( let sym of exports ) {
878
894
let name = unescapeName ( sym . name ) ;
@@ -907,9 +923,9 @@ class Annotator extends ClosureRewriter {
907
923
*/
908
924
private emitTypeDefExports ( exports : NamedSymbol [ ] ) {
909
925
if ( this . options . untyped ) return ;
910
- const typeChecker = this . program . getTypeChecker ( ) ;
911
926
for ( let exp of exports ) {
912
- if ( exp . sym . flags & ts . SymbolFlags . Alias ) exp . sym = typeChecker . getAliasedSymbol ( exp . sym ) ;
927
+ if ( exp . sym . flags & ts . SymbolFlags . Alias )
928
+ exp . sym = this . typeChecker . getAliasedSymbol ( exp . sym ) ;
913
929
const isTypeAlias = ( exp . sym . flags & ts . SymbolFlags . TypeAlias ) !== 0 &&
914
930
( exp . sym . flags & ts . SymbolFlags . Value ) === 0 ;
915
931
if ( ! isTypeAlias ) continue ;
@@ -985,20 +1001,19 @@ class Annotator extends ClosureRewriter {
985
1001
// all symbols from this import to be prefixed.
986
1002
if ( ! this . options . untyped ) {
987
1003
let symbols : NamedSymbol [ ] = [ ] ;
988
- const typeChecker = this . program . getTypeChecker ( ) ;
989
1004
if ( importClause . name ) {
990
1005
// import a from ...;
991
1006
symbols = [ {
992
1007
name : getIdentifierText ( importClause . name ) ,
993
- sym : typeChecker . getSymbolAtLocation ( importClause . name )
1008
+ sym : this . typeChecker . getSymbolAtLocation ( importClause . name )
994
1009
} ] ;
995
1010
} else {
996
1011
// import {a as b} from ...;
997
1012
if ( ! importClause . namedBindings ||
998
1013
importClause . namedBindings . kind !== ts . SyntaxKind . NamedImports ) {
999
1014
throw new Error ( 'unreached' ) ; // Guaranteed by if check above.
1000
1015
}
1001
- symbols = this . getNamedSymbols ( importClause . namedBindings . elements , typeChecker ) ;
1016
+ symbols = this . getNamedSymbols ( importClause . namedBindings . elements ) ;
1002
1017
}
1003
1018
this . forwardDeclare ( decl . moduleSpecifier , symbols , ! ! importClause . name ) ;
1004
1019
}
@@ -1016,15 +1031,13 @@ class Annotator extends ClosureRewriter {
1016
1031
}
1017
1032
}
1018
1033
1019
- private getNamedSymbols (
1020
- specifiers : Array < ts . ImportSpecifier | ts . ExportSpecifier > ,
1021
- typeChecker : ts . TypeChecker ) : NamedSymbol [ ] {
1034
+ private getNamedSymbols ( specifiers : Array < ts . ImportSpecifier | ts . ExportSpecifier > ) : NamedSymbol [ ] {
1022
1035
return specifiers . map ( e => {
1023
1036
return {
1024
1037
// e.name might be renaming symbol as in `export {Foo as Bar}`, where e.name would be 'Bar'
1025
1038
// and != sym.name. Store away the name so forwardDeclare below can emit the right name.
1026
1039
name : getIdentifierText ( e . name ) ,
1027
- sym : typeChecker . getSymbolAtLocation ( e . name ) ,
1040
+ sym : this . typeChecker . getSymbolAtLocation ( e . name ) ,
1028
1041
} ;
1029
1042
} ) ;
1030
1043
}
@@ -1043,8 +1056,8 @@ class Annotator extends ClosureRewriter {
1043
1056
const forwardDeclarePrefix = `tsickle_forward_declare_${ ++ this . forwardDeclareCounter } ` ;
1044
1057
const moduleNamespace =
1045
1058
nsImport !== null ? nsImport : this . pathToModuleName ( this . file . fileName , importPath ) ;
1046
- const typeChecker = this . program . getTypeChecker ( ) ;
1047
- const exports = typeChecker . getExportsOfModule ( typeChecker . getSymbolAtLocation ( specifier ) ) ;
1059
+ const exports =
1060
+ this . typeChecker . getExportsOfModule ( this . typeChecker . getSymbolAtLocation ( specifier ) ) ;
1048
1061
// In TypeScript, importing a module for use in a type annotation does not cause a runtime load.
1049
1062
// In Closure Compiler, goog.require'ing a module causes a runtime load, so emitting requires
1050
1063
// here would cause a change in load order, which is observable (and can lead to errors).
@@ -1066,7 +1079,8 @@ class Annotator extends ClosureRewriter {
1066
1079
this . emit ( `\ngoog.require('${ moduleNamespace } '); // force type-only module to be loaded` ) ;
1067
1080
}
1068
1081
for ( let exp of exportedSymbols ) {
1069
- if ( exp . sym . flags & ts . SymbolFlags . Alias ) exp . sym = typeChecker . getAliasedSymbol ( exp . sym ) ;
1082
+ if ( exp . sym . flags & ts . SymbolFlags . Alias )
1083
+ exp . sym = this . typeChecker . getAliasedSymbol ( exp . sym ) ;
1070
1084
// goog: imports don't actually use the .default property that TS thinks they have.
1071
1085
const qualifiedName = nsImport && isDefaultImport ? forwardDeclarePrefix :
1072
1086
forwardDeclarePrefix + '.' + exp . sym . name ;
@@ -1106,7 +1120,7 @@ class Annotator extends ClosureRewriter {
1106
1120
private emitInterface ( iface : ts . InterfaceDeclaration ) {
1107
1121
// If this symbol is both a type and a value, we cannot emit both into Closure's
1108
1122
// single namespace.
1109
- let sym = this . program . getTypeChecker ( ) . getSymbolAtLocation ( iface . name ) ;
1123
+ let sym = this . typeChecker . getSymbolAtLocation ( iface . name ) ;
1110
1124
if ( sym . flags & ts . SymbolFlags . Value ) return ;
1111
1125
1112
1126
let docTags = this . getJSDoc ( iface ) || [ ] ;
@@ -1215,7 +1229,7 @@ class Annotator extends ClosureRewriter {
1215
1229
1216
1230
// If the type is also defined as a value, skip emitting it. Closure collapses type & value
1217
1231
// namespaces, the two emits would conflict if tsickle emitted both.
1218
- let sym = this . program . getTypeChecker ( ) . getSymbolAtLocation ( node . name ) ;
1232
+ let sym = this . typeChecker . getSymbolAtLocation ( node . name ) ;
1219
1233
if ( sym . flags & ts . SymbolFlags . Value ) return ;
1220
1234
1221
1235
// Write a Closure typedef, which involves an unused "var" declaration.
@@ -1247,7 +1261,7 @@ class Annotator extends ClosureRewriter {
1247
1261
for ( let member of node . members ) {
1248
1262
let memberName = member . name . getText ( ) ;
1249
1263
if ( member . initializer ) {
1250
- let enumConstValue = this . program . getTypeChecker ( ) . getConstantValue ( member ) ;
1264
+ let enumConstValue = this . typeChecker . getConstantValue ( member ) ;
1251
1265
if ( enumConstValue !== undefined ) {
1252
1266
members . set ( memberName , enumConstValue ) ;
1253
1267
i = enumConstValue + 1 ;
0 commit comments