@@ -65,11 +65,14 @@ export type TypeResolveContext = ScriptCompileContext | SimpleTypeResolveContext
65
65
66
66
type Import = Pick < ImportBinding , 'source' | 'imported' >
67
67
68
- type ScopeTypeNode = Node & {
69
- // scope types always has ownerScope attached
68
+ interface WithScope {
70
69
_ownerScope : TypeScope
71
70
}
72
71
72
+ // scope types always has ownerScope attached
73
+ type ScopeTypeNode = Node &
74
+ WithScope & { _ns ?: TSModuleDeclaration & WithScope }
75
+
73
76
export interface TypeScope {
74
77
filename : string
75
78
source : string
@@ -79,7 +82,7 @@ export interface TypeScope {
79
82
exportedTypes : Record < string , ScopeTypeNode >
80
83
}
81
84
82
- export interface WithScope {
85
+ export interface MaybeWithScope {
83
86
_ownerScope ?: TypeScope
84
87
}
85
88
@@ -100,7 +103,7 @@ interface ResolvedElements {
100
103
*/
101
104
export function resolveTypeElements (
102
105
ctx : TypeResolveContext ,
103
- node : Node & WithScope & { _resolvedElements ?: ResolvedElements } ,
106
+ node : Node & MaybeWithScope & { _resolvedElements ?: ResolvedElements } ,
104
107
scope ?: TypeScope
105
108
) : ResolvedElements {
106
109
if ( node . _resolvedElements ) {
@@ -177,7 +180,7 @@ function typeElementsToMap(
177
180
const res : ResolvedElements = { props : { } }
178
181
for ( const e of elements ) {
179
182
if ( e . type === 'TSPropertySignature' || e . type === 'TSMethodSignature' ) {
180
- ; ( e as WithScope ) . _ownerScope = scope
183
+ ; ( e as MaybeWithScope ) . _ownerScope = scope
181
184
const name = getId ( e . key )
182
185
if ( name && ! e . computed ) {
183
186
res . props [ name ] = e as ResolvedElements [ 'props' ] [ string ]
@@ -248,7 +251,7 @@ function createProperty(
248
251
249
252
function resolveInterfaceMembers (
250
253
ctx : TypeResolveContext ,
251
- node : TSInterfaceDeclaration & WithScope ,
254
+ node : TSInterfaceDeclaration & MaybeWithScope ,
252
255
scope : TypeScope
253
256
) : ResolvedElements {
254
257
const base = typeElementsToMap ( ctx , node . body . body , node . _ownerScope )
@@ -289,7 +292,7 @@ function resolveIndexType(
289
292
ctx : TypeResolveContext ,
290
293
node : TSIndexedAccessType ,
291
294
scope : TypeScope
292
- ) : ( TSType & WithScope ) [ ] {
295
+ ) : ( TSType & MaybeWithScope ) [ ] {
293
296
if ( node . indexType . type === 'TSNumberKeyword' ) {
294
297
return resolveArrayElementType ( ctx , node . objectType , scope )
295
298
}
@@ -308,7 +311,7 @@ function resolveIndexType(
308
311
for ( const key of keys ) {
309
312
const targetType = resolved . props [ key ] ?. typeAnnotation ?. typeAnnotation
310
313
if ( targetType ) {
311
- ; ( targetType as TSType & WithScope ) . _ownerScope =
314
+ ; ( targetType as TSType & MaybeWithScope ) . _ownerScope =
312
315
resolved . props [ key ] . _ownerScope
313
316
types . push ( targetType )
314
317
}
@@ -532,22 +535,22 @@ function innerResolveTypeReference(
532
535
}
533
536
}
534
537
} else {
535
- const ns = innerResolveTypeReference (
536
- ctx ,
537
- scope ,
538
- name [ 0 ] ,
539
- node ,
540
- onlyExported
541
- )
542
- if ( ns && ns . type === 'TSModuleDeclaration' ) {
543
- const childScope = moduleDeclToScope ( ns , scope )
544
- return innerResolveTypeReference (
545
- ctx ,
546
- childScope ,
547
- name . length > 2 ? name . slice ( 1 ) : name [ name . length - 1 ] ,
548
- node ,
549
- ! ns . declare
550
- )
538
+ let ns = innerResolveTypeReference ( ctx , scope , name [ 0 ] , node , onlyExported )
539
+ if ( ns ) {
540
+ if ( ns . type !== 'TSModuleDeclaration' ) {
541
+ // namespace merged with other types, attached as _ns
542
+ ns = ns . _ns
543
+ }
544
+ if ( ns ) {
545
+ const childScope = moduleDeclToScope ( ns , ns . _ownerScope || scope )
546
+ return innerResolveTypeReference (
547
+ ctx ,
548
+ childScope ,
549
+ name . length > 2 ? name . slice ( 1 ) : name [ name . length - 1 ] ,
550
+ node ,
551
+ ! ns . declare
552
+ )
553
+ }
551
554
}
552
555
}
553
556
}
@@ -771,7 +774,6 @@ export function fileToScope(
771
774
exportedTypes : Object . create ( null )
772
775
}
773
776
recordTypes ( body , scope , asGlobal )
774
-
775
777
fileToScopeCache . set ( filename , scope )
776
778
return scope
777
779
}
@@ -858,10 +860,21 @@ function moduleDeclToScope(
858
860
}
859
861
const scope : TypeScope = {
860
862
...parentScope ,
863
+ imports : Object . create ( parentScope . imports ) ,
864
+ // TODO this seems wrong
861
865
types : Object . create ( parentScope . types ) ,
862
- imports : Object . create ( parentScope . imports )
866
+ exportedTypes : Object . create ( null )
867
+ }
868
+
869
+ if ( node . body . type === 'TSModuleDeclaration' ) {
870
+ const decl = node . body as TSModuleDeclaration & WithScope
871
+ decl . _ownerScope = scope
872
+ const id = getId ( decl . id )
873
+ scope . types [ id ] = scope . exportedTypes [ id ] = decl
874
+ } else {
875
+ recordTypes ( node . body . body , scope )
863
876
}
864
- recordTypes ( ( node . body as TSModuleBlock ) . body , scope )
877
+
865
878
return ( node . _resolvedChildScope = scope )
866
879
}
867
880
@@ -923,20 +936,52 @@ function recordTypes(body: Statement[], scope: TypeScope, asGlobal = false) {
923
936
}
924
937
}
925
938
for ( const key of Object . keys ( types ) ) {
926
- types [ key ] . _ownerScope = scope
939
+ const node = types [ key ]
940
+ node . _ownerScope = scope
941
+ if ( node . _ns ) node . _ns . _ownerScope = scope
927
942
}
928
943
}
929
944
930
945
function recordType ( node : Node , types : Record < string , Node > ) {
931
946
switch ( node . type ) {
932
947
case 'TSInterfaceDeclaration' :
933
948
case 'TSEnumDeclaration' :
934
- case 'TSModuleDeclaration' :
935
- case 'ClassDeclaration' : {
936
- const id = node . id . type === 'Identifier' ? node . id . name : node . id . value
937
- types [ id ] = node
949
+ case 'TSModuleDeclaration' : {
950
+ const id = getId ( node . id )
951
+ let existing = types [ id ]
952
+ if ( existing ) {
953
+ if ( node . type === 'TSModuleDeclaration' ) {
954
+ if ( existing . type === 'TSModuleDeclaration' ) {
955
+ mergeNamespaces ( existing as typeof node , node )
956
+ } else {
957
+ attachNamespace ( existing , node )
958
+ }
959
+ break
960
+ }
961
+ if ( existing . type === 'TSModuleDeclaration' ) {
962
+ // replace and attach namespace
963
+ types [ id ] = node
964
+ attachNamespace ( node , existing )
965
+ break
966
+ }
967
+
968
+ if ( existing . type !== node . type ) {
969
+ // type-level error
970
+ break
971
+ }
972
+ if ( node . type === 'TSInterfaceDeclaration' ) {
973
+ ; ( existing as typeof node ) . body . body . push ( ...node . body . body )
974
+ } else {
975
+ ; ( existing as typeof node ) . members . push ( ...node . members )
976
+ }
977
+ } else {
978
+ types [ id ] = node
979
+ }
938
980
break
939
981
}
982
+ case 'ClassDeclaration' :
983
+ types [ getId ( node . id ) ] = node
984
+ break
940
985
case 'TSTypeAliasDeclaration' :
941
986
types [ node . id . name ] = node . typeAnnotation
942
987
break
@@ -955,6 +1000,47 @@ function recordType(node: Node, types: Record<string, Node>) {
955
1000
}
956
1001
}
957
1002
1003
+ function mergeNamespaces ( to : TSModuleDeclaration , from : TSModuleDeclaration ) {
1004
+ const toBody = to . body
1005
+ const fromBody = from . body
1006
+ if ( toBody . type === 'TSModuleDeclaration' ) {
1007
+ if ( fromBody . type === 'TSModuleDeclaration' ) {
1008
+ // both decl
1009
+ mergeNamespaces ( toBody , fromBody )
1010
+ } else {
1011
+ // to: decl -> from: block
1012
+ fromBody . body . push ( {
1013
+ type : 'ExportNamedDeclaration' ,
1014
+ declaration : toBody ,
1015
+ exportKind : 'type' ,
1016
+ specifiers : [ ]
1017
+ } )
1018
+ }
1019
+ } else if ( fromBody . type === 'TSModuleDeclaration' ) {
1020
+ // to: block <- from: decl
1021
+ toBody . body . push ( {
1022
+ type : 'ExportNamedDeclaration' ,
1023
+ declaration : fromBody ,
1024
+ exportKind : 'type' ,
1025
+ specifiers : [ ]
1026
+ } )
1027
+ } else {
1028
+ // both block
1029
+ toBody . body . push ( ...fromBody . body )
1030
+ }
1031
+ }
1032
+
1033
+ function attachNamespace (
1034
+ to : Node & { _ns ?: TSModuleDeclaration } ,
1035
+ ns : TSModuleDeclaration
1036
+ ) {
1037
+ if ( ! to . _ns ) {
1038
+ to . _ns = ns
1039
+ } else {
1040
+ mergeNamespaces ( to . _ns , ns )
1041
+ }
1042
+ }
1043
+
958
1044
export function recordImports ( body : Statement [ ] ) {
959
1045
const imports : TypeScope [ 'imports' ] = Object . create ( null )
960
1046
for ( const s of body ) {
@@ -977,7 +1063,7 @@ function recordImport(node: Node, imports: TypeScope['imports']) {
977
1063
978
1064
export function inferRuntimeType (
979
1065
ctx : TypeResolveContext ,
980
- node : Node & WithScope ,
1066
+ node : Node & MaybeWithScope ,
981
1067
scope = node . _ownerScope || ctxToScope ( ctx )
982
1068
) : string [ ] {
983
1069
switch ( node . type ) {
@@ -1035,11 +1121,11 @@ export function inferRuntimeType(
1035
1121
}
1036
1122
1037
1123
case 'TSTypeReference' :
1124
+ const resolved = resolveTypeReference ( ctx , node , scope )
1125
+ if ( resolved ) {
1126
+ return inferRuntimeType ( ctx , resolved , resolved . _ownerScope )
1127
+ }
1038
1128
if ( node . typeName . type === 'Identifier' ) {
1039
- const resolved = resolveTypeReference ( ctx , node , scope )
1040
- if ( resolved ) {
1041
- return inferRuntimeType ( ctx , resolved , resolved . _ownerScope )
1042
- }
1043
1129
switch ( node . typeName . name ) {
1044
1130
case 'Array' :
1045
1131
case 'Function' :
0 commit comments