Question: Sometimes at the beginning of my Python functions I check whether the correct variables types were used, or whether something was passed as None. For example: def fails_with_none(x: int): if x is None: raise TypeError('Function fails with None!') return x + 1  I am hesitating whether x should be typed as int or as Optional[int]. The reason for using just int is that, semantically, the function requires an int. However, if I think about this from a programming perspective, the function handles both integer and None inputs. Is there a recommended way? For example, according to this answer the Optional hint means "either an object of the specific type is required, or None is required". However, the question still remains: Required by what? If we take it to mean "required by the logic of the function", then it should be typed as int. If we take it to mean "required by the code being executed", then since we check whether x is None, it should be included as a possible type hint.

Answer: TL;DR: If x being None has meaning in your function, then annotate x accordingly. Otherwise don't and don't check it.   Sometimes at the beginning of my python functions I check whether the correct variables types were used, or whether something was passed as None.  I am not saying this is absolutely wrong, but there is no doubt that this goes hardcore against the philosophy of Python. A main pillar of the paradigm of the language is dynamic typing. This is also why type annotations in Python are commonly referred to as hints. This is to signal that the language has no built in mechanism for enforcing this and that by convention functions do not force type conformity on users. The contract is this: "Hey user, my function f accepts an argument of type int. That is how I designed it. You may try and use it differently, but at your own peril. I don't vouch for it working as intended, if you pass anything other than int to it." If you insist on enforcing types, then the only consistent way IMHO is to do a negative check against the type you annotated with. If you have f(x: int), then it would be consistent to check like this: def f(x: int) -> None: if not isinstance(x, int): raise TypeError print(x**2)  Because the alternative is arbitrarily checking against any other type that x might also be. Specifically, why would you check for it being None? Why not also check against it being a str? Or an empty tuple? Or literally anything else? Since you provided no additional context, I have to assume from your example that None has no special semantic meaning in your function other than that it triggers the TypeError.   I am hesitating whether x should be typed as int or as Optional[int]. The reason for using just int is that, semantically, the function requires an int.  There is your answer then. Semantics is king. If None has no other meaning in your function, then you should completely ignore its existence. It's up to the user of the function to adhere to your type annotations or disregard them at his own peril. In this concrete case, this would not even be particularly useful because the default built-in Python error for misusing numeric operands is this: TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'  So you don't even change the error type, just the message in this particular case.  Don't get me wrong, there are obviously cases, where passing None as an argument is meaningful. If an argument being None has meaning in a function, then obviously you should type it accordingly. Here is a crude example: def f(x: int | None) -> None: if x is None: print("Ok then.") else: print(f"{x**2=}")  In this case the function behaves differently depending on whether you pass it an int or a NoneType. Thus, a union of those is the proper type annotation for x. And Optional[int] is just a (arguably poorly named) equivalent of that union.  Thus, to me, your question title is a non-starter:  Should I type something as Optional if None breaks the logic of the function, but I do check for it inside the body?  If you check for None, then None does not break the logic of your function; it is specifically part of the logic and thus should be accounted for in the parameter type annotation. The bigger question (as I laid out above) is, whether you should check for it. If you only check, so you can yell at the programmer in your own customized way for using None, I don't see the value in that.