@@ -4,6 +4,7 @@ import { Tsoa } from '@tsoa/runtime';
4
4
const hasInitializer = ( node : ts . Node ) : node is ts . HasInitializer => Object . prototype . hasOwnProperty . call ( node , 'initializer' ) ;
5
5
const extractInitializer = ( decl ?: ts . Declaration ) => ( decl && hasInitializer ( decl ) && ( decl . initializer as ts . Expression ) ) || undefined ;
6
6
const extractImportSpecifier = ( symbol ?: ts . Symbol ) => ( symbol ?. declarations && symbol . declarations . length > 0 && ts . isImportSpecifier ( symbol . declarations [ 0 ] ) && symbol . declarations [ 0 ] ) || undefined ;
7
+ const isIterable = ( obj : any ) : obj is Iterable < any > => obj != null && typeof obj [ Symbol . iterator ] === 'function' ;
7
8
8
9
export type InitializerValue = string | number | boolean | undefined | null | InitializerValue [ ] ;
9
10
export type DefinedInitializerValue = string | number | boolean | null | DefinedInitializerValue [ ] ;
@@ -23,7 +24,19 @@ export function getInitializerValue(initializer?: ts.Expression | ts.ImportSpeci
23
24
switch ( initializer . kind ) {
24
25
case ts . SyntaxKind . ArrayLiteralExpression : {
25
26
const arrayLiteral = initializer as ts . ArrayLiteralExpression ;
26
- return arrayLiteral . elements . map ( element => getInitializerValue ( element , typeChecker ) ) ;
27
+ return arrayLiteral . elements . reduce ( ( acc , element ) => {
28
+ if ( ts . isSpreadElement ( element ) ) {
29
+ const spreadValue = getInitializerValue ( element . expression , typeChecker ) ;
30
+ if ( spreadValue && isIterable ( spreadValue ) ) {
31
+ return acc . concat ( ...spreadValue ) ;
32
+ } else {
33
+ throw new Error ( `${ typeof spreadValue } is not iterable` ) ;
34
+ }
35
+ } else {
36
+ acc . push ( getInitializerValue ( element , typeChecker ) ) ;
37
+ }
38
+ return acc ;
39
+ } , [ ] as InitializerValue [ ] ) ;
27
40
}
28
41
case ts . SyntaxKind . StringLiteral :
29
42
case ts . SyntaxKind . NoSubstitutionTemplateLiteral :
@@ -73,7 +86,18 @@ export function getInitializerValue(initializer?: ts.Expression | ts.ImportSpeci
73
86
const objectLiteral = initializer as ts . ObjectLiteralExpression ;
74
87
const nestedObject : any = { } ;
75
88
objectLiteral . properties . forEach ( ( p : any ) => {
76
- nestedObject [ p . name . text ] = getInitializerValue ( p . initializer , typeChecker ) ;
89
+ if ( ts . isSpreadAssignment ( p ) ) {
90
+ const spreadValue = getInitializerValue ( p . expression , typeChecker ) ;
91
+ if ( spreadValue ) {
92
+ if ( typeof spreadValue === 'object' ) {
93
+ Object . assign ( nestedObject , spreadValue ) ;
94
+ } else {
95
+ throw new Error ( `Spread types may only be created from object types.` ) ;
96
+ }
97
+ }
98
+ } else {
99
+ nestedObject [ p . name . text ] = getInitializerValue ( p . initializer , typeChecker ) ;
100
+ }
77
101
} ) ;
78
102
return nestedObject ;
79
103
}
0 commit comments