3
3
namespace Spatie \TypeScriptTransformer \Actions ;
4
4
5
5
use Exception ;
6
+ use PHPStan \PhpDocParser \Ast \ConstExpr \ConstExprStringNode ;
7
+ use PHPStan \PhpDocParser \Ast \ConstExpr \ConstFetchNode ;
6
8
use PHPStan \PhpDocParser \Ast \Type \ArrayShapeItemNode ;
7
9
use PHPStan \PhpDocParser \Ast \Type \ArrayShapeNode ;
8
10
use PHPStan \PhpDocParser \Ast \Type \ArrayTypeNode ;
11
+ use PHPStan \PhpDocParser \Ast \Type \ConstTypeNode ;
9
12
use PHPStan \PhpDocParser \Ast \Type \GenericTypeNode ;
10
13
use PHPStan \PhpDocParser \Ast \Type \IdentifierTypeNode ;
11
14
use PHPStan \PhpDocParser \Ast \Type \IntersectionTypeNode ;
24
27
use Spatie \TypeScriptTransformer \TypeScriptNodes \TypeScriptGeneric ;
25
28
use Spatie \TypeScriptTransformer \TypeScriptNodes \TypeScriptIdentifier ;
26
29
use Spatie \TypeScriptTransformer \TypeScriptNodes \TypeScriptIntersection ;
30
+ use Spatie \TypeScriptTransformer \TypeScriptNodes \TypeScriptLiteral ;
27
31
use Spatie \TypeScriptTransformer \TypeScriptNodes \TypeScriptNode ;
28
32
use Spatie \TypeScriptTransformer \TypeScriptNodes \TypeScriptNull ;
29
33
use Spatie \TypeScriptTransformer \TypeScriptNodes \TypeScriptNumber ;
@@ -65,18 +69,52 @@ protected function identifierNode(
65
69
return new TypeScriptAny ();
66
70
}
67
71
68
- if ($ node ->name === 'string ' || $ node ->name === 'class-string ' ) {
72
+ if ($ node ->name === 'string '
73
+ || $ node ->name === 'class-string '
74
+ || $ node ->name === 'interface-string '
75
+ || $ node ->name === 'trait-string '
76
+ || $ node ->name === 'callable-string '
77
+ || $ node ->name === 'enum-string '
78
+ || $ node ->name === 'lowercase-string '
79
+ || $ node ->name === 'uppercase-string '
80
+ || $ node ->name === 'literal-string '
81
+ || $ node ->name === 'numeric-string '
82
+ || $ node ->name === 'non-empty-string '
83
+ || $ node ->name === 'non-empty-lowercase-string '
84
+ || $ node ->name === 'non-empty-uppercase-string '
85
+ || $ node ->name === 'truthy-string '
86
+ || $ node ->name === 'non-falsy-string '
87
+ || $ node ->name === 'non-empty-literal-string '
88
+ ) {
69
89
return new TypeScriptString ();
70
90
}
71
91
72
- if ($ node ->name === 'float ' || $ node ->name === 'double ' || $ node ->name === 'int ' || $ node ->name === 'integer ' ) {
92
+ if ($ node ->name === 'float '
93
+ || $ node ->name === 'double '
94
+ || $ node ->name === 'int '
95
+ || $ node ->name === 'integer '
96
+ || $ node ->name === 'positive-int '
97
+ || $ node ->name === 'negative-int '
98
+ || $ node ->name === 'non-positive-int '
99
+ || $ node ->name === 'non-negative-int '
100
+ || $ node ->name === 'non-zero-int '
101
+ || $ node ->name === 'numeric '
102
+ ) {
73
103
return new TypeScriptNumber ();
74
104
}
75
105
76
106
if ($ node ->name === 'bool ' || $ node ->name === 'boolean ' || $ node ->name === 'true ' || $ node ->name === 'false ' ) {
77
107
return new TypeScriptBoolean ();
78
108
}
79
109
110
+ if ($ node ->name === 'scalar ' ) {
111
+ return new TypeScriptUnion ([
112
+ new TypeScriptNumber (),
113
+ new TypeScriptString (),
114
+ new TypeScriptBoolean (),
115
+ ]);
116
+ }
117
+
80
118
if ($ node ->name === 'void ' ) {
81
119
return new TypeScriptVoid ();
82
120
}
@@ -93,10 +131,6 @@ protected function identifierNode(
93
131
return new TypeScriptNull ();
94
132
}
95
133
96
- if ($ node ->name === 'self ' || $ node ->name === 'static ' ) {
97
- return new TypeReference (new ClassStringReference ($ phpClassNode ->getName ()));
98
- }
99
-
100
134
if ($ node ->name === 'object ' ) {
101
135
return new TypeScriptObject ([]);
102
136
}
@@ -108,24 +142,41 @@ protected function identifierNode(
108
142
]);
109
143
}
110
144
111
- if (class_exists ($ node ->name ) || interface_exists ($ node ->name )) {
112
- return new TypeReference (new ClassStringReference ($ node ->name ));
145
+ $ className = $ this ->resolveClass ($ node ->name , $ phpClassNode );
146
+
147
+ if ($ className ) {
148
+ return new TypeReference (new ClassStringReference ($ className ));
149
+ }
150
+
151
+ return new TypeScriptUnknown ();
152
+ }
153
+
154
+ protected function resolveClass (
155
+ string $ className ,
156
+ ?PhpClassNode $ phpClassNode
157
+ ): ?string {
158
+ if ($ className === 'self ' || $ className === 'static ' || $ className === '$this ' ) {
159
+ return $ phpClassNode ?->getName();
160
+ }
161
+
162
+ if (class_exists ($ className ) || interface_exists ($ className )) {
163
+ return $ className ;
113
164
}
114
165
115
166
if ($ phpClassNode === null ) {
116
- return new TypeScriptUnknown () ;
167
+ return null ;
117
168
}
118
169
119
170
$ referenced = $ this ->findClassNameFqcnAction ->execute (
120
171
$ phpClassNode ,
121
- $ node -> name
172
+ $ className
122
173
);
123
174
124
175
if (class_exists ($ referenced ) || interface_exists ($ referenced )) {
125
- return new TypeReference ( new ClassStringReference ( $ referenced)) ;
176
+ return $ referenced ;
126
177
}
127
178
128
- return new TypeScriptUnknown () ;
179
+ return null ;
129
180
}
130
181
131
182
protected function arrayTypeNode (
@@ -143,8 +194,13 @@ protected function arrayShapeNode(
143
194
): TypeScriptObject {
144
195
return new TypeScriptObject (array_map (
145
196
function (ArrayShapeItemNode |ObjectShapeItemNode $ item ) use ($ phpClassNode ) {
197
+ $ name = match ($ item ->keyName ::class) {
198
+ IdentifierTypeNode::class => $ item ->keyName ->name ,
199
+ ConstExprStringNode::class => $ item ->keyName ->value ,
200
+ };
201
+
146
202
return new TypeScriptProperty (
147
- ( string ) $ item -> keyName ,
203
+ $ name ,
148
204
$ this ->execute ($ item ->valueType , $ phpClassNode ),
149
205
isOptional: $ item ->optional
150
206
);
@@ -194,26 +250,27 @@ protected function genericNode(
194
250
GenericTypeNode $ node ,
195
251
?PhpClassNode $ phpClassNode
196
252
): TypeScriptNode {
197
- if ($ node ->type ->name === 'array ' || $ node ->type ->name === 'Array ' ) {
253
+ if ($ node ->type ->name === 'array '
254
+ || $ node ->type ->name === 'Array '
255
+ || $ node ->type ->name === 'non-empty-array '
256
+ || $ node ->type ->name === 'list '
257
+ || $ node ->type ->name === 'non-empty-list '
258
+ ) {
198
259
return $ this ->genericArrayNode ($ node , $ phpClassNode );
199
260
}
200
261
201
- $ type = $ this ->execute ($ node ->type , $ phpClassNode );
262
+ if ($ node ->type ->name === 'int ' ) {
263
+ return new TypeScriptNumber ();
264
+ }
202
265
203
- if ($ type instanceof TypeScriptString ) {
204
- return $ type ; // class-string<something> case
266
+ if ($ node -> type -> name === ' key-of ' || $ node -> type -> name === ' value-of ' ) {
267
+ return $ this -> keyOrValueOfGenericNode ( $ node , $ phpClassNode );
205
268
}
206
269
207
- return new TypeScriptGeneric (
208
- $ type ,
209
- array_map (
210
- fn (TypeNode $ type ) => $ this ->execute ($ type , $ phpClassNode ),
211
- $ node ->genericTypes
212
- )
213
- );
270
+ return $ this ->defaultGenericNode ($ node , $ phpClassNode );
214
271
}
215
272
216
- private function genericArrayNode (GenericTypeNode $ node , ?PhpClassNode $ phpClassNode ): TypeScriptGeneric |TypeScriptArray
273
+ protected function genericArrayNode (GenericTypeNode $ node , ?PhpClassNode $ phpClassNode ): TypeScriptGeneric |TypeScriptArray
217
274
{
218
275
$ genericTypes = count ($ node ->genericTypes );
219
276
@@ -241,4 +298,57 @@ private function genericArrayNode(GenericTypeNode $node, ?PhpClassNode $phpClass
241
298
[$ key , $ value ,]
242
299
);
243
300
}
301
+
302
+ protected function keyOrValueOfGenericNode (GenericTypeNode $ node , ?PhpClassNode $ phpClassNode ): TypeScriptNode
303
+ {
304
+ if (count ($ node ->genericTypes ) === 1
305
+ && $ node ->genericTypes [0 ] instanceof ConstTypeNode
306
+ && $ node ->genericTypes [0 ]->constExpr instanceof ConstFetchNode
307
+ ) {
308
+ return $ this ->keyOrValueOfArrayConstNode ($ node , $ phpClassNode , $ node ->genericTypes [0 ]->constExpr );
309
+ }
310
+
311
+
312
+ return $ this ->defaultGenericNode ($ node , $ phpClassNode );
313
+ }
314
+
315
+ protected function keyOrValueOfArrayConstNode (
316
+ GenericTypeNode $ node ,
317
+ ?PhpClassNode $ phpClassNode ,
318
+ ConstFetchNode $ constFetchNode ,
319
+ ): TypeScriptNode {
320
+ $ class = $ this ->resolveClass ($ constFetchNode ->className , $ phpClassNode );
321
+
322
+ if ($ class === null ) {
323
+ return $ this ->defaultGenericNode ($ node , $ phpClassNode );
324
+ }
325
+
326
+ $ array = $ class ::{$ constFetchNode ->name };
327
+
328
+ $ items = $ node ->type ->name === 'key-of '
329
+ ? array_keys ($ array )
330
+ : array_values ($ array );
331
+
332
+ return new TypeScriptUnion (array_map (
333
+ fn (mixed $ key ) => new TypeScriptLiteral ($ key ),
334
+ $ items
335
+ ));
336
+ }
337
+
338
+ protected function defaultGenericNode (GenericTypeNode $ node , ?PhpClassNode $ phpClassNode ): TypeScriptNode
339
+ {
340
+ $ type = $ this ->execute ($ node ->type , $ phpClassNode );
341
+
342
+ if ($ type instanceof TypeScriptString) {
343
+ return $ type ; // class-string<something> case
344
+ }
345
+
346
+ return new TypeScriptGeneric (
347
+ $ type ,
348
+ array_map (
349
+ fn (TypeNode $ type ) => $ this ->execute ($ type , $ phpClassNode ),
350
+ $ node ->genericTypes
351
+ )
352
+ );
353
+ }
244
354
}
0 commit comments