Skip to content

Commit 9bf8f69

Browse files
authored
TypeScript generator: Use string type as the discriminator property type in specialized interfaces (#1718)
* improve TypeScript interface generation * TypeScript generator: Use string type as the discriminator property type in specialized interfaces * fix tests
1 parent 9545eed commit 9bf8f69

8 files changed

+322
-9
lines changed

src/NJsonSchema.CodeGeneration.Tests/EnumGenerationTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,8 @@ public async Task When_enum_has_string_value_then_TS_code_has_string_value()
166166
var code = generator.GenerateFile("MyClass");
167167

168168
//// Assert
169-
Assert.Contains("_0562 = <any>\"0562\",", code);
170-
Assert.Contains("_0532 = <any>\"0532\",", code);
169+
Assert.Contains("_0562 = \"0562\",", code);
170+
Assert.Contains("_0532 = \"0532\",", code);
171171
}
172172

173173
public class ClassWithStringEnum
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//----------------------
2+
// <auto-generated>
3+
// </auto-generated>
4+
//----------------------
5+
6+
7+
8+
9+
10+
11+
12+
export interface Base {
13+
Type: EBase;
14+
}
15+
16+
export enum EBase {
17+
OneChild = "OneChild",
18+
SecondChild = "SecondChild",
19+
}
20+
21+
export interface OneChild extends Base {
22+
A: string;
23+
Type: EBase.OneChild;
24+
}
25+
26+
export interface SecondChild extends Base {
27+
B: string;
28+
Type: EBase.SecondChild;
29+
}
30+
31+
export interface MyClass {
32+
Child: Base;
33+
Children: Base[];
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//----------------------
2+
// <auto-generated>
3+
// </auto-generated>
4+
//----------------------
5+
6+
7+
8+
9+
10+
11+
12+
export interface Base {
13+
Type: EBase;
14+
}
15+
16+
export type EBase = "OneChild" | "SecondChild";
17+
18+
export interface OneChild extends Base {
19+
A: string;
20+
Type: 'OneChild';
21+
}
22+
23+
export interface SecondChild extends Base {
24+
B: string;
25+
Type: 'SecondChild';
26+
}
27+
28+
export interface MyClass {
29+
Child: Base;
30+
Children: Base[];
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
//----------------------
2+
// <auto-generated>
3+
// </auto-generated>
4+
//----------------------
5+
6+
7+
8+
9+
10+
11+
12+
export abstract class Base implements IBase {
13+
14+
protected _discriminator: string;
15+
16+
constructor(data?: IBase) {
17+
if (data) {
18+
for (var property in data) {
19+
if (data.hasOwnProperty(property))
20+
(<any>this)[property] = (<any>data)[property];
21+
}
22+
}
23+
this._discriminator = "Base";
24+
}
25+
26+
init(_data?: any) {
27+
}
28+
29+
static fromJS(data: any): Base {
30+
data = typeof data === 'object' ? data : {};
31+
if (data["Type"] === "OneChild") {
32+
let result = new OneChild();
33+
result.init(data);
34+
return result;
35+
}
36+
if (data["Type"] === "SecondChild") {
37+
let result = new SecondChild();
38+
result.init(data);
39+
return result;
40+
}
41+
throw new Error("The abstract class 'Base' cannot be instantiated.");
42+
}
43+
44+
toJSON(data?: any) {
45+
data = typeof data === 'object' ? data : {};
46+
data["Type"] = this._discriminator;
47+
return data;
48+
}
49+
}
50+
51+
export interface IBase {
52+
}
53+
54+
export class OneChild extends Base implements IOneChild {
55+
a: string;
56+
type: EBase;
57+
58+
constructor(data?: IOneChild) {
59+
super(data);
60+
this._discriminator = "OneChild";
61+
}
62+
63+
init(_data?: any) {
64+
super.init(_data);
65+
if (_data) {
66+
this.a = _data["A"];
67+
this.type = _data["Type"];
68+
}
69+
}
70+
71+
static fromJS(data: any): OneChild {
72+
data = typeof data === 'object' ? data : {};
73+
let result = new OneChild();
74+
result.init(data);
75+
return result;
76+
}
77+
78+
toJSON(data?: any) {
79+
data = typeof data === 'object' ? data : {};
80+
data["A"] = this.a;
81+
data["Type"] = this.type;
82+
super.toJSON(data);
83+
return data;
84+
}
85+
}
86+
87+
export interface IOneChild extends IBase {
88+
a: string;
89+
type: EBase;
90+
}
91+
92+
export enum EBase {
93+
OneChild = "OneChild",
94+
SecondChild = "SecondChild",
95+
}
96+
97+
export class SecondChild extends Base implements ISecondChild {
98+
b: string;
99+
type: EBase;
100+
101+
constructor(data?: ISecondChild) {
102+
super(data);
103+
this._discriminator = "SecondChild";
104+
}
105+
106+
init(_data?: any) {
107+
super.init(_data);
108+
if (_data) {
109+
this.b = _data["B"];
110+
this.type = _data["Type"];
111+
}
112+
}
113+
114+
static fromJS(data: any): SecondChild {
115+
data = typeof data === 'object' ? data : {};
116+
let result = new SecondChild();
117+
result.init(data);
118+
return result;
119+
}
120+
121+
toJSON(data?: any) {
122+
data = typeof data === 'object' ? data : {};
123+
data["B"] = this.b;
124+
data["Type"] = this.type;
125+
super.toJSON(data);
126+
return data;
127+
}
128+
}
129+
130+
export interface ISecondChild extends IBase {
131+
b: string;
132+
type: EBase;
133+
}
134+
135+
export class MyClass implements IMyClass {
136+
child: OneChild | SecondChild;
137+
children: (OneChild | SecondChild)[];
138+
139+
constructor(data?: IMyClass) {
140+
if (data) {
141+
for (var property in data) {
142+
if (data.hasOwnProperty(property))
143+
(<any>this)[property] = (<any>data)[property];
144+
}
145+
}
146+
}
147+
148+
init(_data?: any) {
149+
if (_data) {
150+
this.child = _data["Child"] ? OneChild | SecondChild.fromJS(_data["Child"]) : <any>undefined;
151+
if (Array.isArray(_data["Children"])) {
152+
this.children = [] as any;
153+
for (let item of _data["Children"])
154+
this.children.push(OneChild | SecondChild.fromJS(item));
155+
}
156+
}
157+
}
158+
159+
static fromJS(data: any): MyClass {
160+
data = typeof data === 'object' ? data : {};
161+
let result = new MyClass();
162+
result.init(data);
163+
return result;
164+
}
165+
166+
toJSON(data?: any) {
167+
data = typeof data === 'object' ? data : {};
168+
data["Child"] = this.child ? this.child.toJSON() : <any>undefined;
169+
if (Array.isArray(this.children)) {
170+
data["Children"] = [];
171+
for (let item of this.children)
172+
data["Children"].push(item.toJSON());
173+
}
174+
return data;
175+
}
176+
}
177+
178+
export interface IMyClass {
179+
child: OneChild | SecondChild;
180+
children: (OneChild | SecondChild)[];
181+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//----------------------
2+
// <auto-generated>
3+
// </auto-generated>
4+
//----------------------
5+
6+
7+
8+
9+
10+
11+
12+
export interface Base {
13+
Type: string;
14+
}
15+
16+
export interface OneChild extends Base {
17+
A: string;
18+
Type: EBase.OneChild;
19+
}
20+
21+
export enum EBase {
22+
OneChild = "OneChild",
23+
SecondChild = "SecondChild",
24+
}
25+
26+
export interface SecondChild extends Base {
27+
B: string;
28+
Type: EBase.SecondChild;
29+
}
30+
31+
export interface MyClass {
32+
Child: OneChild | SecondChild;
33+
Children: (OneChild | SecondChild)[];
34+
}

src/NJsonSchema.CodeGeneration.TypeScript.Tests/TypeScriptDiscriminatorTests.cs

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
using System.Threading.Tasks;
22
using Newtonsoft.Json;
3-
using NJsonSchema.Generation;
43
using System.Collections.Generic;
54
using System.Runtime.Serialization;
65
using Xunit;
76
using NJsonSchema.NewtonsoftJson.Converters;
87
using NJsonSchema.NewtonsoftJson.Generation;
8+
using VerifyXunit;
9+
using Newtonsoft.Json.Converters;
910

1011
namespace NJsonSchema.CodeGeneration.TypeScript.Tests
1112
{
13+
[UsesVerify]
1214
public class TypeScriptDiscriminatorTests
1315
{
1416
[JsonConverter(typeof(JsonInheritanceConverter), nameof(Type))]
@@ -19,6 +21,7 @@ public abstract class Base
1921
public abstract EBase Type { get; }
2022
}
2123

24+
[JsonConverter(typeof(StringEnumConverter))]
2225
public enum EBase
2326
{
2427
OneChild,
@@ -55,6 +58,7 @@ public async Task When_generating_interface_contract_add_discriminator()
5558
GenerateAbstractProperties = true,
5659
});
5760
var data = schema.ToJson();
61+
var json = JsonConvert.SerializeObject(new OneChild());
5862

5963
//// Act
6064
var generator = new TypeScriptGenerator(schema, new TypeScriptGeneratorSettings
@@ -66,8 +70,9 @@ public async Task When_generating_interface_contract_add_discriminator()
6670

6771
//// Assert
6872
Assert.Contains("export interface Base {\n Type: EBase;\n}", code);
73+
await VerifyHelper.Verify(code);
6974
}
70-
75+
7176
[Fact]
7277
public async Task When_generating_interface_contract_add_discriminator_string_literal()
7378
{
@@ -89,6 +94,7 @@ public async Task When_generating_interface_contract_add_discriminator_string_li
8994

9095
//// Assert
9196
Assert.Contains("export interface Base {\n Type: EBase;\n}", code);
97+
await VerifyHelper.Verify(code);
9298
}
9399

94100
[Fact]
@@ -112,8 +118,9 @@ public async Task When_parameter_is_abstract_then_generate_union_interface()
112118
Assert.Contains("export interface SecondChild extends Base", code);
113119
Assert.Contains("Child: OneChild | SecondChild;", code);
114120
Assert.Contains("Children: (OneChild | SecondChild)[];", code);
121+
await VerifyHelper.Verify(code);
115122
}
116-
123+
117124
[Fact]
118125
public async Task When_parameter_is_abstract_then_generate_union_class()
119126
{
@@ -135,6 +142,7 @@ public async Task When_parameter_is_abstract_then_generate_union_class()
135142
Assert.Contains("export class SecondChild extends Base", code);
136143
Assert.Contains("child: OneChild | SecondChild;", code);
137144
Assert.Contains("children: (OneChild | SecondChild)[];", code);
145+
await VerifyHelper.Verify(code);
138146
}
139147
}
140148
}

src/NJsonSchema.CodeGeneration.TypeScript/Models/EnumTemplateModel.cs

+1-3
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,7 @@ public List<EnumerationItemModel> Enums
6262
entries.Add(new EnumerationItemModel
6363
{
6464
Name = _settings.EnumNameGenerator.Generate(i, name, value, _schema),
65-
Value = _schema.Type.IsInteger() ?
66-
value.ToString() :
67-
(_settings.TypeScriptVersion < 2.4m ? "<any>" : "") + "\"" + value + "\"",
65+
Value = _schema.Type.IsInteger() ? value.ToString() : "\"" + value + "\"",
6866
});
6967
}
7068
}

0 commit comments

Comments
 (0)