11import {
22 factory ,
3+ type Identifier ,
34 type ImportDeclaration ,
5+ type QualifiedName ,
46 SyntaxKind ,
57 type TypeNode ,
68} from 'typescript' ;
@@ -18,16 +20,18 @@ export type NameNodePathResolver<T> = (
1820 nodeParts : string [ ] ,
1921) => {
2022 path : string ;
21- name : string ;
23+ importName : string ;
2224 isTypeOnly : boolean ;
25+ typeIdentifiers : string [ ] ;
2326} ;
2427
2528export class PhpDocTypeNodeToTypescriptTypeNodeTranspiler {
2629 constructor (
2730 public nameNodePathResolver : ( nodeParts : string [ ] ) => {
2831 path : string ;
29- name : string ;
32+ importName : string ;
3033 isTypeOnly : boolean ;
34+ typeIdentifiers : string [ ] ;
3135 } ,
3236 ) { }
3337
@@ -87,6 +91,7 @@ export class PhpDocTypeNodeToTypescriptTypeNodeTranspiler {
8791 'non-empty-array' ,
8892 'list' ,
8993 'non-empty-list' ,
94+ '\\Illuminate\\Support\\Collection' ,
9095 '\\Illuminate\\Database\\Eloquent\\Collection' ,
9196 ] . includes ( sourceTypeNode . type . name )
9297 ) {
@@ -167,7 +172,11 @@ export class PhpDocTypeNodeToTypescriptTypeNodeTranspiler {
167172 return factory . createToken ( SyntaxKind . VoidKeyword ) ;
168173 }
169174
170- if ( sourceTypeNode . name === 'null' ) {
175+ if (
176+ [ '\\Illuminate\\Http\\Resources\\MissingValue' , 'null' ] . includes (
177+ sourceTypeNode . name ,
178+ )
179+ ) {
171180 return factory . createLiteralTypeNode ( factory . createNull ( ) ) ;
172181 }
173182
@@ -179,10 +188,11 @@ export class PhpDocTypeNodeToTypescriptTypeNodeTranspiler {
179188 if ( / ^ [ A - Z \\ ] / . test ( sourceTypeNode . name ) ) {
180189 const nameNodeParts = sourceTypeNode . name . split ( '\\' ) ;
181190
182- const { name , path, isTypeOnly } =
191+ const { path, isTypeOnly, importName , typeIdentifiers } =
183192 this . nameNodePathResolver ( nameNodeParts ) ;
184193
185- if ( name !== 'string' && path !== '' ) {
194+ // For external types, generate import statement
195+ if ( importName !== 'string' && path !== '' ) {
186196 this . importDeclarations . push (
187197 factory . createImportDeclaration (
188198 undefined ,
@@ -193,7 +203,7 @@ export class PhpDocTypeNodeToTypescriptTypeNodeTranspiler {
193203 factory . createImportSpecifier (
194204 false ,
195205 undefined ,
196- factory . createIdentifier ( name ) ,
206+ factory . createIdentifier ( importName ) ,
197207 ) ,
198208 ] ) ,
199209 ) ,
@@ -203,10 +213,30 @@ export class PhpDocTypeNodeToTypescriptTypeNodeTranspiler {
203213 ) ;
204214 }
205215
206- return factory . createTypeReferenceNode (
207- factory . createIdentifier ( name ) ,
208- undefined ,
216+ // Build qualified name recursively for multiple identifiers
217+ let typeNameNode : QualifiedName | Identifier = factory . createIdentifier (
218+ typeIdentifiers [ 0 ] ,
209219 ) ;
220+
221+ // typeIdentifiers = ['RestaurantNamespace', 'IPizza']
222+ // factory.createQualifiedName(
223+ // factory.createIdentifier("RestaurantNamespace"),
224+ // factory.createIdentifier("IPizza")
225+ // )
226+ // ⬇️
227+ // export interface IMyOrder {
228+ // ...
229+ // pizza: RestaurantNamespace.IPizza;
230+ // ...
231+ // }
232+ for ( let i = 1 ; i < typeIdentifiers . length ; i += 1 ) {
233+ typeNameNode = factory . createQualifiedName (
234+ typeNameNode ,
235+ factory . createIdentifier ( typeIdentifiers [ i ] ) ,
236+ ) ;
237+ }
238+
239+ return factory . createTypeReferenceNode ( typeNameNode , undefined ) ;
210240 }
211241 }
212242
0 commit comments