Skip to content

Generated Types

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

Models

Flattening

model Properties {
name: string;
}
model Foo {
@flattenProperty
prop: Properties;
}
{
"kind": "model",
"name": "Foo",
"properties": [
{
"kind": "property",
"name": "prop",
"serializedName": "prop",
"flatten": true,
"optional": false,
"type": {
"kind": "model",
"name": "Properties",
"properties": [
{
"kind": "property",
"name": "name",
"serializedName": "name",
"flatten": false,
"optional": false,
"type": {
"kind": "string",
"encode": "string"
}
}
]
}
}
]
}

Models with additional properties

Additional properties of any type

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;
}
{
"kind": "model",
"name": "Animal",
"properties": [
{
"kind": "property",
"name": "name",
"serializedName": "name",
"optional": false,
"type": {
"kind": "string",
"encode": "string"
}
},
{
"kind": "property",
"name": "kind",
"serializedName": "kind",
"optional": false,
"type": {
"kind": "string",
"encode": "string"
}
}
],
"additionalProperties": {
"kind": "any"
}
}

Additional properties of specific type

model AnimalProperty {
category: string;
value: unknown;
}
model Animal {
name: string;
kind: string;
...Record<AnimalProperty>;
}
{
"kind": "model",
"name": "Animal",
"properties": [
{
"kind": "property",
"name": "name",
"serializedName": "name",
"optional": false,
"type": {
"kind": "string",
"encode": "string"
}
},
{
"kind": "property",
"name": "kind",
"serializedName": "kind",
"optional": false,
"type": {
"kind": "string",
"encode": "string"
}
}
],
"additionalProperties": {
"kind": "model",
"name": "AnimalProperty",
"properties": [
{
"kind": "property",
"name": "category",
"serializedName": "category",
"optional": false,
"type": {
"kind": "string",
"encode": "string"
}
},
{
"kind": "property",
"name": "value",
"serializedName": "value",
"optional": false,
"type": {
"kind": "any"
}
}
],
"additionalProperties": undefined
}
}

Additional properties of union type

model Animal {
name: string;
kind: string;
...Record<string | int32>;
}
model Animal {
name: string;
kind: string;
...Record<string>;
...Record<int32>;
}
{
"kind": "model",
"name": "Animal",
"properties": [
{
"kind": "property",
"name": "name",
"serializedName": "name",
"optional": false,
"type": {
"kind": "string",
"encode": "string"
}
},
{
"kind": "property",
"name": "kind",
"serializedName": "kind",
"optional": false,
"type": {
"kind": "string",
"encode": "string"
}
}
],
"additionalProperties": {
"kind": "union",
"name": "AnimalAdditionalProperty",
"generatedName": true,
"values": [
{
"kind": "string",
"encode": "string"
},
{
"kind": "int32"
}
]
}
}

Additional properties of nullable type

model Animal {
name: string;
kind: string;
...Record<string | null>;
}
{
"kind": "model",
"name": "Animal",
"properties": [
{
"kind": "property",
"name": "name",
"serializedName": "name",
"optional": false,
"type": {
"kind": "string",
"encode": "string"
}
},
{
"kind": "property",
"name": "kind",
"serializedName": "kind",
"optional": false,
"type": {
"kind": "string",
"encode": "string"
}
}
],
"additionalProperties": {
"kind": "nullable",
"valueType": {
"kind": "string",
"encode": "string"
}
}
}

Discriminator

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

TypeSpec now has two ways to represent a discriminated set.

  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;
}
  1. Use union
@discriminator("kind")
union Cat {
Siamese,
Ragdoll,
}
model Siamese {}
model Ragdoll {}

TCGC currently only supports the discriminated set based on models, discriminated union is not supported yet.

This is a brief structure of the models in a discriminated set in the output of TCGC.

{
"models": [
{
"kind": "model",
"name": "Cat",
"properties": [
{
"kind": "property",
"name": "kind",
"type": {
"kind": "string"
},
"discriminator": true
}
],
"discriminatorProperty": {
// the same instance of the property in the properties list here
},
"discriminatedSubtype": {
"siamese": {
"kind": "model",
"name": "Siamese",
"properties": [],
"discriminatorValue": "siamese"
},
"ragdoll": {
"kind": "model",
"name": "Ragdoll",
"properties": [],
"discriminatorValue": "ragdoll"
}
}
},
{
// the same instance of the model Siamese as we have above in `discriminatedSubtype` property
},
{
// the same instance of the model Ragdoll as we have above in `discriminatedSubtype` property
}
]
}

Nullable

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;
unionNullableProperty: Bar | Baz | null;
enumNullableProperty: LR | null;
}
model Bar {
prop: string;
}
model Baz {
prop: int32;
}
enum LR {
left,
right,
}

A nullable type has kind nullable and property valueType. The kind of the type tells you the property is nullable, while the valueType tells you the underlying type you want to generate.

