Skip to content

Commit 1d6e35a

Browse files
authored
fix!: typescript date mapping (#2393)
1 parent c192e80 commit 1d6e35a

File tree

9 files changed

+83
-28
lines changed

9 files changed

+83
-28
lines changed

docs/migrations/version-5-to-6.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,33 @@ const models = await generator.generateCompleteModels(schema, {
5252

5353
The generated Python code behavior remains unchanged - all imports will continue to use the explicit style.
5454

55+
## Typescript
56+
### Date formats now generate `Date` instead of `string`
57+
58+
In Modelina v5, OpenAPI schema fields with type `string` and formats such as
59+
`date-time`, `date`, or `time` were generated as `string` in TypeScript models.
60+
61+
Starting from v6, these formats are now mapped to the native `Date` type.
62+
63+
This is a **breaking change** and may require updates in consumer code.
64+
65+
#### What changed
66+
67+
**Before (v5):**
68+
```ts
69+
sentAt?: string;
70+
```
71+
72+
**After(v6):**
73+
```ts
74+
sentAt?: Date;
75+
```
76+
#### Migration notes
77+
78+
- Update TypeScript type annotations that previously expected `string`
79+
- Ensure any custom serialization or parsing logic handles `Date` objects
80+
- Update mocks, tests, and fixtures that rely on string values for date fields
81+
5582
## AsyncAPI
5683

5784
### Improved schema naming strategy

examples/integrate-with-next/package-lock.json

Lines changed: 0 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/integrate-with-next/tests/__snapshots__/index.test.tsx.snap

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@ exports[`Jest Snapshot testing suite Matches DOM Snapshot 1`] = `
2929
class LightMeasured {
3030
private _id?: number;
3131
private _lumens?: number;
32-
private _sentAt?: string;
32+
private _sentAt?: Date;
3333
private _additionalProperties?: Map<string, any>;
3434
3535
constructor(input: {
3636
id?: number,
3737
lumens?: number,
38-
sentAt?: string,
38+
sentAt?: Date,
3939
additionalProperties?: Map<string, any>,
4040
}) {
4141
this._id = input.id;
@@ -50,8 +50,8 @@ class LightMeasured {
5050
get lumens(): number | undefined { return this._lumens; }
5151
set lumens(lumens: number | undefined) { this._lumens = lumens; }
5252
53-
get sentAt(): string | undefined { return this._sentAt; }
54-
set sentAt(sentAt: string | undefined) { this._sentAt = sentAt; }
53+
get sentAt(): Date | undefined { return this._sentAt; }
54+
set sentAt(sentAt: Date | undefined) { this._sentAt = sentAt; }
5555
5656
get additionalProperties(): Map<string, any> | undefined { return this._additionalProperties; }
5757
set additionalProperties(additionalProperties: Map<string, any> | undefined) { this._additionalProperties = additionalProperties; }
@@ -77,12 +77,12 @@ export { LightMeasured };
7777
7878
class TurnOn {
7979
private _id?: number;
80-
private _sentAt?: string;
80+
private _sentAt?: Date;
8181
private _additionalProperties?: Map<string, any>;
8282
8383
constructor(input: {
8484
id?: number,
85-
sentAt?: string,
85+
sentAt?: Date,
8686
additionalProperties?: Map<string, any>,
8787
}) {
8888
this._id = input.id;
@@ -93,8 +93,8 @@ class TurnOn {
9393
get id(): number | undefined { return this._id; }
9494
set id(id: number | undefined) { this._id = id; }
9595
96-
get sentAt(): string | undefined { return this._sentAt; }
97-
set sentAt(sentAt: string | undefined) { this._sentAt = sentAt; }
96+
get sentAt(): Date | undefined { return this._sentAt; }
97+
set sentAt(sentAt: Date | undefined) { this._sentAt = sentAt; }
9898
9999
get additionalProperties(): Map<string, any> | undefined { return this._additionalProperties; }
100100
set additionalProperties(additionalProperties: Map<string, any> | undefined) { this._additionalProperties = additionalProperties; }

package-lock.json

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"dependencies": {
3838
"@apidevtools/json-schema-ref-parser": "^11.1.0",
3939
"@apidevtools/swagger-parser": "^10.1.0",
40+
"@asyncapi/modelina": "^5.10.1",
4041
"@asyncapi/multi-parser": "^2.2.0",
4142
"@asyncapi/parser": "^3.4.0",
4243
"alterschema": "^1.1.2",

src/generators/typescript/TypeScriptConstrainer.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ export const TypeScriptDefaultTypeMapping: TypeScriptTypeMapping = {
3131
return applyNullable(constrainedModel, 'number');
3232
},
3333
String({ constrainedModel }): string {
34+
const format = constrainedModel?.options?.format;
35+
if (format === 'date-time' || format === 'date' || format === 'time') {
36+
return applyNullable(constrainedModel, 'Date');
37+
}
3438
return applyNullable(constrainedModel, 'string');
3539
},
3640
Boolean({ constrainedModel }): string {

test/generators/typescript/__snapshots__/TypeScriptGenerator.spec.ts.snap

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,15 @@ Array [
9797
private _specversion: string;
9898
private _reservedType: CloudEventType.DOG = CloudEventType.DOG;
9999
private _dataschema?: string;
100-
private _time?: string;
100+
private _time?: Date;
101101
private _additionalProperties?: Map<string, any>;
102102
103103
constructor(input: {
104104
id: string,
105105
source: string,
106106
specversion: string,
107107
dataschema?: string,
108-
time?: string,
108+
time?: Date,
109109
additionalProperties?: Map<string, any>,
110110
}) {
111111
this._id = input.id;
@@ -130,8 +130,8 @@ Array [
130130
get dataschema(): string | undefined { return this._dataschema; }
131131
set dataschema(dataschema: string | undefined) { this._dataschema = dataschema; }
132132
133-
get time(): string | undefined { return this._time; }
134-
set time(time: string | undefined) { this._time = time; }
133+
get time(): Date | undefined { return this._time; }
134+
set time(time: Date | undefined) { this._time = time; }
135135
136136
get additionalProperties(): Map<string, any> | undefined { return this._additionalProperties; }
137137
set additionalProperties(additionalProperties: Map<string, any> | undefined) { this._additionalProperties = additionalProperties; }
@@ -146,15 +146,15 @@ Array [
146146
private _specversion: string;
147147
private _reservedType: CloudEventType.CAT = CloudEventType.CAT;
148148
private _dataschema?: string;
149-
private _time?: string;
149+
private _time?: Date;
150150
private _additionalProperties?: Map<string, any>;
151151
152152
constructor(input: {
153153
id: string,
154154
source: string,
155155
specversion: string,
156156
dataschema?: string,
157-
time?: string,
157+
time?: Date,
158158
additionalProperties?: Map<string, any>,
159159
}) {
160160
this._id = input.id;
@@ -179,8 +179,8 @@ Array [
179179
get dataschema(): string | undefined { return this._dataschema; }
180180
set dataschema(dataschema: string | undefined) { this._dataschema = dataschema; }
181181
182-
get time(): string | undefined { return this._time; }
183-
set time(time: string | undefined) { this._time = time; }
182+
get time(): Date | undefined { return this._time; }
183+
set time(time: Date | undefined) { this._time = time; }
184184
185185
get additionalProperties(): Map<string, any> | undefined { return this._additionalProperties; }
186186
set additionalProperties(additionalProperties: Map<string, any> | undefined) { this._additionalProperties = additionalProperties; }
@@ -377,13 +377,13 @@ Array [
377377
"class ReservedEvent {
378378
private _id: string;
379379
private _action?: Action;
380-
private _eventTime?: string;
380+
private _eventTime?: Date;
381381
private _additionalProperties?: Map<string, any>;
382382
383383
constructor(input: {
384384
id: string,
385385
action?: Action,
386-
eventTime?: string,
386+
eventTime?: Date,
387387
additionalProperties?: Map<string, any>,
388388
}) {
389389
this._id = input.id;
@@ -398,8 +398,8 @@ Array [
398398
get action(): Action | undefined { return this._action; }
399399
set action(action: Action | undefined) { this._action = action; }
400400
401-
get eventTime(): string | undefined { return this._eventTime; }
402-
set eventTime(eventTime: string | undefined) { this._eventTime = eventTime; }
401+
get eventTime(): Date | undefined { return this._eventTime; }
402+
set eventTime(eventTime: Date | undefined) { this._eventTime = eventTime; }
403403
404404
get additionalProperties(): Map<string, any> | undefined { return this._additionalProperties; }
405405
set additionalProperties(additionalProperties: Map<string, any> | undefined) { this._additionalProperties = additionalProperties; }

test/runtime/generic-input-all.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
"string_type": {
77
"type": "string"
88
},
9+
"createdAt": {
10+
"type": "string",
11+
"format": "date-time"
12+
},
913
"number_type": {
1014
"type": "number"
1115
},

test/runtime/runtime-typescript/test/Marshalling.spec.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ describe('Marshalling', () => {
99
test: 'test'
1010
});
1111
const testObject = new TestObject({
12-
stringType: 'test',
12+
createdAt: new Date('2023-01-01T10:00:00Z'),
13+
stringType: 'test',
1314
numberType: 1,
1415
booleanType: true,
1516
arrayType: [1, 'test'],
@@ -18,12 +19,13 @@ describe('Marshalling', () => {
1819
additionalProperties: new Map(Object.entries({"test": "test"})),
1920
enumType: EnumType.CURLYLEFT_QUOTATION_TEST_QUOTATION_COLON_2_CURLYRIGHT,
2021
tupleType: ['test', 1],
21-
unionType: 'test'
22+
unionType: 'test',
2223
});
2324
test('be able to serialize model', () => {
2425
const serialized = testObject.marshal();
25-
expect(serialized).toEqual("{\"string_type\": \"test\",\"number_type\": 1,\"boolean_type\": true,\"union_type\": \"test\",\"array_type\": [1,\"test\"],\"tuple_type\": [\"test\",1],\"object_type\": {\"test\": \"test\"},\"dictionary_type\": {},\"enum_type\": \"{\\\"test\\\":2}\",\"test\": \"test\"}");
26-
});
26+
expect(serialized).toEqual(
27+
"{\"string_type\": \"test\",\"createdAt\": \"2023-01-01T10:00:00.000Z\",\"number_type\": 1,\"boolean_type\": true,\"union_type\": \"test\",\"array_type\": [1,\"test\"],\"tuple_type\": [\"test\",1],\"object_type\": {\"test\": \"test\"},\"dictionary_type\": {},\"enum_type\": \"{\\\"test\\\":2}\",\"test\": \"test\"}"
28+
);});
2729
test('be able to serialize model and turning it back to a model with the same values', () => {
2830
const serialized = testObject.marshal();
2931
const newAddress = TestObject.unmarshal(serialized);

0 commit comments

Comments
 (0)