@typespec/emitter-framework 0.11.0-dev.1 → 0.11.0-dev.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/dist/src/core/components/index.js +2 -1
  2. package/dist/src/core/components/index.js.map +1 -0
  3. package/dist/src/core/components/output.js +2 -1
  4. package/dist/src/core/components/output.js.map +1 -0
  5. package/dist/src/core/components/overrides/component-overrides.js +2 -1
  6. package/dist/src/core/components/overrides/component-overrides.js.map +1 -0
  7. package/dist/src/core/components/overrides/config.js +2 -1
  8. package/dist/src/core/components/overrides/config.js.map +1 -0
  9. package/dist/src/core/components/overrides/context.js +2 -1
  10. package/dist/src/core/components/overrides/context.js.map +1 -0
  11. package/dist/src/core/context/index.js +2 -1
  12. package/dist/src/core/context/index.js.map +1 -0
  13. package/dist/src/core/context/name-policy-context.js +2 -1
  14. package/dist/src/core/context/name-policy-context.js.map +1 -0
  15. package/dist/src/core/context/tsp-context.js +2 -1
  16. package/dist/src/core/context/tsp-context.js.map +1 -0
  17. package/dist/src/core/index.js +2 -1
  18. package/dist/src/core/index.js.map +1 -0
  19. package/dist/src/core/transport-name-policy.js +2 -1
  20. package/dist/src/core/transport-name-policy.js.map +1 -0
  21. package/dist/src/core/write-output.d.ts.map +1 -1
  22. package/dist/src/core/write-output.js +4 -3
  23. package/dist/src/core/write-output.js.map +1 -0
  24. package/dist/src/csharp/components/class/declaration.d.ts +1 -1
  25. package/dist/src/csharp/components/class/declaration.d.ts.map +1 -1
  26. package/dist/src/csharp/components/class/declaration.js +14 -5
  27. package/dist/src/csharp/components/class/declaration.js.map +1 -0
  28. package/dist/src/csharp/components/class/declaration.test.js +65 -105
  29. package/dist/src/csharp/components/class/declaration.test.js.map +1 -0
  30. package/dist/src/csharp/components/enum/declaration.js +2 -1
  31. package/dist/src/csharp/components/enum/declaration.js.map +1 -0
  32. package/dist/src/csharp/components/enum/declaration.test.js +52 -88
  33. package/dist/src/csharp/components/enum/declaration.test.js.map +1 -0
  34. package/dist/src/csharp/components/index.js +2 -1
  35. package/dist/src/csharp/components/index.js.map +1 -0
  36. package/dist/src/csharp/components/property/property.d.ts.map +1 -1
  37. package/dist/src/csharp/components/property/property.js +40 -14
  38. package/dist/src/csharp/components/property/property.js.map +1 -0
  39. package/dist/src/csharp/components/property/property.test.js +129 -29
  40. package/dist/src/csharp/components/property/property.test.js.map +1 -0
  41. package/dist/src/csharp/components/type-expression.d.ts.map +1 -1
  42. package/dist/src/csharp/components/type-expression.js +22 -3
  43. package/dist/src/csharp/components/type-expression.js.map +1 -0
  44. package/dist/src/csharp/components/type-expression.test.js +87 -54
  45. package/dist/src/csharp/components/type-expression.test.js.map +1 -0
  46. package/dist/src/csharp/components/utils/doc-comments.js +2 -1
  47. package/dist/src/csharp/components/utils/doc-comments.js.map +1 -0
  48. package/dist/src/csharp/components/utils/nullable-util.d.ts +4 -0
  49. package/dist/src/csharp/components/utils/nullable-util.d.ts.map +1 -0
  50. package/dist/src/csharp/components/utils/nullable-util.js +27 -0
  51. package/dist/src/csharp/components/utils/nullable-util.js.map +1 -0
  52. package/dist/src/csharp/components/utils/refkey.js +2 -1
  53. package/dist/src/csharp/components/utils/refkey.js.map +1 -0
  54. package/dist/src/csharp/index.js +2 -1
  55. package/dist/src/csharp/index.js.map +1 -0
  56. package/dist/src/lib.js +2 -1
  57. package/dist/src/lib.js.map +1 -0
  58. package/dist/src/testing/index.js +2 -1
  59. package/dist/src/testing/index.js.map +1 -0
  60. package/dist/src/testing/scenario-test/code-block-expectation.js +2 -1
  61. package/dist/src/testing/scenario-test/code-block-expectation.js.map +1 -0
  62. package/dist/src/testing/scenario-test/code-block-expectation.test.js +2 -1
  63. package/dist/src/testing/scenario-test/code-block-expectation.test.js.map +1 -0
  64. package/dist/src/testing/scenario-test/harness.js +2 -1
  65. package/dist/src/testing/scenario-test/harness.js.map +1 -0
  66. package/dist/src/testing/scenario-test/index.js +2 -1
  67. package/dist/src/testing/scenario-test/index.js.map +1 -0
  68. package/dist/src/testing/scenario-test/snippet-extractor.js +2 -1
  69. package/dist/src/testing/scenario-test/snippet-extractor.js.map +1 -0
  70. package/dist/src/typescript/components/array-expression.js +2 -1
  71. package/dist/src/typescript/components/array-expression.js.map +1 -0
  72. package/dist/src/typescript/components/arrow-function.js +2 -1
  73. package/dist/src/typescript/components/arrow-function.js.map +1 -0
  74. package/dist/src/typescript/components/class-method.js +2 -1
  75. package/dist/src/typescript/components/class-method.js.map +1 -0
  76. package/dist/src/typescript/components/enum-declaration.js +2 -1
  77. package/dist/src/typescript/components/enum-declaration.js.map +1 -0
  78. package/dist/src/typescript/components/function-declaration.js +2 -1
  79. package/dist/src/typescript/components/function-declaration.js.map +1 -0
  80. package/dist/src/typescript/components/function-expression.js +2 -1
  81. package/dist/src/typescript/components/function-expression.js.map +1 -0
  82. package/dist/src/typescript/components/function-type.js +2 -1
  83. package/dist/src/typescript/components/function-type.js.map +1 -0
  84. package/dist/src/typescript/components/index.js +2 -1
  85. package/dist/src/typescript/components/index.js.map +1 -0
  86. package/dist/src/typescript/components/interface-declaration.js +2 -1
  87. package/dist/src/typescript/components/interface-declaration.js.map +1 -0
  88. package/dist/src/typescript/components/interface-member.js +2 -1
  89. package/dist/src/typescript/components/interface-member.js.map +1 -0
  90. package/dist/src/typescript/components/interface-method.js +2 -1
  91. package/dist/src/typescript/components/interface-method.js.map +1 -0
  92. package/dist/src/typescript/components/record-expression.js +2 -1
  93. package/dist/src/typescript/components/record-expression.js.map +1 -0
  94. package/dist/src/typescript/components/static-serializers.js +2 -1
  95. package/dist/src/typescript/components/static-serializers.js.map +1 -0
  96. package/dist/src/typescript/components/type-alias-declaration.js +2 -1
  97. package/dist/src/typescript/components/type-alias-declaration.js.map +1 -0
  98. package/dist/src/typescript/components/type-declaration.js +2 -1
  99. package/dist/src/typescript/components/type-declaration.js.map +1 -0
  100. package/dist/src/typescript/components/type-expression.js +2 -1
  101. package/dist/src/typescript/components/type-expression.js.map +1 -0
  102. package/dist/src/typescript/components/type-transform.js +2 -1
  103. package/dist/src/typescript/components/type-transform.js.map +1 -0
  104. package/dist/src/typescript/components/union/declaration.js +2 -1
  105. package/dist/src/typescript/components/union/declaration.js.map +1 -0
  106. package/dist/src/typescript/components/union/declaration.test.js +2 -1
  107. package/dist/src/typescript/components/union/declaration.test.js.map +1 -0
  108. package/dist/src/typescript/components/union/expression.js +2 -1
  109. package/dist/src/typescript/components/union/expression.js.map +1 -0
  110. package/dist/src/typescript/components/union/expression.test.js +2 -1
  111. package/dist/src/typescript/components/union/expression.test.js.map +1 -0
  112. package/dist/src/typescript/components/value-expression.js +2 -1
  113. package/dist/src/typescript/components/value-expression.js.map +1 -0
  114. package/dist/src/typescript/index.js +2 -1
  115. package/dist/src/typescript/index.js.map +1 -0
  116. package/dist/src/typescript/lib.js +2 -1
  117. package/dist/src/typescript/lib.js.map +1 -0
  118. package/dist/src/typescript/utils/index.js +2 -1
  119. package/dist/src/typescript/utils/index.js.map +1 -0
  120. package/dist/src/typescript/utils/operation.js +2 -1
  121. package/dist/src/typescript/utils/operation.js.map +1 -0
  122. package/dist/src/typescript/utils/refkey.js +2 -1
  123. package/dist/src/typescript/utils/refkey.js.map +1 -0
  124. package/dist/test/test-host.js +2 -1
  125. package/dist/test/test-host.js.map +1 -0
  126. package/dist/test/testing/snippet-extractor-csharp.test.js +2 -1
  127. package/dist/test/testing/snippet-extractor-csharp.test.js.map +1 -0
  128. package/dist/test/testing/snippet-extractor-java.test.js +2 -1
  129. package/dist/test/testing/snippet-extractor-java.test.js.map +1 -0
  130. package/dist/test/testing/snippet-extractor-python.test.js +2 -1
  131. package/dist/test/testing/snippet-extractor-python.test.js.map +1 -0
  132. package/dist/test/testing/snippet-extractor-typescript.test.js +2 -1
  133. package/dist/test/testing/snippet-extractor-typescript.test.js.map +1 -0
  134. package/dist/test/typescript/components/arrow-function.test.js +18 -22
  135. package/dist/test/typescript/components/arrow-function.test.js.map +1 -0
  136. package/dist/test/typescript/components/component-override.test.js +3 -2
  137. package/dist/test/typescript/components/component-override.test.js.map +1 -0
  138. package/dist/test/typescript/components/enum-declaration.test.js +2 -1
  139. package/dist/test/typescript/components/enum-declaration.test.js.map +1 -0
  140. package/dist/test/typescript/components/function-declaration.test.js +3 -2
  141. package/dist/test/typescript/components/function-declaration.test.js.map +1 -0
  142. package/dist/test/typescript/components/function-expression.test.js +18 -22
  143. package/dist/test/typescript/components/function-expression.test.js.map +1 -0
  144. package/dist/test/typescript/components/function-type.test.js +18 -22
  145. package/dist/test/typescript/components/function-type.test.js.map +1 -0
  146. package/dist/test/typescript/components/interface-declaration.test.js +3 -2
  147. package/dist/test/typescript/components/interface-declaration.test.js.map +1 -0
  148. package/dist/test/typescript/components/interface-method.test.js +37 -45
  149. package/dist/test/typescript/components/interface-method.test.js.map +1 -0
  150. package/dist/test/typescript/components/member-expression.test.js +2 -1
  151. package/dist/test/typescript/components/member-expression.test.js.map +1 -0
  152. package/dist/test/typescript/components/type-alias-declaration.test.js +8 -5
  153. package/dist/test/typescript/components/type-alias-declaration.test.js.map +1 -0
  154. package/dist/test/typescript/components/type-transform.test.js +29 -27
  155. package/dist/test/typescript/components/type-transform.test.js.map +1 -0
  156. package/dist/test/typescript/components/value-expression.test.js +3 -2
  157. package/dist/test/typescript/components/value-expression.test.js.map +1 -0
  158. package/dist/test/typescript/utils.js +2 -1
  159. package/dist/test/typescript/utils.js.map +1 -0
  160. package/dist/test/utils.d.ts +1 -0
  161. package/dist/test/utils.d.ts.map +1 -1
  162. package/dist/test/utils.js +13 -3
  163. package/dist/test/utils.js.map +1 -0
  164. package/dist/test/vitest.setup.js +2 -1
  165. package/dist/test/vitest.setup.js.map +1 -0
  166. package/package.json +8 -8
  167. package/src/core/write-output.ts +2 -2
  168. package/src/csharp/components/class/declaration.test.tsx +64 -102
  169. package/src/csharp/components/class/declaration.tsx +13 -3
  170. package/src/csharp/components/enum/declaration.test.tsx +47 -78
  171. package/src/csharp/components/property/property.test.tsx +108 -25
  172. package/src/csharp/components/property/property.tsx +43 -11
  173. package/src/csharp/components/type-expression.test.tsx +84 -69
  174. package/src/csharp/components/type-expression.tsx +25 -4
  175. package/src/csharp/components/utils/nullable-util.ts +25 -0
  176. package/test/typescript/components/arrow-function.test.tsx +23 -42
  177. package/test/typescript/components/component-override.test.tsx +1 -1
  178. package/test/typescript/components/function-declaration.test.tsx +2 -1
  179. package/test/typescript/components/function-expression.test.tsx +23 -42
  180. package/test/typescript/components/function-type.test.tsx +22 -40
  181. package/test/typescript/components/interface-declaration.test.tsx +1 -1
  182. package/test/typescript/components/interface-method.test.tsx +50 -92
  183. package/test/typescript/components/type-alias-declaration.test.tsx +8 -6
  184. package/test/typescript/components/type-transform.test.tsx +37 -37
  185. package/test/typescript/components/value-expression.test.tsx +1 -1
  186. package/test/utils.ts +10 -3
  187. package/dist/test/typescript/test-host.d.ts +0 -12
  188. package/dist/test/typescript/test-host.d.ts.map +0 -1
  189. package/dist/test/typescript/test-host.js +0 -29
  190. package/test/typescript/test-host.ts +0 -37
