Skip to main content

Pyright Strict Mode in Pyrefly

Pyright strict mode and Pyrefly strict mode are both useful policy bundles, but they do not enable the same checks. Treat "strict" as a name for two different policies rather than a setting to copy across.

Global strict mode

Pyright:

{
"typeCheckingMode": "strict"
}

Pyrefly starting point:

preset = "strict"
infer-with-first-use = false

infer-with-first-use = false keeps empty-container and unsolved-type-variable inference closer to Pyright; it is also what pyrefly init writes when it converts a Pyright config. Then review your explicit report* overrides using the diagnostic mapping.

Why the presets differ

Pyright strict mode enables its own collection of report* rules, including several linter-like checks. Pyrefly's strict preset instead sets strict-callable-subtyping = true and enables these error kinds:

Keep Ruff or another linter for the Pyright rules about unused imports, duplicate imports, implicit string concatenation, mutable defaults, and naming. Pyrefly does not try to absorb them.

Path-specific strict mode

Pyright:

{
"strict": ["src/core"]
}

Pyrefly cannot put preset = "strict" inside a [[sub-config]], so encode the policies you care about explicitly:

[[sub-config]]
matches = "src/core/**"

[sub-config.errors]
implicit-any = "error"
missing-override-decorator = "error"
unused-ignore = "error"
potential-bad-keyword-argument = "error"

The result is an explicit policy you can extend after comparing both tools, rather than a recreation of Pyright strict mode. If a subtree also needs a different Python version, platform, or import roots, give it its own configuration file instead.

Broad Pyright rules still need review

Strict mode turns on broad rules such as reportCallIssue, reportAttributeAccessIssue, and reportGeneralTypeIssues. Pyrefly splits the first two into many specific kinds and has no single equivalent for the last one, so review the generated [errors] table rather than trusting the word "strict".