Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tcgc] change nullable behavior #870

Merged
merged 27 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .chronus/changes/move_nullable_to_type-2024-4-21-18-6-54.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: breaking
packages:
- "@azure-tools/typespec-client-generator-core"
---

return nullable types as a new type called `SdkNullableType`
237 changes: 214 additions & 23 deletions docs/howtos/DataPlane Generation - DPG/06types.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ model Foo {
"serializedName": "prop",
"flatten": true,
"optional": false,
"nullable": false,
"type": {
"kind": "model",
"name": "Properties",
Expand All @@ -50,7 +49,6 @@ model Foo {
"serializedName": "name",
"flatten": false,
"optional": false,
"nullable": false,
"type": {
"kind": "string",
"encode": "string"
Expand Down Expand Up @@ -224,7 +222,6 @@ model Animal is Record<unknown> {
"name": "name",
"serializedName": "name",
"optional": false,
"nullable": false,
"type": {
"kind": "string",
"encode": "string"
Expand All @@ -235,7 +232,6 @@ model Animal is Record<unknown> {
"name": "kind",
"serializedName": "kind",
"optional": false,
"nullable": false,
"type": {
"kind": "string",
"encode": "string"
Expand All @@ -244,8 +240,7 @@ model Animal is Record<unknown> {
],
"additionalProperties": {
"kind": "any"
},
"additionalPropertiesNullable": false
}
}
```

Expand Down Expand Up @@ -353,7 +348,6 @@ model Animal {
"name": "name",
"serializedName": "name",
"optional": false,
"nullable": false,
"type": {
"kind": "string",
"encode": "string"
Expand All @@ -364,7 +358,6 @@ model Animal {
"name": "kind",
"serializedName": "kind",
"optional": false,
"nullable": false,
"type": {
"kind": "string",
"encode": "string"
Expand All @@ -380,7 +373,6 @@ model Animal {
"name": "category",
"serializedName": "category",
"optional": false,
"nullable": false,
"type": {
"kind": "string",
"encode": "string"
Expand All @@ -391,16 +383,13 @@ model Animal {
"name": "value",
"serializedName": "value",
"optional": false,
"nullable": false,
"type": {
"kind": "any"
}
}
],
"additionalProperties": undefined,
"additionalPropertiesNullable": undefined
},
"additionalPropertiesNullable": false
"additionalProperties": undefined
}
}
```

Expand Down Expand Up @@ -581,7 +570,6 @@ model Animal {
"name": "name",
"serializedName": "name",
"optional": false,
"nullable": false,
"type": {
"kind": "string",
"encode": "string"
Expand All @@ -592,7 +580,6 @@ model Animal {
"name": "kind",
"serializedName": "kind",
"optional": false,
"nullable": false,
"type": {
"kind": "string",
"encode": "string"
Expand All @@ -612,8 +599,7 @@ model Animal {
"kind": "int32"
}
]
},
"additionalPropertiesNullable": false
}
}
```

Expand Down Expand Up @@ -711,7 +697,6 @@ model Animal {
"name": "name",
"serializedName": "name",
"optional": false,
"nullable": false,
"type": {
"kind": "string",
"encode": "string"
Expand All @@ -722,17 +707,19 @@ model Animal {
"name": "kind",
"serializedName": "kind",
"optional": false,
"nullable": false,
"type": {
"kind": "string",
"encode": "string"
}
}
],
"additionalProperties": {
"kind": "string"
},
"additionalPropertiesNullable": true
"kind": "nullable",
"valueType": {
"kind": "string",
"encode": "string"
}
}
}
```

Expand Down Expand Up @@ -806,6 +793,210 @@ public final class Animal implements JsonSerializable<Animal> {
</TabItem>
</Tabs>

### 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.

<Tabs>
<TabItem value="typespec" label="TypeSpec" default>

```typespec
model Foo {
basicNullableProperty: string | null;
modelNullableProperty: Bar | null;
unionNullableProperty: Bar | Baz | null;
enumNullableProperty: LR | null;
}
iscai-msft marked this conversation as resolved.
Show resolved Hide resolved

model Bar {
prop: string;
}

model Baz {
prop: int32;
}

enum LR {
left,
right,
}
```

</TabItem>
<TabItem value="tcgc" label="TCGC">

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.

```json
{
"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
}
}
}
]
}
```

</TabItem>
<TabItem value="python" label="Python">

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.

```python
from enum import Enum
from corehttp.utils import CaseInsensitiveEnumMeta

class Bar(_model_base.Model):
prop: Optional[str] = rest_field()

class Baz(_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()
unionNullableProperty: Optional[Union["_models.Bar", "_models.Baz"]] = rest_field()
enumNullableProperty: Optional["LR"] = rest_field()

```

</TabItem>
<TabItem value="csharp" label="CSharp" >

TODO

</TabItem>
<TabItem value="typescript" label="Typescript" >

TODO

</TabItem>
<TabItem value="java" label="Java" >

TODO

</TabItem>
</Tabs>

## Unions

### Union of literals with same type
Expand Down
2 changes: 0 additions & 2 deletions packages/typespec-client-generator-core/doc/types.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ namespace TypeSpec.ClientGenerator.Core;
*
* @property __raw: the original TypeSpec type
* @property kind: the kind of type
* @property nullable: Whether the type is nullable on the wire
* @property deprecation: deprecated message if the type is deprecated
*/
@discriminator("kind")
model SdkType {
__raw?: unknown;
nullable: boolean;
deprecation?: string;
}

Expand Down
Loading
Loading