@@ -1,7 +1,7 @@
1
- import { type Children, For } from "@alloy-js/core";
1
+ import { For, type Children } from "@alloy-js/core";
2
2
  import * as cs from "@alloy-js/csharp";
3
3
  import { Method } from "@alloy-js/csharp";
4
- import type { Interface, Model } from "@typespec/compiler";
4
+ import { isVoidType, type Interface, type Model } from "@typespec/compiler";
5
5
  import { useTsp } from "../../../core/index.js";
6
6
  import { Property } from "../property/property.jsx";
7
7
  import { TypeExpression } from "../type-expression.jsx";
@@ -41,6 +41,12 @@ export function ClassDeclaration(props: ClassDeclarationProps): Children {
41
41
  {...props}
42
42
  name={className}
43
43
  refkey={refkeys}
44
+ baseType={
45
+ props.baseType ??
46
+ (props.type.kind === "Model" && props.type.baseModel ? (
47
+ <TypeExpression type={props.type.baseModel} />
48
+ ) : undefined)
49
+ }
44
50
  doc={getDocComments($, props.type)}
45
51
  >
46
52
  {props.type.kind === "Model" && (
@@ -53,8 +59,12 @@ export function ClassDeclaration(props: ClassDeclarationProps): Children {
53
59
  }
54
60
 
55
61
  function ClassProperties(props: ClassPropertiesProps): Children {
62
+ // Ignore 'void' type properties which is not valid in csharp
63
+ const properties = Array.from(props.type.properties.entries()).filter(
64
+ ([_, p]) => !isVoidType(p.type),
65
+ );
56
66
  return (
57
- <For each={props.type.properties.entries()} hardline>
67
+ <For each={properties} doubleHardline>
58
68
  {([name, property]) => <Property type={property} jsonAttributes={props.jsonAttributes} />}
59
69
  </For>
60
70
  );
@@ -1,6 +1,6 @@
1
1
  import { Tester } from "#test/test-host.js";
2
2
  import { type Children } from "@alloy-js/core";
3
- import { createCSharpNamePolicy, Namespace, SourceFile } from "@alloy-js/csharp";
3
+ import { createCSharpNamePolicy, SourceFile } from "@alloy-js/csharp";
4
4
  import { t, type TesterInstance } from "@typespec/compiler/testing";
5
5
  import { beforeEach, expect, it } from "vitest";
6
6
  import { Output } from "../../../core/index.js";
@@ -16,9 +16,7 @@ function Wrapper(props: { children: Children }) {
16
16
  const policy = createCSharpNamePolicy();
17
17
  return (
18
18
  <Output program={runner.program} namePolicy={policy}>
19
- <Namespace name="TestNamespace">
20
- <SourceFile path="test.cs">{props.children}</SourceFile>
21
- </Namespace>
19
+ <SourceFile path="test.cs">{props.children}</SourceFile>
22
20
  </Output>
23
21
  );
24
22
  }
@@ -37,14 +35,11 @@ it("renders a basic enum declaration", async () => {
37
35
  <EnumDeclaration type={TestEnum} />
38
36
  </Wrapper>,
39
37
  ).toRenderTo(`
40
- namespace TestNamespace
38
+ enum TestEnum
41
39
  {
42
- enum TestEnum
43
- {
44
- Value1,
45
- Value2,
46
- Value3
47
- }
40
+ Value1,
41
+ Value2,
42
+ Value3
48
43
  }
49
44
  `);
50
45
  });
@@ -59,12 +54,9 @@ it("renders an empty enum declaration", async () => {
59
54
  <EnumDeclaration type={TestEnum} />
60
55
  </Wrapper>,
61
56
  ).toRenderTo(`
62
- namespace TestNamespace
57
+ enum TestEnum
63
58
  {
64
- enum TestEnum
65
- {
66
59
 
67
- }
68
60
  }
69
61
  `);
70
62
  });
@@ -82,13 +74,10 @@ it("can override enum name", async () => {
82
74
  <EnumDeclaration type={TestEnum} name="CustomEnumName" />
83
75
  </Wrapper>,
84
76
  ).toRenderTo(`
85
- namespace TestNamespace
77
+ enum CustomEnumName
86
78
  {
87
- enum CustomEnumName
88
- {
89
- Value1,
90
- Value2
91
- }
79
+ Value1,
80
+ Value2
92
81
  }
93
82
  `);
94
83
  });
@@ -106,13 +95,10 @@ it("renders an enum with access modifiers", async () => {
106
95
  <EnumDeclaration type={TestEnum} internal />
107
96
  </Wrapper>,
108
97
  ).toRenderTo(`
109
- namespace TestNamespace
98
+ internal enum TestEnum
110
99
  {
111
- internal enum TestEnum
112
- {
113
- Value1,
114
- Value2
115
- }
100
+ Value1,
101
+ Value2
116
102
  }
117
103
  `);
118
104
  });