{
"kind": "model",
"name": "Foo",
"properties": [
{
"kind": "property",
"name": "basicNullableProperty",
"serializedName": "basicNullableProperty",
"optional": false,
"type": {
"kind": "nullable",
"valueType": {
"kind": "string",
"encode": "string"
}
}
},
{
"kind": "property",
"name": "modelNullableProperty",
"serializedName": "modelNullableProperty",
"optional": false,
"type": {
"kind": "nullable",
"valueType": {
"kind": "model",
"name": "Bar",
"properties": [
{
"kind": "property",
"name": "prop",
"serializedName": "prop",
"optional": false,
"type": {
"kind": "string",
"encode": "string"
}
}
]
}
}
},
{
"kind": "property",
"name": "unionNullableProperty",
"serializedName": "unionNullableProperty",
"optional": false,
"type": {
"kind": "nullable",
"valueType": {
"kind": "union",
"values": [
{
"kind": "model",
"name": "Bar",
"properties": [
{
"kind": "property",
"name": "prop",
"serializedName": "prop",
"optional": false,
"type": {
"kind": "string",
"encode": "string"
}
}
]
},
{
"kind": "model",
"name": "Baz",
"properties": [
{
"kind": "property",
"name": "prop",
"serializedName": "prop",
"optional": false,
"type": {
"kind": "int32",
"encode": "int32"
}
}
]
}
]
}
}
},
{
"kind": "property",
"name": "enumNullableProperty",
"serializedName": "enumNullableProperty",
"optional": false,
"type": {
"kind": "nullable",
"valueType": {
"kind": "enum",
"name": "LR",
"generatedName": false,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "left",
"value": "left"
},
{
"kind": "enumvalue",
"name": "right",
"value": "right"
}
],
"isFixed": true,
"isUnionAsEnum": false
}
}
}
]
}

Unions

Union of literals with same type

All emitters will generate their version of a closed enum.

union LR {
left: "left",
right: "right",
}
{
"kind": "enum",
"name": "LR",
"generatedName": false,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "left",
"value": "left"
},
{
"kind": "enumvalue",
"name": "right",
"value": "right"
}
],
"isFixed": true,
"isUnionAsEnum": true
}

Inline union of literals with same type

This is union defined inline at point of usage.

model Widget {
horizontal: "left" | "right";
}
{
"kind": "enum",
"name": "WidgetHorizontals",
"generatedName": true,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "left",
"value": "left"
},
{
"kind": "enumvalue",
"name": "right",
"value": "right"
}
],
"isFixed": true,
"isUnionAsEnum": true
}

Union of basic type and literals of that type

Each language will generate their version of an open enum.

union Colors {
string,
red: "red",
blue: "blue",
}
{
"kind": "enum",
"name": "Colors",
"generatedName": false,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "red",
"value": "red"
},
{
"kind": "enumvalue",
"name": "blue",
"value": "blue"
}
],
"isFixed": false,
"isUnionAsEnum": true
}

Inline union of basic type and literals of that type

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

model Widget {
color: "red" | "blue" | string;
}
{
"kind": "enum",
"name": "WidgetColors",
"generatedName": true,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "red",
"value": "red"
},
{
"kind": "enumvalue",
"name": "blue",
"value": "blue"
}
],
"isFixed": false,
"isUnionAsEnum": true
}

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,
}

For union of other union or enum. TCGC will do the flatten according to the flag.

With flatten-union-as-enum flagged true:

{
"kind": "enum",
"name": "ProvisioningState",
"generatedName": false,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "InProgress",
"value": "InProgress"
},
{
"kind": "enumvalue",
"name": "Succeeded",
"value": "Succeeded"
},
{
"kind": "enumvalue",
"name": "Failed",
"value": "Failed"
},
{
"kind": "enumvalue",
"name": "Canceled",
"value": "Canceled"
}
],
"isFixed": false,
"isUnionAsEnum": true
}

With flatten-union-as-enum flagged false:

{
"kind": "union",
"name": "ProvisioningState",
"generatedName": false,
"values": [
{
"kind": "string"
},
{
"kind": "constant",
"value": "InProgress",
"valueType": {
"kind": "string"
}
},
{
"kind": "enum",
"name": "ResourceProvisioningState",
"generatedName": false,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "Succeeded",
"value": "Succeeded"
},
{
"kind": "enumvalue",
"name": "Failed",
"value": "Failed"
},
{
"kind": "enumvalue",
"name": "Canceled",
"value": "Canceled"
}
],
"isFixed": true,
"isUnionAsEnum": false
}
]
}

Union of other unions of literals with same type

union LR {
left: "left",
right: "right",
}
union UD {
up: "up",
down: "down",
}
union Orientation {
LR,
UD,
}

For union of other union or enum. TCGC will do the flatten according to the flag.

With flatten-union-as-enum flagged true:

