@@ -389,7 +389,8 @@ def {_field_name}(
389389 type = original_type
390390 any_of : list [TypeExpression ] = []
391391
392- typeddict_encoder = []
392+ # Collect (type_check, encoder_expr) pairs for building ternary chain
393+ encoder_parts : list [tuple [str | None , str ]] = []
393394 for i , t in enumerate (type .anyOf ):
394395 type_name , _ , contents , _ = encode_type (
395396 t ,
@@ -403,34 +404,63 @@ def {_field_name}(
403404 chunks .extend (contents )
404405 if isinstance (t , RiverConcreteType ):
405406 if t .type == "string" :
406- typeddict_encoder .extend (["x" , " if isinstance(x, str) else " ])
407- else :
408- # TODO(dstewart): This structure changed since we were incorrectly
409- # leaking ListTypeExprs into codegen. This generated
410- # code is probably wrong.
407+ encoder_parts .append (("isinstance(x, str)" , "x" ))
408+ elif t .type == "array" :
411409 match type_name :
412410 case ListTypeExpr (inner_type_name ):
413- typeddict_encoder .append (
414- f"encode_{ render_literal_type (inner_type_name )} (x)"
411+ # Primitives don't need encoding
412+ inner_type_str = render_literal_type (inner_type_name )
413+ if inner_type_str in ("str" , "int" , "float" , "bool" , "Any" ):
414+ encoder_parts .append (("isinstance(x, list)" , "list(x)" ))
415+ else :
416+ encoder_parts .append (
417+ (
418+ "isinstance(x, list)" ,
419+ f"[encode_{ inner_type_str } (y) for y in x]" ,
420+ )
421+ )
422+ case _:
423+ encoder_parts .append (("isinstance(x, list)" , "list(x)" ))
424+ elif t .type == "object" :
425+ match type_name :
426+ case TypeName (value ):
427+ encoder_parts .append (
428+ ("isinstance(x, dict)" , f"encode_{ value } (x)" )
415429 )
430+ case _:
431+ encoder_parts .append (("isinstance(x, dict)" , "dict(x)" ))
432+ elif t .type in ("number" , "integer" ):
433+ match type_name :
416434 case LiteralTypeExpr (const ):
417- typeddict_encoder .append (repr (const ))
435+ encoder_parts .append ((f"x == { repr (const )} " , repr (const )))
436+ case _:
437+ encoder_parts .append (("isinstance(x, (int, float))" , "x" ))
438+ elif t .type == "boolean" :
439+ encoder_parts .append (("isinstance(x, bool)" , "x" ))
440+ elif t .type == "null" or t .type == "undefined" :
441+ encoder_parts .append (("x is None" , "None" ))
442+ else :
443+ # Fallback for other types
444+ match type_name :
418445 case TypeName (value ):
419- typeddict_encoder .append (f"encode_{ value } (x)" )
446+ encoder_parts .append ((None , f"encode_{ value } (x)" ))
447+ case LiteralTypeExpr (const ):
448+ encoder_parts .append ((None , repr (const )))
420449 case NoneTypeExpr ():
421- typeddict_encoder .append ("None" )
422- case other :
423- _o2 : (
424- DictTypeExpr
425- | OpenUnionTypeExpr
426- | UnionTypeExpr
427- | LiteralType
428- ) = other
429- raise ValueError (
430- f"What does it mean to have {
431- render_type_expr (_o2 )
432- } here?"
433- )
450+ encoder_parts .append ((None , "None" ))
451+ case _:
452+ encoder_parts .append ((None , "x" ))
453+
454+ # Build the ternary chain from encoder_parts
455+ typeddict_encoder : list [str ] = []
456+ for i , (type_check , encoder_expr ) in enumerate (encoder_parts ):
457+ is_last = i == len (encoder_parts ) - 1
458+ if is_last or type_check is None :
459+ # Last item or no type check - just the expression
460+ typeddict_encoder .append (encoder_expr )
461+ else :
462+ # Add expression with type check
463+ typeddict_encoder .append (f"{ encoder_expr } if { type_check } else" )
434464 if permit_unknown_members :
435465 union = _make_open_union_type_expr (any_of )
436466 else :
0 commit comments