@@ -128,21 +114,16 @@ it("renders enum with C# naming conventions", async () => {
128
114
 
129
115
  expect(
130
116
  <Output program={runner.program} namePolicy={createCSharpNamePolicy()}>
131
- <Namespace name="TestNamespace">
132
- <SourceFile path="test.cs">
133
- <EnumDeclaration type={TestEnum} />
134
- </SourceFile>
135
- </Namespace>
117
+ <SourceFile path="test.cs">
118
+ <EnumDeclaration type={TestEnum} />
119
+ </SourceFile>
136
120
  </Output>,
137
121
  ).toRenderTo(`
138
- namespace TestNamespace
122
+ enum TestEnum
139
123
  {
140
- enum TestEnum
141
- {
142
- ValueOne,
143
- ValueTwo,
144
- ValueThree
145
- }
124
+ ValueOne,
125
+ ValueTwo,
126
+ ValueThree
146
127
  }
147
128
  `);
148
129
  });
@@ -159,13 +140,10 @@ it("renders enum with single value", async () => {
159
140
  <EnumDeclaration type={TestEnum} />
160
141
  </Wrapper>,
161
142
  ).toRenderTo(`
162
- namespace TestNamespace
163
- {
164
- enum TestEnum
165
- {
166
- OnlyValue
167
- }
168
- }
143
+ enum TestEnum
144
+ {
145
+ OnlyValue
146
+ }
169
147
  `);
170
148
  });
