Most of the issues described there are solved for me by using pydantic: https://docs.pydantic.dev/latest/ (whose core has actually been recently rewritten in rust ^^).
To avoid switching parameters with the same type, I like to use * in my methods definitions instead of NewType to force naming parameters. Pydantic allows to validate methods parameters too (see https://docs.pydantic.dev/latest/usage/validation_decorator/) but this is at runtime and can add a performance overhead so it could be enough for the modules interfaces with the rest of the code only. For static checking, the NewType method is probably better, using a simple * avoided us many mistakes already.
To avoid switching parameters with the same type, I like to use * in my methods definitions instead of NewType to force naming parameters. Pydantic allows to validate methods parameters too (see https://docs.pydantic.dev/latest/usage/validation_decorator/) but this is at runtime and can add a performance overhead so it could be enough for the modules interfaces with the rest of the code only. For static checking, the NewType method is probably better, using a simple * avoided us many mistakes already.