@@ -71,6 +71,62 @@ let getGenericArgs (typ: Fable.Type) : Fable.Type list =
7171let containsGenericParams ( t : Fable.Type ) =
7272 FSharp2Fable.Util.getGenParamNames [ t ] |> List.isEmpty |> not
7373
74+ /// Check if a type is a callable type (Lambda or Delegate)
75+ let isCallableType ( t : Fable.Type ) =
76+ match t with
77+ | Fable.LambdaType _
78+ | Fable.DelegateType _ -> true
79+ | _ -> false
80+
81+ /// Get the final (non-callable) return type from a nested callable type.
82+ /// For A -> B -> C -> int, returns int.
83+ let rec getFinalReturnType ( t : Fable.Type ) =
84+ match t with
85+ | Fable.LambdaType(_, returnType) -> getFinalReturnType returnType
86+ | Fable.DelegateType(_, returnType) -> getFinalReturnType returnType
87+ | _ -> t
88+
89+ /// Get the immediate return type of a callable (one level deep).
90+ let getImmediateReturnType ( t : Fable.Type ) =
91+ match t with
92+ | Fable.LambdaType(_, returnType) -> returnType
93+ | Fable.DelegateType(_, returnType) -> returnType
94+ | _ -> t
95+
96+ /// Generate type annotation for a callable (lambda) type.
97+ /// For nested callables:
98+ /// - If returned callable returns another callable: Callable[ ..., Any]
99+ /// - If returned callable returns concrete type: Callable[ ..., Callable[ ..., ConcreteType]]
100+ /// For simple callables (depth 1), preserves full type information.
101+ let makeLambdaTypeAnnotation
102+ ( com : IPythonCompiler )
103+ ctx
104+ ( repeatedGenerics : Set < string > option )
105+ ( argType : Fable.Type )
106+ ( returnType : Fable.Type )
107+ : Expression * Statement list
108+ =
109+ if isCallableType returnType then
110+ // Check if the returned callable also returns a callable
111+ let innerReturnType = getImmediateReturnType returnType
112+
113+ if isCallableType innerReturnType then
114+ // Deeply nested: Callable[..., Any]
115+ let any , stmts = stdlibModuleTypeHint com ctx " typing" " Any" [] repeatedGenerics
116+ stdlibModuleAnnotation com ctx " collections.abc" " Callable" [ Expression.ellipsis; any ], stmts
117+ else
118+ // Returned callable returns concrete type: Callable[..., Callable[..., ConcreteType]]
119+ let concreteReturnExpr , stmts =
120+ typeAnnotation com ctx repeatedGenerics innerReturnType
121+
122+ let innerCallable =
123+ stdlibModuleAnnotation com ctx " collections.abc" " Callable" [ Expression.ellipsis; concreteReturnExpr ]
124+
125+ stdlibModuleAnnotation com ctx " collections.abc" " Callable" [ Expression.ellipsis; innerCallable ], stmts
126+ else
127+ // Simple case: Callable[[A], B] where B is not a callable - preserve full types
128+ stdlibModuleTypeHint com ctx " collections.abc" " Callable" [ argType; returnType ] repeatedGenerics
129+
74130let getEntityGenParams ( ent : Fable.Entity ) =
75131 ent.GenericParameters |> Seq.map ( fun x -> x.Name) |> Set.ofSeq
76132
@@ -88,9 +144,31 @@ let makeMemberTypeParams (com: IPythonCompiler) ctx (genParams: Fable.GenericPar
88144 else
89145 []
90146
147+ /// Try to convert a generic constraint type to its non-generic base type.
148+ /// Python 3.12+ TypeVar bounds cannot use parameterized generic types,
149+ /// so we map e.g., IEnumerable<'T> to IEnumerable (non-generic).
150+ let private tryGetNonGenericBase ( target : Fable.Type ) : Fable.Type option =
151+ match target with
152+ | Fable.DeclaredType( entRef, _ genArgs) ->
153+ match entRef.FullName with
154+ // IEnumerable<T> -> IEnumerable (non-generic)
155+ | Types.ienumerableGeneric ->
156+ let nonGenericRef : Fable.EntityRef =
157+ {
158+ FullName = Types.ienumerable
159+ Path = Fable.CoreAssemblyName " System.Runtime"
160+ }
161+
162+ Some( Fable.DeclaredType( nonGenericRef, []))
163+ // Add other mappings here as needed:
164+ // Types.icomparableGeneric -> Types.icomparable, etc.
165+ | _ -> None
166+ | _ -> None
167+
91168/// Extract bound type from CoercesTo constraint if present.
92169/// Returns the first CoercesTo constraint target type, or None if no such constraint exists.
93- /// Only returns non-generic bounds since Python 3.12+ TypeVar bounds cannot be parameterized.
170+ /// For bounds with generic parameters, attempts to use a non-generic base type instead,
171+ /// since Python 3.12+ TypeVar bounds cannot be parameterized.
94172let tryGetCoercesToBound ( constraints : Fable.Constraint list ) : Fable.Type option =
95173 constraints
96174 |> List.tryPick (
@@ -99,7 +177,8 @@ let tryGetCoercesToBound (constraints: Fable.Constraint list) : Fable.Type optio
99177 // Python 3.12+ doesn't support parameterized generic types as bounds
100178 // e.g., T: IEnumerable[U] is invalid, only T: SomeNonGenericType works
101179 if containsGenericParams target then
102- None
180+ // Try to use a non-generic base type instead
181+ tryGetNonGenericBase target
103182 else
104183 Some target
105184 | _ -> None
@@ -305,10 +384,7 @@ let typeAnnotation
305384 | Fable.Char -> Expression.name " str" , []
306385 | Fable.String -> Expression.name " str" , []
307386 | Fable.Number( kind, info) -> makeNumberTypeAnnotation com ctx kind info
308- | Fable.LambdaType( argType, returnType) ->
309- // Keep curried structure: A -> (B -> C) becomes Callable[[A], Callable[[B], C]]
310- // This matches the actual runtime code which generates nested lambdas
311- stdlibModuleTypeHint com ctx " collections.abc" " Callable" [ argType; returnType ] repeatedGenerics
387+ | Fable.LambdaType( argType, returnType) -> makeLambdaTypeAnnotation com ctx repeatedGenerics argType returnType
312388 | Fable.DelegateType( argTypes, returnType) ->
313389 stdlibModuleTypeHint com ctx " collections.abc" " Callable" ( argTypes @ [ returnType ]) repeatedGenerics
314390 | Fable.Nullable( genArg, isStruct) ->
@@ -545,13 +621,8 @@ let makeBuiltinTypeAnnotation com ctx typ repeatedGenerics kind =
545621 match kind with
546622 | Replacements.Util.BclGuid -> stdlibModuleTypeHint com ctx " uuid" " UUID" [] repeatedGenerics
547623 | Replacements.Util.FSharpReference genArg ->
548- // For inref types (like struct instance member's 'this' parameter),
549- // use the inner type directly since Python doesn't wrap them in FSharpRef
550- if isInRefOrAnyType com typ then
551- typeAnnotation com ctx repeatedGenerics genArg
552- else
553- let resolved , stmts = resolveGenerics com ctx [ genArg ] repeatedGenerics
554- fableModuleAnnotation com ctx " types" " FSharpRef" resolved, stmts
624+ let resolved , stmts = resolveGenerics com ctx [ genArg ] repeatedGenerics
625+ fableModuleAnnotation com ctx " types" " FSharpRef" resolved, stmts
555626 (*
556627 | Replacements.Util.BclTimeSpan -> NumberTypeAnnotation
557628 | Replacements.Util.BclDateTime -> makeSimpleTypeAnnotation com ctx "Date"
@@ -574,6 +645,10 @@ let makeBuiltinTypeAnnotation com ctx typ repeatedGenerics kind =
574645 let resolved , stmts = resolveGenerics com ctx [ ok; err ] repeatedGenerics
575646
576647 fableModuleAnnotation com ctx " result" " FSharpResult_2" resolved, stmts
648+ | Replacements.Util.FSharpChoice genArgs ->
649+ let resolved , stmts = resolveGenerics com ctx genArgs repeatedGenerics
650+ let name = $" FSharpChoice_%d {List.length genArgs}"
651+ fableModuleAnnotation com ctx " choice" name resolved, stmts
577652 | _ -> stdlibModuleTypeHint com ctx " typing" " Any" [] repeatedGenerics
578653
579654let transformFunctionWithAnnotations
0 commit comments