Skip to content

Generated Types

This page documents what type definitions in TypeSpec are generated as in emitted libraries

The namespace for models, enums, and unions will follow the namespace they belong to. You can use @clientNamespace to override it if needed.

namespace Service;
model Foo {
prop: string;
}
from service.models import Foo
foo = Foo(prop="hello")
namespace Service;
namespace Container {
model Foo {
prop: string;
}
}
from service.container.models import Foo
foo = Foo(prop="hello")
namespace Service;
@clientNamespace("Service.Container")
model Foo {
prop: string;
}
from service.container.models import Foo
foo = Foo(prop="hello")

The @clientDoc decorator lets you add documentation that will appear in generated client libraries. This is useful for providing additional context, examples, or implementation notes that are only relevant for SDK consumers.

@doc("This internal model is the base class for shapes")
@clientDoc(
"Base class for all shape objects that can be drawn on a canvas",
DocumentationMode.replace
)
model Shape {
@doc("The coordinates of the shape")
@clientDoc(
"The x,y coordinates where the shape will be positioned on the canvas",
DocumentationMode.replace
)
position: Point;
}
@doc("A two-dimensional point")
@clientDoc("Represents a position in a 2D coordinate system", DocumentationMode.replace)
model Point {
@doc("X coordinate")
@clientDoc("The horizontal position (increases moving right)", DocumentationMode.replace)
x: float32;
@doc("Y coordinate")
@clientDoc("The vertical position (increases moving down)", DocumentationMode.replace)
y: float32;
}
class Point(_model_base.Model):
"""Represents a position in a 2D coordinate system.
:param x: The horizontal position (increases moving right)
:type x: float
:param y: The vertical position (increases moving down)
:type y: float
"""
x: float = rest_field()
"""The horizontal position (increases moving right)."""
y: float = rest_field()
"""The vertical position (increases moving down)."""
class Shape(_model_base.Model):
"""Base class for all shape objects that can be drawn on a canvas.
:param position: The x,y coordinates where the shape will be positioned on the canvas
:type position: Point
"""
position: Point = rest_field()
"""The x,y coordinates where the shape will be positioned on the canvas."""

The @clientDoc decorator can be used on everything that the @doc decorator can be applied to.

When both @doc and @clientDoc are present, the generated code outputs the final documentation depending on the mode that you called @clientDoc with. If called with replace, @clientDoc replaces @doc completely. If called with append, @clientDoc is appended onto @doc.

model Properties {
name: string;
}
model Foo {
@flattenProperty
prop: Properties;
}

Python will do dynamic flattening, exposing the non-flattening syntax, and dynamically accepting the flattened access.

class Properties(_model_base.Model):
name: str = rest_field()
"""Required."""
class Foo(_model_base.Model):
properties: "_models.Properties" = rest_field()
"""Required."""
__flattened_items = ["properties"]
print(f.properties.name) # Non-flattened access is preferred experience
print(f.name) # Flattened access is dynamically supported, but not documented

Recommend usage:

model Animal {
name: string;
kind: string;
...Record<unknown>;
}

Other usages:

model Animal extends Record<unknown> {
name: string;
kind: string;
}
model Animal is Record<unknown> {
name: string;
kind: string;
}

Python models are designed to support adding any additional properties.

from .. import _model_base
from .._model_base import rest_field
class Animal(_model_base.Model):
name: str = rest_field()
kind: str = rest_field()
animal = Animal(name="Tom", kind="Cat")
animal["friend"] = "Jerry"
animal["age"] = 5
model AnimalProperty {
category: string;
value: unknown;
}
model Animal {
name: string;
kind: string;
...Record<AnimalProperty>;
}

Python models are designed to support adding any additional properties.

from typing import Any
from .. import _model_base
from .._model_base import rest_field
class Animal(_model_base.Model):
name: str = rest_field()
kind: str = rest_field()
class AnimalProperty(_model_base.Model):
category: str = rest_field()
value: Any = rest_field()
animal = Animal(name="Tom", kind="Cat")
animal["friend"] = AnimalProperty(category="relationship", value="Jerry")
animal["age"] = AnimalProperty(category="attribute", value=5)
model Animal {
name: string;
kind: string;
...Record<string>;
...Record<int32>;
}

Python models are designed to support adding any additional properties.

from .. import _model_base
from .._model_base import rest_field
class Animal(_model_base.Model):
name: str = rest_field()
kind: str = rest_field()
animal = Animal(name="Tom", kind="Cat")
animal["friend"] = "Jerry"
animal["age"] = 5
model Animal {
name: string;
kind: string;
...Record<string | null>;
}

Python models are designed to support adding any additional properties.

from .. import _model_base
from .._model_base import rest_field
class Animal(_model_base.Model):
name: str = rest_field()
kind: str = rest_field()
animal = Animal(name="Tom", kind="Cat")
animal["friend"] = "Jerry"
animal["alert"] = None

TypeSpec uses @discriminator decorator to add a discriminator to a model.

Client emitters now only support a single way to represent a discriminated set in TypeSpec.

  1. Use model
@discriminator("kind")
model Cat {
kind: string;
}
model Siamese extends Cat {
kind: "siamese";
}
model Ragdoll extends Cat {
kind: "ragdoll";
}

The type of the discriminator property could be an enum (extensible or fixed):

