Skip to content

Commit 67405ec

Browse files
authored
Fixes in generated TypeScript code (#325)
* Fix incorrect type for TypeScript readers/writers of 'abstract' * Fix incorrect type for TypeScript writers of optional fields. It was working only in special cases such as `foo?: int`. * Eliminate the type alias `type Int = number` since it was more confusing than helpful. Occurrences of `Int` are replaced by `number /*int*/`.
1 parent 245d302 commit 67405ec

File tree

5 files changed

+104
-39
lines changed

5 files changed

+104
-39
lines changed

CHANGES.md

+7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
* atdpy: Support parametrized type definitions (#303)
22
* atdpy: Support options approximately by treating them as nullables (#320)
33
* atdts: Support parametrized type definitions (#303)
4+
* atdts: Fix incorrect type for TypeScript readers/writers generated
5+
for type `abstract`.
6+
* atdts: Fix incorrect type for TypeScript writers of optional fields.
7+
It was working only in special cases such as `foo?: int`.
8+
* atdts: Eliminate the type alias `type Int = number` since it was
9+
more confusing than helpful. Occurrences of `Int` are replaced
10+
by `number /*int*/`.
411

512
2.10.0 (2022-08-09)
613
-------------------

atdts/src/lib/Codegen.ml

+7-9
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,6 @@ let runtime_end = {|
197197
// Runtime library
198198
/////////////////////////////////////////////////////////////////////
199199

200-
export type Int = number
201-
202200
export type Option<T> = null | { value: T }
203201

204202
function _atd_missing_json_field(type_name: string, json_field_name: string) {
@@ -231,7 +229,7 @@ function _atd_bad_ts(expected_type: string, ts_value: any, context: any) {
231229
` Occurs in '${JSON.stringify(context)}'.`)
232230
}
233231

234-
function _atd_check_json_tuple(len: Int, x: any, context: any) {
232+
function _atd_check_json_tuple(len: number /*int*/, x: any, context: any) {
235233
if (! Array.isArray(x) || x.length !== len)
236234
_atd_bad_json('tuple of length ' + len, x, context);
237235
}
@@ -254,7 +252,7 @@ function _atd_read_bool(x: any, context: any): boolean {
254252
}
255253
}
256254

257-
function _atd_read_int(x: any, context: any): Int {
255+
function _atd_read_int(x: any, context: any): number /*int*/ {
258256
if (Number.isInteger(x))
259257
return x
260258
else {
@@ -435,7 +433,7 @@ function _atd_write_bool(x: any, context: any): boolean {
435433
}
436434
}
437435

438-
function _atd_write_int(x: any, context: any): Int {
436+
function _atd_write_int(x: any, context: any): number /*int*/ {
439437
if (Number.isInteger(x))
440438
return x
441439
else {
@@ -544,7 +542,7 @@ function _atd_write_required_field<T>(type_name: string,
544542
}
545543

546544
function _atd_write_optional_field<T>(write_elt: (x: T, context: any) => any,
547-
x: T,
545+
x: T | undefined,
548546
context: any): any {
549547
if (x === undefined || x === null)
550548
return x
@@ -596,7 +594,7 @@ let ts_type_name env (name : string) =
596594
match name with
597595
| "unit" -> "Null"
598596
| "bool" -> "boolean"
599-
| "int" -> "Int"
597+
| "int" -> "number /*int*/"
600598
| "float" -> "number"
601599
| "string" -> "string"
602600
| "abstract" -> "any"
@@ -708,7 +706,7 @@ let rec json_reader env e =
708706
| Name (loc, (loc2, name, []), an) ->
709707
(match name with
710708
| "bool" | "int" | "float" | "string" -> sprintf "_atd_read_%s" name
711-
| "abstract" -> "((x: any): any => x)"
709+
| "abstract" -> "((x: any, context): any => x)"
712710
| _ -> reader_name env name)
713711
| Name (loc, _, _) -> assert false
714712
| Tvar (loc, _) -> not_implemented loc "type variables"
@@ -756,7 +754,7 @@ let rec json_writer env e =
756754
| Name (loc, (loc2, name, []), an) ->
757755
(match name with
758756
| "bool" | "int" | "float" | "string" -> sprintf "_atd_write_%s" name
759-
| "abstract" -> "((x: any): any => x)"
757+
| "abstract" -> "((x: any, context): any => x)"
760758
| _ -> writer_name env name)
761759
| Name (loc, _, _) -> not_implemented loc "parametrized types"
762760
| Tvar (loc, _) -> not_implemented loc "type variables"

atdts/test/atd-input/everything.atd

+13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
(*
2+
ATD type definitions for testing their translation to TypeScript.
3+
*)
4+
5+
(* Testing raw JSON. Should map to type 'any'. *)
6+
type anything = abstract
7+
18
type different_kinds_of_things = [
29
| Root (* class name conflict *)
310
| Thing of int
@@ -15,11 +22,16 @@ type ('a, 'b) parametrized_record = {
1522

1623
type 'a parametrized_tuple = ('a * 'a * int)
1724

25+
type simple_record = {
26+
str: string;
27+
}
28+
1829
type root = {
1930
id <json name="ID">: string;
2031
this: this;
2132
items: int list list;
2233
?maybe: int option;
34+
?maybe2: simple_record option;
2335
~extras: int list;
2436
~answer <ts default="42">: int;
2537
aliased: alias;
@@ -35,6 +47,7 @@ type root = {
3547
foo: foo nullable;
3648
parametrized_record: (int, float) parametrized_record;
3749
parametrized_tuple: different_kinds_of_things parametrized_tuple;
50+
anything: anything;
3851
}
3952

4053
type alias = int list

atdts/test/ts-expected/everything.ts

+57-27
Original file line numberDiff line numberDiff line change
@@ -8,50 +8,66 @@
88
// of type 'Foo'.
99

1010

11+
export type Anything = any
12+
1113
export type DifferentKindsOfThings =
1214
| { kind: 'Root' }
13-
| { kind: 'Thing'; value: Int }
15+
| { kind: 'Thing'; value: number /*int*/ }
1416
| { kind: 'WOW' /* JSON: "wow" */ }
1517
| { kind: 'Amaze' /* JSON: "!!!" */; value: string[] }
1618

17-
export type This = Int
19+
export type This = number /*int*/
20+
21+
export type SimpleRecord = {
22+
str: string;
23+
}
1824

1925
export type Root = {
2026
id: string;
2127
this_: This;
22-
items: Int[][];
23-
maybe?: Int;
24-
extras: Int[];
25-
answer: Int;
28+
items: number /*int*/[][];
29+
maybe?: number /*int*/;
30+
maybe2?: SimpleRecord;
31+
extras: number /*int*/[];
32+
answer: number /*int*/;
2633
aliased: Alias;
2734
point: [number, number];
2835
kinds: DifferentKindsOfThings[];
29-
assoc1: [number, Int][];
30-
assoc2: [string, Int][];
31-
assoc3: Map<number, Int>;
32-
assoc4: Map<string, Int>;
33-
options: Option<Int>[];
34-
nullables: (Int | null)[];
36+
assoc1: [number, number /*int*/][];
37+
assoc2: [string, number /*int*/][];
38+
assoc3: Map<number, number /*int*/>;
39+
assoc4: Map<string, number /*int*/>;
40+
options: Option<number /*int*/>[];
41+
nullables: (number /*int*/ | null)[];
3542
untyped_things: any[];
3643
foo: (Foo | null);
3744
parametrized_record: IntFloatParametrizedRecord;
3845
parametrized_tuple: TupleE1a4b40;
46+
anything: Anything;
3947
}
4048

41-
export type Alias = Int[]
49+
export type Alias = number /*int*/[]
4250

43-
export type Pair = [string, Int]
51+
export type Pair = [string, number /*int*/]
4452

4553
export type Foo = {
4654
foo: string;
4755
}
4856

4957
export type IntFloatParametrizedRecord = {
50-
field_a: Int;
58+
field_a: number /*int*/;
5159
field_b: number[];
5260
}
5361

54-
export type TupleE1a4b40 = [DifferentKindsOfThings, DifferentKindsOfThings, Int]
62+
export type TupleE1a4b40 = [DifferentKindsOfThings, DifferentKindsOfThings, number /*int*/]
63+
64+
export function writeAnything(x: Anything, context: any = x): any {
65+
return ((x: any, context): any => x)(x, context);
66+
}
67+
68+
export function readAnything(x: any, context: any = x): Anything {
69+
return ((x: any, context): any => x)(x, context);
70+
}
5571

5672
export function writeDifferentKindsOfThings(x: DifferentKindsOfThings, context: any = x): any {
5773
switch (x.kind) {
@@ -100,12 +116,25 @@ export function readThis(x: any, context: any = x): This {
100116
return _atd_read_int(x, context);
101117
}
102118

119+
export function writeSimpleRecord(x: SimpleRecord, context: any = x): any {
120+
return {
121+
'str': _atd_write_required_field('SimpleRecord', 'str', _atd_write_string, x.str, x),
122+
};
123+
}
124+
125+
export function readSimpleRecord(x: any, context: any = x): SimpleRecord {
126+
return {
127+
str: _atd_read_required_field('SimpleRecord', 'str', _atd_read_string, x['str'], x),
128+
};
129+
}
130+
103131
export function writeRoot(x: Root, context: any = x): any {
104132
return {
105133
'ID': _atd_write_required_field('Root', 'id', _atd_write_string, x.id, x),
106134
'this': _atd_write_required_field('Root', 'this', writeThis, x.this_, x),
107135
'items': _atd_write_required_field('Root', 'items', _atd_write_array(_atd_write_array(_atd_write_int)), x.items, x),
108136
'maybe': _atd_write_optional_field(_atd_write_int, x.maybe, x),
137+
'maybe2': _atd_write_optional_field(writeSimpleRecord, x.maybe2, x),
109138
'extras': _atd_write_field_with_default(_atd_write_array(_atd_write_int), [], x.extras, x),
110139
'answer': _atd_write_field_with_default(_atd_write_int, 42, x.answer, x),
111140
'aliased': _atd_write_required_field('Root', 'aliased', writeAlias, x.aliased, x),
@@ -117,10 +146,11 @@ export function writeRoot(x: Root, context: any = x): any {
117146
'assoc4': _atd_write_required_field('Root', 'assoc4', _atd_write_assoc_map_to_object(_atd_write_int), x.assoc4, x),
118147
'options': _atd_write_field_with_default(_atd_write_array(_atd_write_option(_atd_write_int)), [], x.options, x),
119148
'nullables': _atd_write_field_with_default(_atd_write_array(_atd_write_nullable(_atd_write_int)), [], x.nullables, x),
120-
'untyped_things': _atd_write_required_field('Root', 'untyped_things', _atd_write_array(((x: any): any => x)), x.untyped_things, x),
149+
'untyped_things': _atd_write_required_field('Root', 'untyped_things', _atd_write_array(((x: any, context): any => x)), x.untyped_things, x),
121150
'foo': _atd_write_required_field('Root', 'foo', _atd_write_nullable(writeFoo), x.foo, x),
122151
'parametrized_record': _atd_write_required_field('Root', 'parametrized_record', writeIntFloatParametrizedRecord, x.parametrized_record, x),
123152
'parametrized_tuple': _atd_write_required_field('Root', 'parametrized_tuple', writeTupleE1a4b40, x.parametrized_tuple, x),
153+
'anything': _atd_write_required_field('Root', 'anything', writeAnything, x.anything, x),
124154
};
125155
}
126156

@@ -130,21 +160,23 @@ export function readRoot(x: any, context: any = x): Root {
130160
this_: _atd_read_required_field('Root', 'this', readThis, x['this'], x),
131161
items: _atd_read_required_field('Root', 'items', _atd_read_array(_atd_read_array(_atd_read_int)), x['items'], x),
132162
maybe: _atd_read_optional_field(_atd_read_int, x['maybe'], x),
163+
maybe2: _atd_read_optional_field(readSimpleRecord, x['maybe2'], x),
133164
extras: _atd_read_field_with_default(_atd_read_array(_atd_read_int), [], x['extras'], x),
134165
answer: _atd_read_field_with_default(_atd_read_int, 42, x['answer'], x),
135166
aliased: _atd_read_required_field('Root', 'aliased', readAlias, x['aliased'], x),
136167
point: _atd_read_required_field('Root', 'point', ((x, context): [number, number] => { _atd_check_json_tuple(2, x, context); return [_atd_read_float(x[0], x), _atd_read_float(x[1], x)] }), x['point'], x),
137168
kinds: _atd_read_required_field('Root', 'kinds', _atd_read_array(readDifferentKindsOfThings), x['kinds'], x),
138-
assoc1: _atd_read_required_field('Root', 'assoc1', _atd_read_array(((x, context): [number, Int] => { _atd_check_json_tuple(2, x, context); return [_atd_read_float(x[0], x), _atd_read_int(x[1], x)] })), x['assoc1'], x),
169+
assoc1: _atd_read_required_field('Root', 'assoc1', _atd_read_array(((x, context): [number, number /*int*/] => { _atd_check_json_tuple(2, x, context); return [_atd_read_float(x[0], x), _atd_read_int(x[1], x)] })), x['assoc1'], x),
139170
assoc2: _atd_read_required_field('Root', 'assoc2', _atd_read_assoc_object_into_array(_atd_read_int), x['assoc2'], x),
140171
assoc3: _atd_read_required_field('Root', 'assoc3', _atd_read_assoc_array_into_map(_atd_read_float, _atd_read_int), x['assoc3'], x),
141172
assoc4: _atd_read_required_field('Root', 'assoc4', _atd_read_assoc_object_into_map(_atd_read_int), x['assoc4'], x),
142173
options: _atd_read_field_with_default(_atd_read_array(_atd_read_option(_atd_read_int)), [], x['options'], x),
143174
nullables: _atd_read_field_with_default(_atd_read_array(_atd_read_nullable(_atd_read_int)), [], x['nullables'], x),
144-
untyped_things: _atd_read_required_field('Root', 'untyped_things', _atd_read_array(((x: any): any => x)), x['untyped_things'], x),
175+
untyped_things: _atd_read_required_field('Root', 'untyped_things', _atd_read_array(((x: any, context): any => x)), x['untyped_things'], x),
145176
foo: _atd_read_required_field('Root', 'foo', _atd_read_nullable(readFoo), x['foo'], x),
146177
parametrized_record: _atd_read_required_field('Root', 'parametrized_record', readIntFloatParametrizedRecord, x['parametrized_record'], x),
147178
parametrized_tuple: _atd_read_required_field('Root', 'parametrized_tuple', readTupleE1a4b40, x['parametrized_tuple'], x),
179+
anything: _atd_read_required_field('Root', 'anything', readAnything, x['anything'], x),
148180
};
149181
}
150182

@@ -161,7 +193,7 @@ export function writePair(x: Pair, context: any = x): any {
161193
}
162194

163195
export function readPair(x: any, context: any = x): Pair {
164-
return ((x, context): [string, Int] => { _atd_check_json_tuple(2, x, context); return [_atd_read_string(x[0], x), _atd_read_int(x[1], x)] })(x, context);
196+
return ((x, context): [string, number /*int*/] => { _atd_check_json_tuple(2, x, context); return [_atd_read_string(x[0], x), _atd_read_int(x[1], x)] })(x, context);
165197
}
166198

167199
export function writeFoo(x: Foo, context: any = x): any {
@@ -195,16 +227,14 @@ export function writeTupleE1a4b40(x: TupleE1a4b40, context: any = x): any {
195227
}
196228

197229
export function readTupleE1a4b40(x: any, context: any = x): TupleE1a4b40 {
198-
return ((x, context): [DifferentKindsOfThings, DifferentKindsOfThings, Int] => { _atd_check_json_tuple(3, x, context); return [readDifferentKindsOfThings(x[0], x), readDifferentKindsOfThings(x[1], x), _atd_read_int(x[2], x)] })(x, context);
230+
return ((x, context): [DifferentKindsOfThings, DifferentKindsOfThings, number /*int*/] => { _atd_check_json_tuple(3, x, context); return [readDifferentKindsOfThings(x[0], x), readDifferentKindsOfThings(x[1], x), _atd_read_int(x[2], x)] })(x, context);
199231
}
200232

201233

202234
/////////////////////////////////////////////////////////////////////
203235
// Runtime library
204236
/////////////////////////////////////////////////////////////////////
205237

206-
export type Int = number
207-
208238
export type Option<T> = null | { value: T }
209239

210240
function _atd_missing_json_field(type_name: string, json_field_name: string) {
@@ -237,7 +267,7 @@ function _atd_bad_ts(expected_type: string, ts_value: any, context: any) {
237267
` Occurs in '${JSON.stringify(context)}'.`)
238268
}
239269

240-
function _atd_check_json_tuple(len: Int, x: any, context: any) {
270+
function _atd_check_json_tuple(len: number /*int*/, x: any, context: any) {
241271
if (! Array.isArray(x) || x.length !== len)
242272
_atd_bad_json('tuple of length ' + len, x, context);
243273
}
@@ -260,7 +290,7 @@ function _atd_read_bool(x: any, context: any): boolean {
260290
}
261291
}
262292

263-
function _atd_read_int(x: any, context: any): Int {
293+
function _atd_read_int(x: any, context: any): number /*int*/ {
264294
if (Number.isInteger(x))
265295
return x
266296
else {
@@ -441,7 +471,7 @@ function _atd_write_bool(x: any, context: any): boolean {
441471
}
442472
}
443473

444-
function _atd_write_int(x: any, context: any): Int {
474+
function _atd_write_int(x: any, context: any): number /*int*/ {
445475
if (Number.isInteger(x))
446476
return x
447477
else {
@@ -550,7 +580,7 @@ function _atd_write_required_field<T>(type_name: string,
550580
}
551581

552582
function _atd_write_optional_field<T>(write_elt: (x: T, context: any) => any,
553-
x: T,
583+
x: T | undefined,
554584
context: any): any {
555585
if (x === undefined || x === null)
556586
return x

atdts/test/ts-tests/test_atdts.ts

+20-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import * as API from "./everything"
44
import * as fs from "fs"
55

66
// Check that types are exported
7-
const a: API.Int = 0
8-
const b: API.Option<string> = null
7+
const aaa: API.Option<string> = null
98

109
function assert(is_true: boolean, errmsg: string) {
1110
if (!is_true) {
@@ -69,6 +68,10 @@ function test_everything() {
6968
{ kind: 'WOW' },
7069
100
7170
],
71+
anything: {
72+
any_a: 1234,
73+
any_b: [ [], [[[]]], true, {}, null ]
74+
},
7275
}
7376
const a_str = JSON.stringify(API.writeRoot(a_obj), null, 2)
7477
save('a_str', a_str)
@@ -183,7 +186,21 @@ ${a_str}`
183186
"wow",
184187
"wow",
185188
100
186-
]
189+
],
190+
"anything": {
191+
"any_a": 1234,
192+
"any_b": [
193+
[],
194+
[
195+
[
196+
[]
197+
]
198+
],
199+
true,
200+
{},
201+
null
202+
]
203+
}
187204
}`
188205
save('b_str', b_str)
189206
const b_obj = API.readRoot(JSON.parse(a_str))

0 commit comments

Comments
 (0)