171
149
 
@@ -184,15 +162,12 @@ it("renders enum with numeric-like member names", async () => {
184
162
  <EnumDeclaration type={TestEnum} />
185
163
  </Wrapper>,
186
164
  ).toRenderTo(`
187
- namespace TestNamespace
165
+ enum TestEnum
188
166
  {
189
- enum TestEnum
190
- {
191
- Value0,
192
- Value1,
193
- Value10,
194
- Value100
195
- }
167
+ Value0,
168
+ Value1,
169
+ Value10,
170
+ Value100
196
171
  }
197
172
  `);
198
173
  });
@@ -217,19 +192,16 @@ it("renders multiple enums in the same namespace", async () => {
217
192
  <EnumDeclaration type={TestEnum2} />
218
193
  </Wrapper>,
219
194
  ).toRenderTo(`
220
- namespace TestNamespace
195
+ enum TestEnum1
196
+ {
197
+ Value1,
198
+ Value2
199
+ }
200
+ enum TestEnum2
221
201
  {
222
- enum TestEnum1
223
- {
224
- Value1,
225
- Value2
226
- }
227
- enum TestEnum2
228
- {
229
- OptionA,
230
- OptionB,
231
- OptionC
232
- }
202
+ OptionA,
203
+ OptionB,
204
+ OptionC
233
205
  }
234
206
  `);
235
207
  });
