@@ -4,6 +4,8 @@ import path from 'node:path'
44import {
55 babelParse ,
66 getFileCodeAndLang ,
7+ isStaticExpression ,
8+ resolveLiteral ,
79 resolveObjectKey ,
810} from '@vue-macros/common'
911import { isDeclaration } from '@babel/types'
@@ -21,6 +23,7 @@ import type {
2123 TSInterfaceBody ,
2224 TSInterfaceDeclaration ,
2325 TSIntersectionType ,
26+ TSMappedType ,
2427 TSMethodSignature ,
2528 TSModuleBlock ,
2629 TSModuleDeclaration ,
@@ -30,6 +33,7 @@ import type {
3033 TSTypeAliasDeclaration ,
3134 TSTypeElement ,
3235 TSTypeLiteral ,
36+ UnaryExpression ,
3337} from '@babel/types'
3438
3539export type TSDeclaration =
@@ -57,7 +61,7 @@ export interface TSProperties {
5761 {
5862 value : TSResolvedType < TSType > | null
5963 optional : boolean
60- signature : TSResolvedType < TSPropertySignature >
64+ signature : TSResolvedType < TSPropertySignature | TSMappedType >
6165 }
6266 >
6367}
@@ -100,62 +104,108 @@ export async function resolveTSProperties({
100104 type,
101105 scope,
102106} : TSResolvedType <
103- TSInterfaceDeclaration | TSInterfaceBody | TSTypeLiteral | TSIntersectionType
107+ | TSInterfaceDeclaration
108+ | TSInterfaceBody
109+ | TSTypeLiteral
110+ | TSIntersectionType
111+ | TSMappedType
104112> ) : Promise < TSProperties > {
105- if ( type . type === 'TSInterfaceBody' ) {
106- return resolveTypeElements ( scope , type . body )
107- } else if ( type . type === 'TSTypeLiteral' ) {
108- return resolveTypeElements ( scope , type . members )
109- } else if ( type . type === 'TSInterfaceDeclaration' ) {
110- let properties = resolveTypeElements ( scope , type . body . body )
111- if ( type . extends ) {
112- const resolvedExtends = (
113- await Promise . all (
114- type . extends . map ( ( node ) =>
115- node . expression . type === 'Identifier'
116- ? resolveTSReferencedType ( {
117- scope,
118- type : node . expression ,
119- } )
120- : undefined
121- )
122- )
123- )
124- // eslint-disable-next-line unicorn/no-array-callback-reference
125- . filter ( filterValidExtends )
126-
127- if ( resolvedExtends . length > 0 ) {
128- const ext = (
113+ switch ( type . type ) {
114+ case 'TSInterfaceBody' :
115+ return resolveTypeElements ( scope , type . body )
116+ case 'TSTypeLiteral' :
117+ return resolveTypeElements ( scope , type . members )
118+ case 'TSInterfaceDeclaration' : {
119+ let properties = resolveTypeElements ( scope , type . body . body )
120+ if ( type . extends ) {
121+ const resolvedExtends = (
129122 await Promise . all (
130- resolvedExtends . map ( ( resolved ) => resolveTSProperties ( resolved ) )
123+ type . extends . map ( ( node ) =>
124+ node . expression . type === 'Identifier'
125+ ? resolveTSReferencedType ( {
126+ scope,
127+ type : node . expression ,
128+ } )
129+ : undefined
130+ )
131131 )
132- ) . reduceRight ( ( acc , curr ) => mergeTSProperties ( acc , curr ) )
133- properties = mergeTSProperties ( ext , properties )
132+ )
133+ // eslint-disable-next-line unicorn/no-array-callback-reference
134+ . filter ( filterValidExtends )
135+
136+ if ( resolvedExtends . length > 0 ) {
137+ const ext = (
138+ await Promise . all (
139+ resolvedExtends . map ( ( resolved ) => resolveTSProperties ( resolved ) )
140+ )
141+ ) . reduceRight ( ( acc , curr ) => mergeTSProperties ( acc , curr ) )
142+ properties = mergeTSProperties ( ext , properties )
143+ }
134144 }
145+ return properties
135146 }
136- return properties
137- } else if ( type . type === 'TSIntersectionType' ) {
138- let properties : TSProperties = {
139- callSignatures : [ ] ,
140- constructSignatures : [ ] ,
141- methods : { } ,
142- properties : { } ,
147+ case 'TSIntersectionType' : {
148+ let properties : TSProperties = {
149+ callSignatures : [ ] ,
150+ constructSignatures : [ ] ,
151+ methods : { } ,
152+ properties : { } ,
153+ }
154+ for ( const subType of type . types ) {
155+ const resolved = await resolveTSReferencedType ( {
156+ scope,
157+ type : subType ,
158+ } )
159+ if ( ! filterValidExtends ( resolved ) ) continue
160+ properties = mergeTSProperties (
161+ properties ,
162+ await resolveTSProperties ( resolved )
163+ )
164+ }
165+ return properties
143166 }
144- for ( const subType of type . types ) {
145- const resolved = await resolveTSReferencedType ( {
167+ case 'TSMappedType' : {
168+ const properties : TSProperties = {
169+ callSignatures : [ ] ,
170+ constructSignatures : [ ] ,
171+ methods : { } ,
172+ properties : { } ,
173+ }
174+ if ( ! type . typeParameter . constraint ) return properties
175+
176+ const constraint = await resolveTSReferencedType ( {
177+ type : type . typeParameter . constraint ,
146178 scope,
147- type : subType ,
148179 } )
149- if ( ! filterValidExtends ( resolved ) ) continue
150- properties = mergeTSProperties (
151- properties ,
152- await resolveTSProperties ( resolved )
153- )
180+ if ( ! constraint ?. type ) return properties
181+
182+ const types =
183+ constraint . type . type === 'TSUnionType'
184+ ? constraint . type . types
185+ : [ constraint . type ]
186+
187+ for ( const subType of types ) {
188+ if ( subType . type !== 'TSLiteralType' ) continue
189+ const literal = subType . literal
190+ if ( ! isStaticExpression ( literal ) ) continue
191+ const key = resolveLiteral (
192+ literal as Exclude < typeof literal , UnaryExpression >
193+ )
194+ if ( ! key ) continue
195+ properties . properties [ String ( key ) ] = {
196+ value : type . typeAnnotation
197+ ? { scope, type : type . typeAnnotation }
198+ : null ,
199+ optional : type . optional === '+' || type . optional === true ,
200+ signature : { type, scope } ,
201+ }
202+ }
203+
204+ return properties
154205 }
155- return properties
156- } else {
157- // @ts -expect-error type is never
158- throw new Error ( `unknown node: ${ type ?. type } ` )
206+ default :
207+ // @ts -expect-error type is never
208+ throw new Error ( `unknown node: ${ type ?. type } ` )
159209 }
160210
161211 function filterValidExtends (
0 commit comments