Skip to content

Commit c31ec95

Browse files
author
George Kinsman
committed
fix(api): change representation of nullable types to union with null instead of optional parameter
1 parent 279d6fc commit c31ec95

5 files changed

+27
-20
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ declare namespace Api {
157157

158158
## Nullable Value Types
159159

160-
Nullable value types are rendered to optional properties in TypeScript:
160+
Nullable value types are rendered to properties with a null union in TypeScript:
161161

162162
```csharp
163163
class TypeWithNullable
@@ -170,8 +170,8 @@ class TypeWithNullable
170170
```TypeScript
171171
declare namespace Api {
172172
interface TypeWithNullable {
173-
nullableInt?: number;
174-
nullableGuid?: string;
173+
nullableInt: number | null;
174+
nullableGuid: string | null;
175175
}
176176
}
177177
```

src/Typescript.Tests/Enums/EnumGeneratorTests.Generator_TypeWithNullableEnum_GeneratesSuccessfully.approved.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
declare namespace Api {
22
interface TypeWithNullableEnum {
3-
nullableEnumProp?: 'FirstEnum' | 'SecondEnum' | 'ThirdEnum';
3+
nullableEnumProp: 'FirstEnum' | 'SecondEnum' | 'ThirdEnum' | null;
44
}
55
}
66

src/Typescript.Tests/NullableTypes/NullableTypeGeneratorTests.Generator_TypeWithNestedNullable_GeneratesSuccessfully.approved.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ declare namespace Api {
33
nestedThing: NestedType;
44
}
55
interface NestedType {
6-
nullableInt?: number;
6+
nullableInt: number | null;
77
nullableDateTime: string;
88
}
99
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
declare namespace Api {
22
interface TypeWithNullable {
3-
nullableInt?: number;
4-
nullableGuid?: string;
3+
nullableInt: number | null;
4+
nullableGuid: string | null;
55
}
66
}

src/Typescriptr/TypeScriptGenerator.cs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
namespace Typescriptr
1111
{
1212
public delegate string FormatEnum(Type t, QuoteStyle quoteStyle);
13+
1314
public delegate string FormatEnumProperty(Type t, QuoteStyle quoteStyle);
15+
1416
public delegate string FormatDictionaryProperty(Type t, Func<Type, string> typeNameRenderer);
17+
1518
public delegate string FormatCollectionProperty(Type t, Func<Type, string> typeNameRenderer);
1619

1720
public class TypeScriptGenerator
@@ -55,7 +58,8 @@ private TypeScriptGenerator()
5558
.WithPropertyTypeFormatter<TimeSpan>(t => "string")
5659
.WithPropertyTypeFormatter<Guid>(t => "string")
5760
.WithPropertyTypeFormatter<DateTimeOffset>(t => "string")
58-
.WithEnumFormatter(EnumFormatter.ValueNamedEnumFormatter, EnumFormatter.UnionStringEnumPropertyTypeFormatter)
61+
.WithEnumFormatter(EnumFormatter.ValueNamedEnumFormatter,
62+
EnumFormatter.UnionStringEnumPropertyTypeFormatter)
5963
.WithQuoteStyle(QuoteStyle.Single)
6064
.WithDictionaryPropertyFormatter(DictionaryPropertyFormatter.KeyValueFormatter)
6165
.WithCollectionPropertyFormatter(CollectionPropertyFormatter.Format)
@@ -73,13 +77,13 @@ public TypeScriptGenerator WithQuoteStyle(QuoteStyle style)
7377
_quoteStyle = style;
7478
return this;
7579
}
76-
80+
7781
public TypeScriptGenerator WithNamespace(string @namespace)
7882
{
7983
_namespace = @namespace;
8084
return this;
8185
}
82-
86+
8387
public TypeScriptGenerator WithDictionaryPropertyFormatter(FormatDictionaryProperty formatter)
8488
{
8589
_dictionaryPropertyFormatter = formatter;
@@ -142,7 +146,7 @@ public GenerationResult Generate(IEnumerable<Type> types)
142146
{
143147
typeBuilder.AppendLine();
144148
}
145-
149+
146150
return new GenerationResult(typeBuilder.ToString(), enumBuilder.ToString());
147151
}
148152

@@ -165,8 +169,6 @@ private void RenderType(StringBuilder builder, Type type)
165169

166170
if (_useCamelCasePropertyNames)
167171
propName = propName.ToCamelCase();
168-
if (Nullable.GetUnderlyingType(propType) != null)
169-
propName = $"{propName}?";
170172

171173
RenderProperty(builder, propType, propName);
172174
}
@@ -182,25 +184,30 @@ private void RenderType(StringBuilder builder, Type type)
182184

183185
private string TypeNameRenderer(Type type)
184186
{
187+
Func<string, string> decorate = str => str;
188+
185189
if (Nullable.GetUnderlyingType(type) != null)
190+
{
186191
type = Nullable.GetUnderlyingType(type);
192+
decorate = str => str + " | null";
193+
}
187194

188195
if (_propTypeMap.ContainsKey(type))
189-
return _propTypeMap[type];
190-
196+
return decorate(_propTypeMap[type]);
197+
191198
if (typeof(IDictionary).IsAssignableFrom(type))
192-
return _dictionaryPropertyFormatter(type, TypeNameRenderer);
193-
199+
return decorate(_dictionaryPropertyFormatter(type, TypeNameRenderer));
200+
194201
if (typeof(IEnumerable).IsAssignableFrom(type))
195-
return _collectionPropertyFormatter(type, TypeNameRenderer);
202+
return decorate(_collectionPropertyFormatter(type, TypeNameRenderer));
196203

197204
var typeName = type.Name;
198205
if (typeof(Enum).IsAssignableFrom(type))
199206
typeName = _enumPropertyFormatter(type, _quoteStyle);
200207

201208
if (!_typesGenerated.Contains(type)) _typeStack.Push(type);
202-
203-
return typeName;
209+
210+
return decorate(typeName);
204211
}
205212

206213
private void RenderProperty(StringBuilder builder, Type propType, string propName)

0 commit comments

Comments
 (0)