@@ -249,19 +221,16 @@ it("renders an enum with doc comments", async () => {
249
221
  <EnumDeclaration type={TestEnum} />
250
222
  </Wrapper>,
251
223
  ).toRenderTo(`
252
- namespace TestNamespace
224
+ enum TestEnum
253
225
  {
254
- enum TestEnum
255
- {
256
- /// <summary>
257
- /// This is value one
258
- /// </summary>
259
- Value1,
260
- /// <summary>
261
- /// This is value two
262
- /// </summary>
263
- Value2
264
- }
226
+ /// <summary>
227
+ /// This is value one
228
+ /// </summary>
229
+ Value1,
230
+ /// <summary>
231
+ /// This is value two
232
+ /// </summary>
233
+ Value2
265
234
  }
266
235
  `);
267
236
  });
@@ -1,6 +1,6 @@
1
1
  import { Tester } from "#test/test-host.js";
2
- import { type Children } from "@alloy-js/core";
3
- import { ClassDeclaration, createCSharpNamePolicy, Namespace, SourceFile } from "@alloy-js/csharp";
2
+ import { List, type Children } from "@alloy-js/core";
3
+ import { ClassDeclaration, createCSharpNamePolicy, SourceFile } from "@alloy-js/csharp";
4
4
  import { t, type TesterInstance } from "@typespec/compiler/testing";
