@@ -65,11 +65,14 @@ export type TypeResolveContext = ScriptCompileContext | SimpleTypeResolveContext
6565
6666type Import = Pick < ImportBinding , 'source' | 'imported' >
6767
68- type ScopeTypeNode = Node & {
69- // scope types always has ownerScope attached
68+ interface WithScope {
7069 _ownerScope : TypeScope
7170}
7271
72+ // scope types always has ownerScope attached
73+ type ScopeTypeNode = Node &
74+ WithScope & { _ns ?: TSModuleDeclaration & WithScope }
75+
7376export interface TypeScope {
7477 filename : string
7578 source : string
@@ -79,7 +82,7 @@ export interface TypeScope {
7982 exportedTypes : Record < string , ScopeTypeNode >
8083}
8184
82- export interface WithScope {
85+ export interface MaybeWithScope {
8386 _ownerScope ?: TypeScope
8487}
8588
@@ -100,7 +103,7 @@ interface ResolvedElements {
100103 */
101104export function resolveTypeElements (
102105 ctx : TypeResolveContext ,
103- node : Node & WithScope & { _resolvedElements ?: ResolvedElements } ,
106+ node : Node & MaybeWithScope & { _resolvedElements ?: ResolvedElements } ,
104107 scope ?: TypeScope
105108) : ResolvedElements {
106109 if ( node . _resolvedElements ) {
@@ -177,7 +180,7 @@ function typeElementsToMap(
177180 const res : ResolvedElements = { props : { } }
178181 for ( const e of elements ) {
179182 if ( e . type === 'TSPropertySignature' || e . type === 'TSMethodSignature' ) {
180- ; ( e as WithScope ) . _ownerScope = scope
183+ ; ( e as MaybeWithScope ) . _ownerScope = scope
181184 const name = getId ( e . key )
182185 if ( name && ! e . computed ) {
183186 res . props [ name ] = e as ResolvedElements [ 'props' ] [ string ]
@@ -248,7 +251,7 @@ function createProperty(
248251
249252function resolveInterfaceMembers (
250253 ctx : TypeResolveContext ,
251- node : TSInterfaceDeclaration & WithScope ,
254+ node : TSInterfaceDeclaration & MaybeWithScope ,
252255 scope : TypeScope
253256) : ResolvedElements {
254257 const base = typeElementsToMap ( ctx , node . body . body , node . _ownerScope )
@@ -289,7 +292,7 @@ function resolveIndexType(
289292 ctx : TypeResolveContext ,
290293 node : TSIndexedAccessType ,
291294 scope : TypeScope
292- ) : ( TSType & WithScope ) [ ] {
295+ ) : ( TSType & MaybeWithScope ) [ ] {
293296 if ( node . indexType . type === 'TSNumberKeyword' ) {
294297 return resolveArrayElementType ( ctx , node . objectType , scope )
295298 }
@@ -308,7 +311,7 @@ function resolveIndexType(
308311 for ( const key of keys ) {
309312 const targetType = resolved . props [ key ] ?. typeAnnotation ?. typeAnnotation
310313 if ( targetType ) {
311- ; ( targetType as TSType & WithScope ) . _ownerScope =
314+ ; ( targetType as TSType & MaybeWithScope ) . _ownerScope =
312315 resolved . props [ key ] . _ownerScope
313316 types . push ( targetType )
314317 }
@@ -532,22 +535,22 @@ function innerResolveTypeReference(
532535 }
533536 }
534537 } 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+ }
551554 }
552555 }
553556}
@@ -771,7 +774,6 @@ export function fileToScope(
771774 exportedTypes : Object . create ( null )
772775 }
773776 recordTypes ( body , scope , asGlobal )
774-
775777 fileToScopeCache . set ( filename , scope )
776778 return scope
777779}
@@ -858,10 +860,21 @@ function moduleDeclToScope(
858860 }
859861 const scope : TypeScope = {
860862 ...parentScope ,
863+ imports : Object . create ( parentScope . imports ) ,
864+ // TODO this seems wrong
861865 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 )
863876 }
864- recordTypes ( ( node . body as TSModuleBlock ) . body , scope )
877+
865878 return ( node . _resolvedChildScope = scope )
866879}
867880
@@ -923,20 +936,52 @@ function recordTypes(body: Statement[], scope: TypeScope, asGlobal = false) {
923936 }
924937 }
925938 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
927942 }
928943}
929944
930945function recordType ( node : Node , types : Record < string , Node > ) {
931946 switch ( node . type ) {
932947 case 'TSInterfaceDeclaration' :
933948 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+ }
938980 break
939981 }
982+ case 'ClassDeclaration' :
983+ types [ getId ( node . id ) ] = node
984+ break
940985 case 'TSTypeAliasDeclaration' :
941986 types [ node . id . name ] = node . typeAnnotation
942987 break
@@ -955,6 +1000,47 @@ function recordType(node: Node, types: Record<string, Node>) {
9551000 }
9561001}
9571002
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+
9581044export function recordImports ( body : Statement [ ] ) {
9591045 const imports : TypeScope [ 'imports' ] = Object . create ( null )
9601046 for ( const s of body ) {
@@ -977,7 +1063,7 @@ function recordImport(node: Node, imports: TypeScope['imports']) {
9771063
9781064export function inferRuntimeType (
9791065 ctx : TypeResolveContext ,
980- node : Node & WithScope ,
1066+ node : Node & MaybeWithScope ,
9811067 scope = node . _ownerScope || ctxToScope ( ctx )
9821068) : string [ ] {
9831069 switch ( node . type ) {
@@ -1035,11 +1121,11 @@ export function inferRuntimeType(
10351121 }
10361122
10371123 case 'TSTypeReference' :
1124+ const resolved = resolveTypeReference ( ctx , node , scope )
1125+ if ( resolved ) {
1126+ return inferRuntimeType ( ctx , resolved , resolved . _ownerScope )
1127+ }
10381128 if ( node . typeName . type === 'Identifier' ) {
1039- const resolved = resolveTypeReference ( ctx , node , scope )
1040- if ( resolved ) {
1041- return inferRuntimeType ( ctx , resolved , resolved . _ownerScope )
1042- }
10431129 switch ( node . typeName . name ) {
10441130 case 'Array' :
10451131 case 'Function' :
0 commit comments