@@ -304,7 +304,6 @@ export function resolve(
304304 // For other and anonymous types, don't use ref
305305 return resolveObjectType ( type , spec )
306306 }
307-
308307 // Add to spec components if not already resolved
309308 // tslint:disable-next-line: strict-type-predicates
310309 if ( typeof spec . components ! . schemas ! [ typeName ] === 'undefined' ) {
@@ -422,6 +421,14 @@ function resolveProperties(
422421 writeOnly : true
423422 } )
424423 }
424+ if (
425+ jsDocTags . some ( tag => tag . name === 'additionalproperties' ) &&
426+ ! ( '$ref' in resolvedType )
427+ ) {
428+ appendMetaToResolvedType ( resolvedType , {
429+ additionalProperties : false
430+ } )
431+ }
425432 // JSDoc tags
426433 appendJsDocTags ( jsDocTags , resolvedType )
427434 // initializer
@@ -451,11 +458,9 @@ function resolveProperties(
451458 // OpenAPI don't want the required[] prop if it's empty
452459 delete result . required
453460 }
454-
455461 // Check for index signatures regardless of whether explicit properties exist
456462 const stringIndexType = type . getStringIndexType ( )
457463 const numberIndexType = type . getNumberIndexType ( )
458-
459464 // Handle mapped types and objects with index signatures (ex: { [key: string]: any } or Record<string, any>)
460465 if (
461466 ( typeof stringIndexType !== 'undefined' &&
@@ -474,7 +479,6 @@ function resolveProperties(
474479 // Check if the type represents a Record-like structure that should have additionalProperties
475480 const typeSymbol = type . getSymbol ( )
476481 const typeText = type . getText ( )
477-
478482 // Handle cases where the type might be a transformed Record type
479483 if ( typeSymbol ?. getName ( ) === '__type' || typeText . includes ( 'Record<' ) ) {
480484 // For anonymous types that might be transformed Record types,
@@ -485,7 +489,6 @@ function resolveProperties(
485489 if ( apparentType && apparentType !== type ) {
486490 const apparentStringIndexType = apparentType . getStringIndexType ( )
487491 const apparentNumberIndexType = apparentType . getNumberIndexType ( )
488-
489492 if (
490493 ( typeof apparentStringIndexType !== 'undefined' &&
491494 apparentStringIndexType . getText ( ) !== 'never' ) ||
@@ -500,7 +503,6 @@ function resolveProperties(
500503 }
501504 }
502505 }
503-
504506 return result
505507}
506508
@@ -530,7 +532,6 @@ function resolveMappedObjectType(
530532 const omittedKeys = typeArguments [ 1 ] . isUnion ( )
531533 ? typeArguments [ 1 ] . getUnionTypes ( ) . map ( t => String ( t . getLiteralValue ( ) ) )
532534 : [ String ( typeArguments [ 1 ] . getLiteralValue ( ) ) ]
533-
534535 if ( helperName === 'Omit' && omittedKeys . includes ( 'toJSON' ) ) {
535536 // If we're omitting toJSON, don't follow it to avoid infinite loops
536537 shouldSkipToJSON = true
@@ -539,22 +540,19 @@ function resolveMappedObjectType(
539540 shouldSkipToJSON = true
540541 }
541542 }
542-
543543 // Check if the subject type has a toJSON method
544544 const toJSONProperty = subjectType . getProperty ( 'toJSON' )
545545 if ( toJSONProperty && ! shouldSkipToJSON ) {
546546 const node = getDeclarationForProperty ( subjectType , toJSONProperty ) as
547547 | MethodDeclaration
548548 | MethodSignature
549549 const toJSONReturnType = resolve ( node . getReturnType ( ) , spec )
550-
551550 // Apply mapped type transformation to the toJSON return type
552551 if ( '$ref' in toJSONReturnType ) {
553552 // For reference types, we need to create a new schema with the transformation applied
554553 // This is more complex, so for now we'll fall back to normal resolution
555554 return resolveObjectType ( type , spec )
556555 }
557-
558556 if ( toJSONReturnType . type === 'object' && toJSONReturnType . properties ) {
559557 const transformedSchema = { ...toJSONReturnType }
560558
@@ -571,7 +569,6 @@ function resolveMappedObjectType(
571569 return transformedSchema
572570 }
573571 }
574-
575572 // Fall back to normal object resolution if no toJSON or transformation failed
576573 return resolveObjectType ( type , spec )
577574}
@@ -582,7 +579,6 @@ function resolveMappedObjectType(
582579function hasInterfaceInheritance ( type : Type ) : boolean {
583580 const symbol = type . getSymbol ( )
584581 if ( ! symbol ) return false
585-
586582 const declarations = symbol . getDeclarations ( )
587583
588584 return declarations . some (
@@ -599,13 +595,11 @@ function getBaseInterfaces(
599595) : OpenAPIV3 . ReferenceObject [ ] {
600596 const symbol = type . getSymbol ( )
601597 if ( ! symbol ) return [ ]
602-
603598 const baseRefs : OpenAPIV3 . ReferenceObject [ ] = [ ]
604599 const declarations = symbol . getDeclarations ( )
605600
606601 for ( const decl of declarations ) {
607602 if ( ! Node . isInterfaceDeclaration ( decl ) ) continue
608-
609603 const extendsExpressions = decl . getExtends ( )
610604 for ( const extendsExpr of extendsExpressions ) {
611605 const baseType = extendsExpr . getType ( )
@@ -631,17 +625,14 @@ function getOwnInterfaceProperties(
631625 // Fallback to normal property resolution
632626 return resolveProperties ( type , spec )
633627 }
634-
635628 const declarations = symbol . getDeclarations ( )
636629 const interfaceDecl = declarations . find ( decl =>
637630 Node . isInterfaceDeclaration ( decl )
638631 )
639-
640632 if ( ! interfaceDecl ) {
641633 // Fallback to normal property resolution
642634 return resolveProperties ( type , spec )
643635 }
644-
645636 const result : ResolvePropertiesReturnType = {
646637 properties : { } ,
647638 required : [ ]
@@ -675,7 +666,6 @@ function getOwnInterfaceProperties(
675666 // Handle JSDoc tags
676667 const jsDocTags = propSig . getSymbol ( ) ?. compilerSymbol . getJsDocTags ( ) ?? [ ]
677668 appendJsDocTags ( jsDocTags , resolvedType )
678-
679669 // Add to properties
680670 if (
681671 ! ( 'type' in resolvedType && ( resolvedType . type as any ) === 'undefined' )
@@ -686,14 +676,11 @@ function getOwnInterfaceProperties(
686676 }
687677 }
688678 }
689-
690679 // Handle method signatures (but ignore them like in resolveProperties)
691680 // Methods are already filtered out by only looking at property signatures
692-
693681 if ( result . required ! . length === 0 ) {
694682 delete result . required
695683 }
696-
697684 return result
698685}
699686
@@ -719,31 +706,26 @@ function resolveObjectType(
719706 | MethodSignature
720707 return resolve ( node . getReturnType ( ) , spec )
721708 }
722-
723709 // Check for interface inheritance
724710 if ( ! hasInterfaceInheritance ( type ) ) {
725711 return {
726712 type : 'object' ,
727713 ...resolveProperties ( type , spec )
728714 }
729715 }
730-
731716 const baseRefs = getBaseInterfaces ( type , spec )
732-
733717 // If there are no base interfaces, fall back to normal resolution
734718 if ( baseRefs . length === 0 ) {
735719 return {
736720 type : 'object' ,
737721 ...resolveProperties ( type , spec )
738722 }
739723 }
740-
741724 // Shallow copy to avoid mutating the original baseRefs array
742725 const allOfElements = Array . from <
743726 OpenAPIV3 . ReferenceObject | OpenAPIV3 . SchemaObject
744727 > ( baseRefs )
745728 const ownProps = getOwnInterfaceProperties ( type , spec )
746-
747729 // Add own properties if any exist
748730 if (
749731 Object . keys ( ownProps . properties ) . length > 0 ||
@@ -754,7 +736,6 @@ function resolveObjectType(
754736 ...ownProps
755737 } )
756738 }
757-
758739 return {
759740 allOf : allOfElements
760741 }
@@ -823,7 +804,6 @@ export function appendJsDocTags(
823804 if ( ! supportedTags . includes ( tag . name ) || ! tag . text ) {
824805 continue
825806 }
826-
827807 const textValue = tag . text . map ( t => t . text ) . join ( '\n' )
828808 const value = numericTags . includes ( tag . name )
829809 ? parseFloat ( textValue )
@@ -845,7 +825,6 @@ export function appendInitializer(
845825 // Default value
846826 const initializer = node . getInitializer ( )
847827 const initializerType = initializer ?. getType ( )
848-
849828 if ( initializerType ?. isLiteral ( ) ) {
850829 const initializerLiteralType = initializerType . compilerType as
851830 | ts . StringLiteralType
0 commit comments