5
5
  import { beforeEach, describe, expect, it } from "vitest";
6
6
  import { Output } from "../../../core/components/output.jsx";
@@ -16,11 +16,9 @@ function Wrapper(props: { children: Children }) {
16
16
  const policy = createCSharpNamePolicy();
17
17
  return (
18
18
  <Output program={tester.program} namePolicy={policy}>
19
- <Namespace name="TestNamespace">
20
- <SourceFile path="test.cs">
21
- <ClassDeclaration name="Test">{props.children}</ClassDeclaration>
22
- </SourceFile>
23
- </Namespace>
19
+ <SourceFile path="test.cs">
20
+ <ClassDeclaration name="Test">{props.children}</ClassDeclaration>
21
+ </SourceFile>
24
22
  </Output>
25
23
  );
26
24
  }
@@ -37,12 +35,47 @@ it("maps prop: string | null to nullable property", async () => {
37
35
  <Property type={prop1} />
38
36
  </Wrapper>,
39
37
  ).toRenderTo(`
40
- namespace TestNamespace
38
+ class Test
41
39
  {
42
- class Test
43
- {
44
- public required string? Prop1 { get; set; }
45
- }
40
+ public required string? Prop1 { get; set; }
41
+ }
42
+ `);
43
+ });
44
+
45
+ it("maps optional properties to nullable properties", async () => {
46
+ const { prop1 } = await tester.compile(t.code`
47
+ model TestModel {
48
+ ${t.modelProperty("prop1")}?: string;
49
+ }
50
+ `);
51
+
52
+ expect(
53
+ <Wrapper>
54
+ <Property type={prop1} />
55
+ </Wrapper>,
56
+ ).toRenderTo(`
57
+ class Test
58
+ {
59
+ public string? Prop1 { get; set; }
60
+ }
61
+ `);
62
+ });
63
+
64
+ it("maps optional and nullable properties to nullable properties", async () => {
65
+ const { prop1 } = await tester.compile(t.code`
66
+ model TestModel {
67
+ ${t.modelProperty("prop1")}?: string | null;
68
+ }
69
+ `);
70
+
71
+ expect(
72
+ <Wrapper>
73
+ <Property type={prop1} />
74
+ </Wrapper>,
75
+ ).toRenderTo(`
76
+ class Test
77
+ {
78
+ public string? Prop1 { get; set; }
46
79
  }
47
80
  `);
48
81
  });
