7
7
TSEnumDeclaration ,
8
8
TSExpressionWithTypeArguments ,
9
9
TSFunctionType ,
10
+ TSIndexedAccessType ,
10
11
TSInterfaceDeclaration ,
11
12
TSMappedType ,
12
13
TSMethodSignature ,
@@ -117,30 +118,11 @@ function innerResolveTypeElements(
117
118
case 'TSMappedType' :
118
119
return resolveMappedType ( ctx , node , scope )
119
120
case 'TSIndexedAccessType' : {
120
- if (
121
- node . indexType . type === 'TSLiteralType' &&
122
- node . indexType . literal . type === 'StringLiteral'
123
- ) {
124
- const resolved = resolveTypeElements ( ctx , node . objectType , scope )
125
- const key = node . indexType . literal . value
126
- const targetType = resolved . props [ key ] . typeAnnotation
127
- if ( targetType ) {
128
- return resolveTypeElements (
129
- ctx ,
130
- targetType . typeAnnotation ,
131
- resolved . props [ key ] . _ownerScope
132
- )
133
- } else {
134
- break
135
- }
136
- } else {
137
- // TODO support `number` and `string` index type when possible
138
- ctx . error (
139
- `Unsupported index type: ${ node . indexType . type } ` ,
140
- node . indexType ,
141
- scope
142
- )
143
- }
121
+ const types = resolveIndexType ( ctx , node , scope )
122
+ return mergeElements (
123
+ types . map ( t => resolveTypeElements ( ctx , t , t . _ownerScope ) ) ,
124
+ 'TSUnionType'
125
+ )
144
126
}
145
127
case 'TSExpressionWithTypeArguments' : // referenced by interface extends
146
128
case 'TSTypeReference' : {
@@ -201,6 +183,7 @@ function mergeElements(
201
183
maps : ResolvedElements [ ] ,
202
184
type : 'TSUnionType' | 'TSIntersectionType'
203
185
) : ResolvedElements {
186
+ if ( maps . length === 1 ) return maps [ 0 ]
204
187
const res : ResolvedElements = { props : { } }
205
188
const { props : baseProps } = res
206
189
for ( const { props, calls } of maps ) {
@@ -282,6 +265,66 @@ function resolveMappedType(
282
265
return res
283
266
}
284
267
268
+ function resolveIndexType (
269
+ ctx : ScriptCompileContext ,
270
+ node : TSIndexedAccessType ,
271
+ scope : TypeScope
272
+ ) : ( TSType & WithScope ) [ ] {
273
+ if ( node . indexType . type === 'TSNumberKeyword' ) {
274
+ return resolveArrayElementType ( ctx , node . objectType , scope )
275
+ }
276
+
277
+ const { indexType, objectType } = node
278
+ const types : TSType [ ] = [ ]
279
+ let keys : string [ ]
280
+ let resolved : ResolvedElements
281
+ if ( indexType . type === 'TSStringKeyword' ) {
282
+ resolved = resolveTypeElements ( ctx , objectType , scope )
283
+ keys = Object . keys ( resolved . props )
284
+ } else {
285
+ keys = resolveStringType ( ctx , indexType , scope )
286
+ resolved = resolveTypeElements ( ctx , objectType , scope )
287
+ }
288
+ for ( const key of keys ) {
289
+ const targetType = resolved . props [ key ] ?. typeAnnotation ?. typeAnnotation
290
+ if ( targetType ) {
291
+ ; ( targetType as TSType & WithScope ) . _ownerScope =
292
+ resolved . props [ key ] . _ownerScope
293
+ types . push ( targetType )
294
+ }
295
+ }
296
+ return types
297
+ }
298
+
299
+ function resolveArrayElementType (
300
+ ctx : ScriptCompileContext ,
301
+ node : Node ,
302
+ scope : TypeScope
303
+ ) : TSType [ ] {
304
+ // type[]
305
+ if ( node . type === 'TSArrayType' ) {
306
+ return [ node . elementType ]
307
+ }
308
+ // tuple
309
+ if ( node . type === 'TSTupleType' ) {
310
+ return node . elementTypes . map ( t =>
311
+ t . type === 'TSNamedTupleMember' ? t . elementType : t
312
+ )
313
+ }
314
+ if ( node . type === 'TSTypeReference' ) {
315
+ // Array<type>
316
+ if ( getReferenceName ( node ) === 'Array' && node . typeParameters ) {
317
+ return node . typeParameters . params
318
+ } else {
319
+ const resolved = resolveTypeReference ( ctx , node , scope )
320
+ if ( resolved ) {
321
+ return resolveArrayElementType ( ctx , resolved , scope )
322
+ }
323
+ }
324
+ }
325
+ ctx . error ( 'Failed to resolve element type from target type' , node )
326
+ }
327
+
285
328
function resolveStringType (
286
329
ctx : ScriptCompileContext ,
287
330
node : Node ,
@@ -322,15 +365,15 @@ function resolveStringType(
322
365
return getParam ( ) . map ( s => s [ 0 ] . toLowerCase ( ) + s . slice ( 1 ) )
323
366
default :
324
367
ctx . error (
325
- 'Unsupported type when resolving string type' ,
368
+ 'Unsupported type when resolving index type' ,
326
369
node . typeName ,
327
370
scope
328
371
)
329
372
}
330
373
}
331
374
}
332
375
}
333
- ctx . error ( 'Failed to resolve string type into finite keys' , node , scope )
376
+ ctx . error ( 'Failed to resolve index type into finite keys' , node , scope )
334
377
}
335
378
336
379
function resolveTemplateKeys (
@@ -991,19 +1034,12 @@ export function inferRuntimeType(
991
1034
return [ 'Symbol' ]
992
1035
993
1036
case 'TSIndexedAccessType' : {
994
- if (
995
- node . indexType . type === 'TSLiteralType' &&
996
- node . indexType . literal . type === 'StringLiteral'
997
- ) {
998
- try {
999
- const resolved = resolveTypeElements ( ctx , node . objectType , scope )
1000
- const key = node . indexType . literal . value
1001
- const prop = resolved . props [ key ]
1002
- return inferRuntimeType ( ctx , prop , prop . _ownerScope )
1003
- } catch ( e ) {
1004
- // avoid hard error, fallback to unknown
1005
- return [ UNKNOWN_TYPE ]
1006
- }
1037
+ try {
1038
+ const types = resolveIndexType ( ctx , node , scope )
1039
+ return flattenTypes ( ctx , types , scope )
1040
+ } catch ( e ) {
1041
+ // avoid hard error, fallback to unknown
1042
+ return [ UNKNOWN_TYPE ]
1007
1043
}
1008
1044
}
1009
1045
@@ -1020,6 +1056,9 @@ function flattenTypes(
1020
1056
types : TSType [ ] ,
1021
1057
scope : TypeScope
1022
1058
) : string [ ] {
1059
+ if ( types . length === 1 ) {
1060
+ return inferRuntimeType ( ctx , types [ 0 ] , scope )
1061
+ }
1023
1062
return [
1024
1063
...new Set (
1025
1064
( [ ] as string [ ] ) . concat (
0 commit comments