@discriminator("kind")
model Cat {
kind: CatKind;
}
union CatKind {
string,
Siamese: "siamese",
Ragdoll: "ragdoll",
}
model Siamese extends Cat {
kind: CatKind.Siamese;
}
model Ragdoll extends Cat {
kind: CatKind.Ragdoll;
}
from .. import _model_base
from .._model_base import rest_discriminator, rest_field
class Cat(_model_base.Model):
kind: str = rest_discriminator(name="kind")
class Siamese(Cat):
kind: Literal["siamese"] = rest_discriminator(name="kind")
class Ragdoll(Cat):
kind: Literal["ragdoll"] = rest_discriminator(name="kind")

TypeSpec uses | null to represent nullable types. Nullability is handled differently in languages, but emitter authors will find information about nullability by inspecting the type of a property.

model Foo {
basicNullableProperty: string | null;
modelNullableProperty: Bar | null;
enumNullableProperty: LR | null;
}
model Bar {
prop: string;
}
enum LR {
left,
right,
}

Python treat nullable as optional. If you actually want to send the value null to the service without the property being ignored, you can send in corehttp.serialization.NULL. Python does not restrict you from setting any property to this value.

from enum import Enum
from corehttp.utils import CaseInsensitiveEnumMeta
class Bar(_model_base.Model):
prop: Optional[str] = rest_field()
class LR(str, Enum, metaclass=CaseInsensitiveEnumMeta):
LEFT = "left"
RIGHT = "right"
class Foo(_model_base.Model):
basicNullableProperty: Optional[str] = rest_field()
modelNullableProperty: Optional["_models.Bar"] = rest_field()
enumNullableProperty: Optional["LR"] = rest_field()

All emitters will generate their version of a closed enum.

union LR {
left: "left",
right: "right",
}

Python never generates closed enum by design. We will always permit users to pass in additional values.

from enum import Enum
from corehttp.utils import CaseInsensitiveEnumMeta
class LR(str, Enum, metaclass=CaseInsensitiveEnumMeta):
LEFT = "left"
RIGHT = "right"

This is union defined inline at point of usage.

model Widget {
horizontal: "left" | "right";
}

Python generates this as a union of literals, not as enum. We also don’t generate a closed set of literals.

from typing import Literal, Union
model Widget:
horizontal: Union[Literal["left"] | Literal["right"] | str]

Each language will generate their version of an open enum.

union Colors {
string,
red: "red",
blue: "blue",
}

Python generates open enum again here.

from enum import Enum
from corehttp.utils import CaseInsensitiveEnumMeta
class Colors(str, Enum, metaclass=CaseInsensitiveEnumMeta):
RED = "red"
BLUE = "blue"

This is union defined inline at point of usage which include the base type as an option.

model Widget {
color: "red" | "blue" | string;
}

Python generates a union of literals again.

from typing import Literal, Union
model Widget:
color: Union[Literal["red"] | Literal["blue"] | str]

Union of other union/enum, basic type and literals of that type

Section titled “Union of other union/enum, basic type and literals of that type”
import "@azure-tools/typespec-azure-resource-manager";
union ProvisioningState {
string,
"InProgress",
Azure.ResourceManager.ResourceProvisioningState,
}

Python generates a single open enum.

from enum import Enum
from corehttp.utils import CaseInsensitiveEnumMeta
class ProvisioningState(str, Enum, metaclass=CaseInsensitiveEnumMeta):
INPROGRESS = "InProgress"
SUCCEEDED = "Succeeded"
FAILED = "Failed"
CANCELED = "Canceled"
union LR {
left: "left",
right: "right",
}
union UD {
up: "up",
down: "down",
}
union Orientation {
LR,
UD,
}
from enum import Enum
from corehttp.utils import CaseInsensitiveEnumMeta
class Orientation(str, Enum, metaclass=CaseInsensitiveEnumMeta):
LEFT = "left"
RIGHT = "right"
UP = "up"
DOWN = "down"
union LR {
left: "left",
right: "right",
}
union UD {
up: "up",
down: "down",
}
model Widget {
orientation: LR | UD;
}

Since this is inline, Python will generate this as a single union of all possible literal values.

from typing import Literal
type WidgetOrientation = "left" | "right" | "up" | "down" | str
model Widget:
orientation: WidgetOrientation

Standard enums will be generated as closed enums.

enum LR {
left,
right,
}

Python never generates closed enums by design. We will always permit users to pass in additional values.

from enum import Enum
from corehttp.utils import CaseInsensitiveEnumMeta
class LR(str, Enum, metaclass=CaseInsensitiveEnumMeta):
LEFT = "left"
RIGHT = "right"
@versioned(Versions)
@service
namespace Service;
enum Versions {
v1,
v2,
}
# Python does not generate the enum used for versioning

Spreading enums will return the resultant enum as a new single closed enum.

enum LR {
left,
right,
}
enum UD {
up,
down,
}
enum Orientation {
...LR,
...UD,
}

Python generates one open enum, because Python never generates an enum as fully closed.

from enum import Enum
from corehttp.utils import CaseInsensitiveEnumMeta
class Orientation(str, Enum, metaclass=CaseInsensitiveEnumMeta):
LEFT = "left"
RIGHT = "right"
UP = "up"
DOWN = "down"

We will take the @encode decorator into account, determining how we serialize inputted scalars to send over the wire.

model Test {
@encode(DateTimeKnownEncoding.rfc3339)
prop: utcDateTime;
}
serialized_prop = json.dumps(prop, cls=SdkJSONEncoder, format="rfc3339")

When you specify an encoding type, say that you want to encode an integer as a string, that will also be represented in our generated SDKs.

model Test {
@encode(string)
prop: int64;
}
serialized_prop = json.dumps(prop, cls=SdkJSONEncoder, format="string")