@@ -166,7 +166,7 @@ function deriveFunctionSchema(functionDeclaration: ts.FunctionDeclaration, expor
166
166
const functionSymbol = context . typeChecker . getSymbolAtLocation ( functionIdentifier ) ?? throwError ( `Function '${ exportedFunctionName } ' didn't have a symbol` ) ;
167
167
const functionType = context . typeChecker . getTypeOfSymbolAtLocation ( functionSymbol , functionDeclaration ) ;
168
168
169
- const functionDescription = ts . displayPartsToString ( functionSymbol . getDocumentationComment ( context . typeChecker ) ) . trim ( ) ;
169
+ const functionDescription = getDescriptionFromJsDoc ( functionSymbol , context . typeChecker ) ;
170
170
const markedReadonlyInJsDoc = functionSymbol . getJsDocTags ( ) . find ( e => e . name === "readonly" ) !== undefined ;
171
171
const parallelDegreeResult = getParallelDegreeFromJsDoc ( functionSymbol , markedReadonlyInJsDoc ) ;
172
172
@@ -190,14 +190,19 @@ function deriveFunctionSchema(functionDeclaration: ts.FunctionDeclaration, expor
190
190
191
191
return Result . collectErrors3 ( functionSchemaArguments , returnTypeResult , parallelDegreeResult )
192
192
. map ( ( [ functionSchemaArgs , returnType , parallelDegree ] ) => ( {
193
- description : functionDescription ? functionDescription : null ,
193
+ description : functionDescription ,
194
194
ndcKind : markedReadonlyInJsDoc ? schema . FunctionNdcKind . Function : schema . FunctionNdcKind . Procedure ,
195
195
arguments : functionSchemaArgs ,
196
196
resultType : returnType ,
197
197
parallelDegree,
198
198
} ) ) ;
199
199
}
200
200
201
+ function getDescriptionFromJsDoc ( symbol : ts . Symbol , typeChecker : ts . TypeChecker ) : string | null {
202
+ const description = ts . displayPartsToString ( symbol . getDocumentationComment ( typeChecker ) ) . trim ( )
203
+ return description ? description : null ;
204
+ }
205
+
201
206
function getParallelDegreeFromJsDoc ( functionSymbol : ts . Symbol , functionIsReadonly : boolean ) : Result < number | null , string [ ] > {
202
207
const parallelDegreeTag = functionSymbol . getJsDocTags ( ) . find ( e => e . name === "paralleldegree" ) ;
203
208
if ( parallelDegreeTag === undefined ) {
@@ -422,15 +427,15 @@ function deriveSchemaTypeIfObjectType(tsType: ts.Type, typePath: TypePathSegment
422
427
return new Ok ( { type : 'named' , name : info . generatedTypeName , kind : "object" } ) ;
423
428
}
424
429
425
- context . objectTypeDefinitions [ info . generatedTypeName ] = { properties : [ ] } ; // Break infinite recursion
430
+ context . objectTypeDefinitions [ info . generatedTypeName ] = { properties : [ ] , description : null } ; // Break infinite recursion
426
431
427
- const propertyResults = Result . traverseAndCollectErrors ( Array . from ( info . members ) , ( [ propertyName , propertyType ] ) => {
428
- return deriveSchemaTypeForTsType ( propertyType , [ ...typePath , { segmentType : "ObjectProperty" , typeName : info . generatedTypeName , propertyName } ] , context , recursionDepth + 1 )
429
- . map ( propertyType => ( { propertyName : propertyName , type : propertyType } ) ) ;
432
+ const propertyResults = Result . traverseAndCollectErrors ( Array . from ( info . properties ) , ( [ propertyName , propertyInfo ] ) => {
433
+ return deriveSchemaTypeForTsType ( propertyInfo . tsType , [ ...typePath , { segmentType : "ObjectProperty" , typeName : info . generatedTypeName , propertyName } ] , context , recursionDepth + 1 )
434
+ . map ( propertyType => ( { propertyName : propertyName , type : propertyType , description : propertyInfo . description } ) ) ;
430
435
} ) ;
431
436
432
437
if ( propertyResults instanceof Ok ) {
433
- context . objectTypeDefinitions [ info . generatedTypeName ] = { properties : propertyResults . data }
438
+ context . objectTypeDefinitions [ info . generatedTypeName ] = { properties : propertyResults . data , description : info . description }
434
439
return new Ok ( { type : 'named' , name : info . generatedTypeName , kind : "object" } )
435
440
} else {
436
441
// Remove the recursion short-circuit to ensure errors are raised if this type is encountered again
@@ -475,12 +480,19 @@ function unwrapNullableType(ty: ts.Type): [ts.Type, schema.NullOrUndefinability]
475
480
: null ;
476
481
}
477
482
483
+ type PropertyTypeInfo = {
484
+ tsType : ts . Type ,
485
+ description : string | null ,
486
+ }
487
+
478
488
type ObjectTypeInfo = {
479
489
// The name of the type; it may be a generated name if it is an anonymous type, or if it from an external module
480
490
generatedTypeName : string ,
481
- // The member properties of the object type. The types are
491
+ // The properties of the object type. The types are
482
492
// concrete types after type parameter resolution
483
- members : Map < string , ts . Type >
493
+ properties : Map < string , PropertyTypeInfo > ,
494
+ // The JSDoc comment on the type
495
+ description : string | null ,
484
496
}
485
497
486
498
// TODO: This can be vastly simplified when I yeet the name qualification stuff
@@ -490,30 +502,36 @@ function getObjectTypeInfo(tsType: ts.Type, typePath: TypePathSegment[], typeChe
490
502
return null ;
491
503
}
492
504
505
+ const symbolForDocs = tsType . aliasSymbol ?? tsType . getSymbol ( ) ;
506
+ const description = symbolForDocs ? getDescriptionFromJsDoc ( symbolForDocs , typeChecker ) : null ;
507
+
493
508
// Anonymous object type - this covers:
494
509
// - {a: number, b: string}
495
510
// - type Bar = { test: string }
496
511
// - type GenericBar<T> = { data: T }
497
512
if ( tsutils . isObjectType ( tsType ) && tsutils . isObjectFlagSet ( tsType , ts . ObjectFlags . Anonymous ) ) {
498
513
return {
499
514
generatedTypeName : qualifyTypeName ( tsType , typePath , tsType . aliasSymbol ? typeChecker . typeToString ( tsType ) : null , functionsFilePath ) ,
500
- members : getMembers ( tsType . getProperties ( ) , typeChecker )
515
+ properties : getMembers ( tsType . getProperties ( ) , typeChecker ) ,
516
+ description,
501
517
}
502
518
}
503
519
// Interface type - this covers:
504
520
// interface IThing { test: string }
505
521
else if ( tsutils . isObjectType ( tsType ) && tsutils . isObjectFlagSet ( tsType , ts . ObjectFlags . Interface ) ) {
506
522
return {
507
523
generatedTypeName : tsType . getSymbol ( ) ?. name ?? generateTypeNameFromTypePath ( typePath ) ,
508
- members : getMembers ( tsType . getProperties ( ) , typeChecker )
524
+ properties : getMembers ( tsType . getProperties ( ) , typeChecker ) ,
525
+ description,
509
526
}
510
527
}
511
528
// Generic interface type - this covers:
512
529
// interface IGenericThing<T> { data: T }
513
530
else if ( tsutils . isTypeReference ( tsType ) && tsutils . isObjectFlagSet ( tsType . target , ts . ObjectFlags . Interface ) && typeChecker . isArrayType ( tsType ) === false && tsType . getSymbol ( ) ?. getName ( ) !== "Promise" ) {
514
531
return {
515
532
generatedTypeName : tsType . getSymbol ( ) ?. name ?? generateTypeNameFromTypePath ( typePath ) ,
516
- members : getMembers ( tsType . getProperties ( ) , typeChecker )
533
+ properties : getMembers ( tsType . getProperties ( ) , typeChecker ) ,
534
+ description,
517
535
}
518
536
}
519
537
// Intersection type - this covers:
@@ -523,16 +541,21 @@ function getObjectTypeInfo(tsType: ts.Type, typePath: TypePathSegment[], typeChe
523
541
else if ( tsutils . isIntersectionType ( tsType ) ) {
524
542
return {
525
543
generatedTypeName : qualifyTypeName ( tsType , typePath , tsType . aliasSymbol ? typeChecker . typeToString ( tsType ) : null , functionsFilePath ) ,
526
- members : getMembers ( tsType . getProperties ( ) , typeChecker )
544
+ properties : getMembers ( tsType . getProperties ( ) , typeChecker ) ,
545
+ description,
527
546
}
528
547
}
529
548
530
549
return null ;
531
550
}
532
551
533
- function getMembers ( propertySymbols : ts . Symbol [ ] , typeChecker : ts . TypeChecker ) {
552
+ function getMembers ( propertySymbols : ts . Symbol [ ] , typeChecker : ts . TypeChecker ) : Map < string , PropertyTypeInfo > {
534
553
return new Map (
535
- propertySymbols . map ( symbol => [ symbol . name , typeChecker . getTypeOfSymbol ( symbol ) ] )
554
+ propertySymbols . map ( symbol => {
555
+ const tsType = typeChecker . getTypeOfSymbol ( symbol ) ;
556
+ const description = getDescriptionFromJsDoc ( symbol , typeChecker ) ;
557
+ return [ symbol . name , { tsType, description} ]
558
+ } )
536
559
)
537
560
}
538
561
0 commit comments