Truthiness check narrows NewType(float) to TFloat & ~AlwaysFalsy, which is rejected as float (original) (raw)

Summary

This looks like a truthiness-narrowing issue involving NewType(float). This behavior is not observed in NewType(str).

Minimal reproduction (playground link):

from typing import NewType, reveal_type

TFloat = NewType("TFloat", float)

def takes_float(x: float) -> None: ...

t = TFloat(1.0)

takes_float(t) # OK

if t: reveal_type(t) # TFloat & ~AlwaysFalsy takes_float(t) # error: Expected int | float, found TFloat & ~AlwaysFalsy

Motivating example (Optional case + comparison with float | None):

from typing import NewType, reveal_type

TFloat = NewType("TFloat", float)

def takes_float(x: float) -> None: ...

def optional_tfloat() -> TFloat | None: ... def optional_float() -> float | None: ...

t = optional_tfloat() if t: reveal_type(t) # TFloat & ~AlwaysFalsy takes_float(t) # error

f = optional_float() if f: reveal_type(f) # (int & ~AlwaysFalsy) | (float & ~AlwaysFalsy) takes_float(f) # OK

Version

ty 0.0.20