Union Types¶
Incan supports anonymous closed union types for values that may be one of several unrelated types.
def parse_value(flag: bool) -> int | str:
if flag:
return 42
return "fallback"
Union[A, B, ...] is the canonical spelling for ordinary unions. A | B is equivalent syntax in type positions, and nested unions, duplicate members, and member ordering normalize to the same semantic type.
When None appears in a union, the type canonicalizes through Option[...]:
str | None # Option[str]
int | str | None # Option[Union[int, str]]
Concrete member values are assignable to a union that contains that member. A source union is assignable to a target union when every source member is accepted by some target member.
Union values do not expose member-specific methods or operators until narrowed. Use isinstance(value, T) or a type pattern in match:
def normalize(value: int | str) -> str:
if isinstance(value, str):
return value.upper()
return "number"
def describe(value: int | str) -> str:
match value:
int(n) =>
return str(n)
str(s) =>
return s.upper()
match over a union must cover every member type or include _.
For unions that canonicalize through Option[...], use is None or is not None to narrow the optional value:
def label(value: str | None) -> str:
if value is not None:
return value.upper()
return "missing"
Current implementation note: ordinary unions support return/assignment/call-argument wrapping, isinstance narrowing for true branches, else branches, wider unions, and chained elif branches, and exhaustive match type patterns. Unions containing None continue to use the existing Option[...] representation and narrow through is None, is not None, isinstance, and match.