Type Annotations¶
# Simple types
x: int = 42
name: str = "Alice"
flag: bool = True
pi: float = 3.14159
# Type inference (annotation optional when initializer present)
y = 42 # Inferred as int
pi = 3.14159 # Inferred as float
Implementation - ✅ Native - Direct mapping to C# type declarations.
Nullability and Optional Type Syntax¶
T # Non-nullable type
T? # Optional[T] — safe tagged union (Sharpy-native)
T | None # C# nullable — .NET interop only
T !E # Result[T, E] — for return type annotations
T?is syntactic sugar forOptional[T]. See Optional Type.T | Noneis the only valid inline union form. No free unions likeint | str. See Nullable Types.T !Eis syntactic sugar forResult[T, E], recommended for top-level return types. See Result Type.
None in Annotations¶
None is a value, not a type. It appears in an annotation only as a return type: -> None says a function returns no value, and that includes the return slot of a function type, (str) -> None. Any other annotation — a local, parameter, field, module variable, constant, type argument (list[None]), a function type's parameter, or None? — could only ever hold None, so it is refused with SPY0614. A slot that may hold None is spelled T | None (or T? for an Optional), and object holds any value.
def log(message: str) -> None:
print(message)
def main() -> None:
handler: (str) -> None = log
count: int | None = None
handler("count is " + str(count))
Output: count is None.
A modifier over None wraps nothing, so None?, None | None and None !E are refused in every position, a return type included: -> None? is SPY0614, and a function that returns no value says -> None. int | None is unaffected — its non-None side is a type.
A property is a value, so a property getter's -> None is a value position and is refused too, while a setter's -> None is a true return. The refusal is the annotation's only diagnostic: x: None = None reports SPY0614 alone, with no SPY0227 "cannot infer a type" for its initializer (an annotation that failed to resolve is error recovery, not a missing annotation).
def describe(x: None) -> str: # SPY0614: 'None' is not a type; annotate ... as `T | None`, or use `object`
return "nothing"
class Box:
property get p(self) -> None: # SPY0614: 'None' is not a type (a getter's return is the property's type)
pass
Implementation
- 🔄 Lowered - -> None is C# void. A value-position None has no C# spelling (void is not a value type), so it is refused at semantic analysis rather than reaching the C# compiler.
Shorthand Syntax¶
Sharpy supports shorthand syntax for common collection types. See Type Annotation Shorthand for details.