@@ -60,13 +93,10 @@ describe("jsonAttributes", () => {
60
93
  <Property type={prop1} jsonAttributes />
61
94
  </Wrapper>,
62
95
  ).toRenderTo(`
63
- namespace TestNamespace
96
+ class Test
64
97
  {
65
- class Test
66
- {
67
- [System.Text.Json.JsonPropertyName("prop1")]
68
- public required string Prop1 { get; set; }
69
- }
98
+ [System.Text.Json.JsonPropertyName("prop1")]
99
+ public required string Prop1 { get; set; }
70
100
  }
71
101
  `);
72
102
  });
@@ -84,14 +114,67 @@ describe("jsonAttributes", () => {
84
114
  <Property type={prop1} jsonAttributes />
85
115
  </Wrapper>,
86
116
  ).toRenderTo(`
87
- namespace TestNamespace
117
+ class Test
118
+ {
119
+ [System.Text.Json.JsonPropertyName("prop_1")]
120
+ public required string Prop1 { get; set; }
121
+ }
122
+ `);
123
+ });
124
+
125
+ it("inherit prop: override, new", async () => {
126
+ const r = await tester.compile(t.code`
127
+ model TestModel extends BaseModel {
128
+ ${t.modelProperty("prop1")}: string;
129
+ ${t.modelProperty("prop2")}: string | null;
130
+ }
131
+ model BaseModel {
132
+ prop1: string | null;
133
+ prop2: string | null;
134
+ }
135
+ `);
136
+
137
+ expect(
138
+ <Wrapper>
139
+ <List>
140
+ <Property type={r.prop1} />
141
+ <Property type={r.prop2} />
142
+ </List>
143
+ </Wrapper>,
144
+ ).toRenderTo(`
145
+ class Test
88
146
  {
89
- class Test
90
- {
91
- [System.Text.Json.JsonPropertyName("prop_1")]
92
- public required string Prop1 { get; set; }
93
- }
147
+ public new required string Prop1 { get; set; }
148
+ public override required string? Prop2 { get; set; }
94
149
  }
95
- `);
150
+ `);
151
+ });
152
+
153
+ it("inherit prop: virtual", async () => {
154
+ const r = await tester.compile(t.code`
155
+ model TestModel extends BaseModel {
156
+ prop1: string;
157
+ prop2: string | null;
158
+ }
159
+ model BaseModel {
160
+ ${t.modelProperty("prop1")}: string | null;
161
+ ${t.modelProperty("prop2")}: string | null;
162
+ }
163
+ `);
164
+
165
+ expect(
166
+ <Wrapper>
167
+ <List>
168
+ <Property type={r.prop1} />
169
+ <Property type={r.prop2} />
170
+ </List>
171
+ </Wrapper>,
172
+ ).toRenderTo(`
173
+ class Test
174
+ {
175
+ public required string? Prop1 { get; set; }
176
+ public virtual required string? Prop2 { get; set; }
177
+ }
178
+ `);
96
179
  });
97
180
  });
@@ -1,10 +1,11 @@
1
1
  import { type Children } from "@alloy-js/core";
2
2
  import * as cs from "@alloy-js/csharp";
3
3
  import { Attribute } from "@alloy-js/csharp";
4
- import { type ModelProperty, resolveEncodedName, type Type } from "@typespec/compiler";
4
+ import { getProperty, type ModelProperty, resolveEncodedName, type Type } from "@typespec/compiler";
5
5
  import { useTsp } from "../../../core/index.js";
6
6
  import { TypeExpression } from "../type-expression.jsx";
7
7
  import { getDocComments } from "../utils/doc-comments.jsx";
8
+ import { getNullableUnionInnerType } from "../utils/nullable-util.js";
8
9
 
9
10
  export interface PropertyProps {
10
11
  type: ModelProperty;
@@ -16,14 +17,47 @@ export interface PropertyProps {
16
17
  * Create a C# property declaration from a TypeSpec property type.
17
18
  */
18
19
  export function Property(props: PropertyProps): Children {
19
- const result = preprocessPropertyType(props.type.type);
20
+ const result = preprocessPropertyType(props.type);
20
21
  const { $ } = useTsp();
21
22
 
23
+ let overrideType: "" | "override" | "new" = "";
24
+ let isVirtual = false;
25
+ if (props.type.model) {
26
+ if (props.type.model.baseModel) {
27
+ const base = props.type.model.baseModel;
28
+ const baseProperty = getProperty(base, props.type.name);
29
+ if (baseProperty) {
30
+ const baseResult = preprocessPropertyType(baseProperty);
31
+ if (baseResult.nullable === result.nullable && baseResult.type === result.type) {
32
+ overrideType = "override";
33
+ } else {
34
+ overrideType = "new";
35
+ }
36
+ }
37
+ }
38
+ if (
39
+ overrideType === "" &&
40
+ props.type.model.derivedModels &&
41
+ props.type.model.derivedModels.length > 0
42
+ ) {
43
+ isVirtual = props.type.model.derivedModels.some((derived) => {
44
+ const derivedProperty = derived.properties.get(props.type.name);
45
+ if (derivedProperty) {
46
+ const derivedResult = preprocessPropertyType(derivedProperty);
47
+ return derivedResult.nullable === result.nullable && derivedResult.type === result.type;
48
+ }
49
+ });
50
+ }
51
+ }
52
+
22
53
  return (
23
54
  <cs.Property
24
55
  name={props.type.name}
25
56
  type={<TypeExpression type={result.type} />}
57
+ override={overrideType === "override"}
58
+ new={overrideType === "new"}
26
59
  public
60
+ virtual={isVirtual}
27
61
  required={!props.type.optional}
28
62
  nullable={result.nullable}
29
63
  doc={getDocComments($, props.type)}
@@ -44,19 +78,17 @@ function JsonNameAttribute(props: JsonNameAttributeProps): Children {
44
78
  return <Attribute name="System.Text.Json.JsonPropertyName" args={[JSON.stringify(jsonName)]} />;
45
79
  }
46
80
 
47
- function preprocessPropertyType(type: Type): { type: Type; nullable: boolean } {
48
- const { $ } = useTsp();
81
+ function preprocessPropertyType(prop: ModelProperty): { type: Type; nullable: boolean } {
82
+ const type = prop.type;
49
83
 
50
84
  if (type.kind === "Union") {
51
- const variants = type.variants;
52
- const nonNullVariant = [...variants.values()].find((v) => v.type !== $.intrinsic.null);
53
- const nullVariant = [...variants.values()].find((v) => v.type !== $.intrinsic.null);
54
- if (nonNullVariant && nullVariant && variants.size === 2) {
55
- return { type: nonNullVariant.type, nullable: true };
85
+ const innerType = getNullableUnionInnerType(type);
86
+ if (innerType) {
87
+ return { type: innerType, nullable: true };
56
88
  } else {
57
- return { type, nullable: false };
89
+ return { type, nullable: prop.optional };
58
90
  }
59
91
  } else {
60
- return { type, nullable: false };
92
+ return { type, nullable: prop.optional };
61
93
  }
62
94
  }