{
"kind": "enum",
"name": "Orientation",
"generatedName": false,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "left",
"value": "left"
},
{
"kind": "enumvalue",
"name": "right",
"value": "right"
},
{
"kind": "enumvalue",
"name": "up",
"value": "up"
},
{
"kind": "enumvalue",
"name": "down",
"value": "down"
}
],
"isFixed": true,
"isUnionAsEnum": true
}

With flatten-union-as-enum flagged false:

{
"kind": "union",
"name": "Orientation",
"generatedName": false,
"values": [
{
"kind": "enum",
"name": "LR",
"generatedName": false,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "left",
"value": "left"
},
{
"kind": "enumvalue",
"name": "right",
"value": "right"
}
],
"isFixed": true,
"isUnionAsEnum": true
},
{
"kind": "enum",
"name": "UD",
"generatedName": false,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "up",
"value": "up"
},
{
"kind": "enumvalue",
"name": "down",
"value": "down"
}
],
"isFixed": true,
"isUnionAsEnum": true
}
]
}

Inline union of other unions of literals with same type

union LR {
left: "left",
right: "right",
}
union UD {
up: "up",
down: "down",
}
model Widget {
orientation: LR | UD;
}

For union of other union or enum. TCGC will do the flatten according to the flag.

With flatten-union-as-enum flagged true:

{
"kind": "enum",
"name": "WidgetOrientations",
"generatedName": true,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "left",
"value": "left"
},
{
"kind": "enumvalue",
"name": "right",
"value": "right"
},
{
"kind": "enumvalue",
"name": "up",
"value": "up"
},
{
"kind": "enumvalue",
"name": "down",
"value": "down"
}
],
"isFixed": true,
"isUnionAsEnum": true
}

With flatten-union-as-enum flagged false:

{
"kind": "union",
"name": "WidgetOrientations",
"generatedName": true,
"values": [
{
"kind": "enum",
"name": "LR",
"generatedName": false,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "left",
"value": "left"
},
{
"kind": "enumvalue",
"name": "right",
"value": "right"
}
],
"isFixed": true,
"isUnionAsEnum": true
},
{
"kind": "enum",
"name": "UD",
"generatedName": false,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "up",
"value": "up"
},
{
"kind": "enumvalue",
"name": "down",
"value": "down"
}
],
"isFixed": true,
"isUnionAsEnum": true
}
]
}

Union with multiple types

These are unions where the values don’t share same type.

model Shirt {
sizing: 32 | 34 | int32 | "small" | "medium" | string;
}
{
"kind": "union",
"name": "ShirtSizings",
"generatedName": true,
"values": [
{
"kind": "constant",
"value": 32,
"valueType": {
"kind": "int32"
}
},
{
"kind": "constant",
"value": 34,
"valueType": {
"kind": "int32"
}
},
{
"kind": "constant",
"value": "small",
"valueType": {
"kind": "string"
}
},
{
"kind": "constant",
"value": "medium",
"valueType": {
"kind": "string"
}
},
{
"kind": "string"
}
]
}

Enums

Standard

Standard enums will be generated as closed enums.

enum LR {
left,
right,
}
{
"kind": "enum",
"name": "LR",
"generatedName": false,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "left",
"value": "left"
},
{
"kind": "enumvalue",
"name": "right",
"value": "right"
}
],
"isFixed": true,
"isUnionAsEnum": false
}

Versioning Enums

@versioned(Versions)
@service
namespace My.Service;
enum Versions {
v1,
v2,
}
{
"kind": "enum",
"name": "Versions",
"generatedName": false,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "v1",
"value": "v1"
},
{
"kind": "enumvalue",
"name": "v2",
"value": "v2"
}
],
"isFixed": true,
"isUnionAsEnum": false,
"usage": 8
}

Spread

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,
}
{
"kind": "enum",
"name": "Orientation",
"generatedName": false,
"valueType": {
"kind": "string"
},
"values": [
{
"kind": "enumvalue",
"name": "left",
"value": "left"
},
{
"kind": "enumvalue",
"name": "right",
"value": "right"
},
{
"kind": "enumvalue",
"name": "up",
"value": "up"
},
{
"kind": "enumvalue",
"name": "down",
"value": "down"
}
],
"isFixed": true,
"isUnionAsEnum": false
}

Scalars

Encoding

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;
}
{
"kind": "property",
"name": "prop",
"type": {
"kind": "utcDateTime",
"encode": "rfc3339",
"wireType": {
"kind": "string"
}
}
}

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;
}
{
"kind": "property",
"name": "prop",
"type": {
"kind": "int64",
"encode": "string",
"wireType": {
"kind": "string"
}
}
}

Namespace

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

namespace My.Service;
model Foo {
prop: string;
}
@clientNamespace("My.Service.Temp")
model Bar {
prop: string;
}
from my.service import Foo
from my.service.temp import Bar
foo = Foo(prop="hello")
bar = Bar(prop="world")