@@ -5,18 +5,20 @@ import {
5
5
TSEnumDeclaration ,
6
6
TSExpressionWithTypeArguments ,
7
7
TSFunctionType ,
8
+ TSMappedType ,
8
9
TSMethodSignature ,
9
10
TSPropertySignature ,
10
11
TSType ,
11
12
TSTypeAnnotation ,
12
13
TSTypeElement ,
13
- TSTypeReference
14
+ TSTypeReference ,
15
+ TemplateLiteral
14
16
} from '@babel/types'
15
17
import { UNKNOWN_TYPE } from './utils'
16
18
import { ScriptCompileContext } from './context'
17
19
import { ImportBinding } from '../compileScript'
18
20
import { TSInterfaceDeclaration } from '@babel/types'
19
- import { hasOwn , isArray } from '@vue/shared'
21
+ import { capitalize , hasOwn , isArray } from '@vue/shared'
20
22
import { Expression } from '@babel/types'
21
23
22
24
export interface TypeScope {
@@ -65,14 +67,23 @@ function innerResolveTypeElements(
65
67
return ret
66
68
}
67
69
case 'TSExpressionWithTypeArguments' : // referenced by interface extends
68
- case 'TSTypeReference' :
69
- return resolveTypeElements ( ctx , resolveTypeReference ( ctx , node ) )
70
+ case 'TSTypeReference' : {
71
+ const resolved = resolveTypeReference ( ctx , node )
72
+ if ( resolved ) {
73
+ return resolveTypeElements ( ctx , resolved )
74
+ } else {
75
+ // TODO Pick / Omit
76
+ ctx . error ( `Failed to resolved type reference` , node )
77
+ }
78
+ }
70
79
case 'TSUnionType' :
71
80
case 'TSIntersectionType' :
72
81
return mergeElements (
73
82
node . types . map ( t => resolveTypeElements ( ctx , t ) ) ,
74
83
node . type
75
84
)
85
+ case 'TSMappedType' :
86
+ return resolveMappedType ( ctx , node )
76
87
}
77
88
ctx . error ( `Unsupported type in SFC macro: ${ node . type } ` , node )
78
89
}
@@ -113,6 +124,10 @@ function typeElementsToMap(
113
124
: null
114
125
if ( name && ! e . computed ) {
115
126
ret [ name ] = e
127
+ } else if ( e . key . type === 'TemplateLiteral' ) {
128
+ for ( const key of resolveTemplateKeys ( ctx , e . key ) ) {
129
+ ret [ key ] = e
130
+ }
116
131
} else {
117
132
ctx . error (
118
133
`computed keys are not supported in types referenced by SFC macros.` ,
@@ -136,7 +151,11 @@ function mergeElements(
136
151
if ( ! ( key in res ) ) {
137
152
res [ key ] = m [ key ]
138
153
} else {
139
- res [ key ] = createProperty ( res [ key ] . key , type , [ res [ key ] , m [ key ] ] )
154
+ res [ key ] = createProperty ( res [ key ] . key , {
155
+ type,
156
+ // @ts -ignore
157
+ types : [ res [ key ] , m [ key ] ]
158
+ } )
140
159
}
141
160
}
142
161
if ( m . __callSignatures ) {
@@ -148,19 +167,15 @@ function mergeElements(
148
167
149
168
function createProperty (
150
169
key : Expression ,
151
- type : 'TSUnionType' | 'TSIntersectionType' ,
152
- types : Node [ ]
170
+ typeAnnotation : TSType
153
171
) : TSPropertySignature {
154
172
return {
155
173
type : 'TSPropertySignature' ,
156
174
key,
157
175
kind : 'get' ,
158
176
typeAnnotation : {
159
177
type : 'TSTypeAnnotation' ,
160
- typeAnnotation : {
161
- type,
162
- types : types as TSType [ ]
163
- }
178
+ typeAnnotation
164
179
}
165
180
}
166
181
}
@@ -183,22 +198,102 @@ function resolveInterfaceMembers(
183
198
return base
184
199
}
185
200
186
- function resolveTypeReference (
201
+ function resolveMappedType (
187
202
ctx : ScriptCompileContext ,
188
- node : TSTypeReference | TSExpressionWithTypeArguments ,
189
- scope ?: TypeScope
190
- ) : Node
191
- function resolveTypeReference (
203
+ node : TSMappedType
204
+ ) : ResolvedElements {
205
+ const res : ResolvedElements = { }
206
+ if ( ! node . typeParameter . constraint ) {
207
+ ctx . error ( `mapped type used in macros must have a finite constraint.` , node )
208
+ }
209
+ const keys = resolveStringType ( ctx , node . typeParameter . constraint )
210
+ for ( const key of keys ) {
211
+ res [ key ] = createProperty (
212
+ {
213
+ type : 'Identifier' ,
214
+ name : key
215
+ } ,
216
+ node . typeAnnotation !
217
+ )
218
+ }
219
+ return res
220
+ }
221
+
222
+ function resolveStringType ( ctx : ScriptCompileContext , node : Node ) : string [ ] {
223
+ switch ( node . type ) {
224
+ case 'StringLiteral' :
225
+ return [ node . value ]
226
+ case 'TSLiteralType' :
227
+ return resolveStringType ( ctx , node . literal )
228
+ case 'TSUnionType' :
229
+ return node . types . map ( t => resolveStringType ( ctx , t ) ) . flat ( )
230
+ case 'TemplateLiteral' : {
231
+ return resolveTemplateKeys ( ctx , node )
232
+ }
233
+ case 'TSTypeReference' : {
234
+ const resolved = resolveTypeReference ( ctx , node )
235
+ if ( resolved ) {
236
+ return resolveStringType ( ctx , resolved )
237
+ }
238
+ if ( node . typeName . type === 'Identifier' ) {
239
+ const getParam = ( index = 0 ) =>
240
+ resolveStringType ( ctx , node . typeParameters ! . params [ index ] )
241
+ switch ( node . typeName . name ) {
242
+ case 'Extract' :
243
+ return getParam ( 1 )
244
+ case 'Exclude' : {
245
+ const excluded = getParam ( 1 )
246
+ return getParam ( ) . filter ( s => ! excluded . includes ( s ) )
247
+ }
248
+ case 'Uppercase' :
249
+ return getParam ( ) . map ( s => s . toUpperCase ( ) )
250
+ case 'Lowercase' :
251
+ return getParam ( ) . map ( s => s . toLowerCase ( ) )
252
+ case 'Capitalize' :
253
+ return getParam ( ) . map ( capitalize )
254
+ case 'Uncapitalize' :
255
+ return getParam ( ) . map ( s => s [ 0 ] . toLowerCase ( ) + s . slice ( 1 ) )
256
+ default :
257
+ ctx . error ( 'Failed to resolve type reference' , node )
258
+ }
259
+ }
260
+ }
261
+ }
262
+ ctx . error ( 'Failed to resolve string type into finite keys' , node )
263
+ }
264
+
265
+ function resolveTemplateKeys (
192
266
ctx : ScriptCompileContext ,
193
- node : TSTypeReference | TSExpressionWithTypeArguments ,
194
- scope : TypeScope ,
195
- bail : false
196
- ) : Node | undefined
267
+ node : TemplateLiteral
268
+ ) : string [ ] {
269
+ if ( ! node . expressions . length ) {
270
+ return [ node . quasis [ 0 ] . value . raw ]
271
+ }
272
+
273
+ const res : string [ ] = [ ]
274
+ const e = node . expressions [ 0 ]
275
+ const q = node . quasis [ 0 ]
276
+ const leading = q ? q . value . raw : ``
277
+ const resolved = resolveStringType ( ctx , e )
278
+ const restResolved = resolveTemplateKeys ( ctx , {
279
+ ...node ,
280
+ expressions : node . expressions . slice ( 1 ) ,
281
+ quasis : q ? node . quasis . slice ( 1 ) : node . quasis
282
+ } )
283
+
284
+ for ( const r of resolved ) {
285
+ for ( const rr of restResolved ) {
286
+ res . push ( leading + r + rr )
287
+ }
288
+ }
289
+
290
+ return res
291
+ }
292
+
197
293
function resolveTypeReference (
198
294
ctx : ScriptCompileContext ,
199
295
node : TSTypeReference | TSExpressionWithTypeArguments ,
200
- scope = getRootScope ( ctx ) ,
201
- bail = true
296
+ scope = getRootScope ( ctx )
202
297
) : Node | undefined {
203
298
const ref = node . type === 'TSTypeReference' ? node . typeName : node . expression
204
299
if ( ref . type === 'Identifier' ) {
@@ -211,9 +306,6 @@ function resolveTypeReference(
211
306
// TODO qualified name, e.g. Foo.Bar
212
307
// return resolveTypeReference()
213
308
}
214
- if ( bail ) {
215
- ctx . error ( 'Failed to resolve type reference.' , node )
216
- }
217
309
}
218
310
219
311
function getRootScope ( ctx : ScriptCompileContext ) : TypeScope {
@@ -332,7 +424,7 @@ export function inferRuntimeType(
332
424
333
425
case 'TSTypeReference' :
334
426
if ( node . typeName . type === 'Identifier' ) {
335
- const resolved = resolveTypeReference ( ctx , node , scope , false )
427
+ const resolved = resolveTypeReference ( ctx , node , scope )
336
428
if ( resolved ) {
337
429
return inferRuntimeType ( ctx , resolved , scope )
338
430
}
0 commit comments