Skip to content

Pyright type error in transpiled Json.fs: getCases returns Any | list[Unknown] instead of Array[str] #278

@dbrattli

Description

@dbrattli

Summary

When src/stdlib/Json.fs is transpiled to Python and consumed by a project that runs pyright in standard mode, a type error is reported in the generated json.py:

json.py:29:37 - error: Type "Any | list[Unknown]" is not assignable to declared type "Array"
  "list[Unknown]" is not assignable to "FSharpArray[str]" (reportAssignmentType)

The offending generated line:

cases: Array[str] = getattr(type(o), 'cases', lambda: [])()

This surfaced when bumping Fable.Python from 4.24.0 to 5.0.0 in the Fable repo's Python test project (fable-compiler/Fable#4571 — pyright CI job).

Root cause

In src/stdlib/Json.fs (around line 90):

[<Emit("getattr(type($0), 'cases', lambda: [])()")>]
let private getCases (o: obj) : string array = nativeOnly

The F# return type string array becomes Array[str] (FSharpArray) in the generated Python annotation. But the emit's default branch (lambda: []) returns a plain Python list, not an FSharpArray — so pyright infers the expression's type as Any | list[Unknown], which doesn't satisfy Array[str].

The Fable.Python.Json module is new in v5.0.0 (introduced by #175, expanded in #182 and #184), so this error didn't exist on 4.24.0.

Reproduction

  1. In a project referencing Fable.Python 5.0.0, transpile any F# file that pulls in Fable.Python.Json.
  2. Run pyright (standard mode, default settings) over the resulting fable_modules/fable_python/stdlib/json.py.
  3. Observe the error above on line 29.

Suggested directions

getCases is only used inside fableDefault to read cases.Length and cases.[tag], so the implementation has flexibility. Some options:

  1. Replace the Emit with plain F# using the existing private hasattr / getattr helpers to check for cases on type(o) and call it — returning [||] when absent. This keeps the F# type honest and lets Fable generate well-typed Python.
  2. Change the return type to something the emit actually produces (e.g. a seq) and adapt call sites.
  3. Inline the lookup at the call site so no separate helper is needed.

# type: ignore / cast(Any, ...) / excluding the file are not great options — they hide a real type mismatch.

Workaround for downstream users

Add **/fable_modules/fable_python/stdlib/json.py to the exclude list in the consuming project's pyrightconfig.json until a fix is released.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions