@@ -663,6 +663,7 @@ export class TypeProcessor {
663663 * @private
664664 */
665665 visitType ( type , node ) {
666+ const typeArguments = this . getTypeArguments ( type ) ;
666667 if ( this . checker . isArrayType ( type ) ) {
667668 const typeArgs = this . checker . getTypeArguments ( type ) ;
668669 if ( typeArgs && typeArgs . length > 0 ) {
@@ -672,6 +673,11 @@ export class TypeProcessor {
672673 return "[JSObject]" ;
673674 }
674675
676+ const recordType = this . convertRecordType ( type , typeArguments , node ) ;
677+ if ( recordType ) {
678+ return recordType ;
679+ }
680+
675681 // Treat A<B> and A<C> as the same type
676682 if ( isTypeReference ( type ) ) {
677683 type = type . target ;
@@ -760,6 +766,54 @@ export class TypeProcessor {
760766 return swiftType ;
761767 }
762768
769+ /**
770+ * Convert a `Record<string, T>` TypeScript type into a Swift dictionary type.
771+ * Falls back to `JSObject` when keys are not string-compatible or type arguments are missing.
772+ * @param {ts.Type } type
773+ * @param {ts.Type[] } typeArguments
774+ * @param {ts.Node } node
775+ * @returns {string | null }
776+ * @private
777+ */
778+ convertRecordType ( type , typeArguments , node ) {
779+ const symbol = type . aliasSymbol ?? type . getSymbol ( ) ;
780+ if ( ! symbol || symbol . name !== "Record" ) {
781+ return null ;
782+ }
783+ if ( typeArguments . length !== 2 ) {
784+ this . diagnosticEngine . print ( "warning" , "Record expects two type arguments" , node ) ;
785+ return "JSObject" ;
786+ }
787+ const [ keyType , valueType ] = typeArguments ;
788+ const stringType = this . checker . getStringType ( ) ;
789+ if ( ! this . checker . isTypeAssignableTo ( keyType , stringType ) ) {
790+ this . diagnosticEngine . print (
791+ "warning" ,
792+ `Record key type must be assignable to string: ${ this . checker . typeToString ( keyType ) } ` ,
793+ node
794+ ) ;
795+ return "JSObject" ;
796+ }
797+
798+ const valueSwiftType = this . visitType ( valueType , node ) ;
799+ return `[String: ${ valueSwiftType } ]` ;
800+ }
801+
802+ /**
803+ * Retrieve type arguments for a given type, including type alias instantiations.
804+ * @param {ts.Type } type
805+ * @returns {ts.Type[] }
806+ * @private
807+ */
808+ getTypeArguments ( type ) {
809+ if ( isTypeReference ( type ) ) {
810+ return this . checker . getTypeArguments ( type ) ;
811+ }
812+ // Non-TypeReference alias instantiations store type arguments separately.
813+ // @ts -ignore: `aliasTypeArguments` is intentionally accessed for alias instantiations.
814+ return type . aliasTypeArguments ?? [ ] ;
815+ }
816+
763817 /**
764818 * Derive the type name from a type
765819 * @param {ts.Type } type - TypeScript type
0 commit comments