@omnifyjp/ts 5.8.36 → 5.8.37

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.
@@ -18,7 +18,15 @@ export declare function getPropertyType(property: PropertyDefinition, allSchemas
18
18
  export declare function propertyToTSProperties(propertyName: string, property: PropertyDefinition, schema: SchemaDefinition, allSchemas: Record<string, SchemaDefinition>, options: GeneratorOptions): TSProperty[];
19
19
  /** Generate a TSInterface from a schema definition. */
20
20
  export declare function schemaToInterface(schema: SchemaDefinition, allSchemas: Record<string, SchemaDefinition>, options: GeneratorOptions): TSInterface;
21
- /** Format a TypeScript property line. */
21
+ /** Format a TypeScript property line.
22
+ *
23
+ * Nullable fields (DB column allows NULL) emit `field?: T | null` so
24
+ * consumers can accept the actual JSON value the backend serializes
25
+ * (`null`), not just an absent key. Mirrors the Go target's `*T`
26
+ * pointer convention — `*string` in Go ↔ `string | null` in TS.
27
+ * Pure-optional fields (e.g. payload DTOs that allow omitting) keep
28
+ * the `field?: T` shape. Issue #103 Phase 2 TS-Go nullability sync.
29
+ */
22
30
  export declare function formatProperty(property: TSProperty): string;
23
31
  /** Format a TypeScript interface. */
24
32
  export declare function formatInterface(iface: TSInterface): string;
@@ -129,6 +129,7 @@ export function propertyToTSProperties(propertyName, property, schema, allSchema
129
129
  name: col.name,
130
130
  type: tsType,
131
131
  optional: col.nullable ?? false,
132
+ nullable: col.nullable ?? false,
132
133
  readonly: isReadonly,
133
134
  });
134
135
  }
@@ -142,6 +143,7 @@ export function propertyToTSProperties(propertyName, property, schema, allSchema
142
143
  name: propertyName,
143
144
  type: tsType,
144
145
  optional: isNullable,
146
+ nullable: isNullable,
145
147
  readonly: isReadonly,
146
148
  }];
147
149
  }
@@ -185,10 +187,13 @@ export function propertyToTSProperties(propertyName, property, schema, allSchema
185
187
  name: `${propertyName}_id`,
186
188
  type: fkType,
187
189
  optional: isNullable,
190
+ nullable: isNullable,
188
191
  readonly: isReadonly,
189
192
  },
190
193
  {
191
194
  name: propertyName,
195
+ // Type already carries `| null` when nullable — don't double up
196
+ // by also setting nullable: true on the property.
192
197
  type: isNullable ? `${targetName} | null` : targetName,
193
198
  optional: true,
194
199
  readonly: isReadonly,
@@ -211,6 +216,7 @@ export function propertyToTSProperties(propertyName, property, schema, allSchema
211
216
  name: propertyName,
212
217
  type,
213
218
  optional: isNullable,
219
+ nullable: isNullable,
214
220
  readonly: isReadonly,
215
221
  }];
216
222
  }
@@ -252,13 +258,17 @@ export function schemaToInterface(schema, allSchemas, options) {
252
258
  properties.push(...propertyToTSProperties(propName, property, schema, allSchemas, options));
253
259
  }
254
260
  }
255
- // Timestamps
261
+ // Timestamps. created_at + updated_at are NOT NULL with DEFAULT
262
+ // CURRENT_TIMESTAMP — every persisted row has them. Mark optional in
263
+ // Create-DTO contexts (caller can omit, DB fills) but always present
264
+ // and never NULL on the read shape, so no `| null`.
256
265
  if (schema.options?.timestamps !== false) {
257
266
  properties.push({ name: 'created_at', type: 'DateTimeString', optional: true, readonly: false }, { name: 'updated_at', type: 'DateTimeString', optional: true, readonly: false });
258
267
  }
259
- // Soft delete
268
+ // Soft delete. deleted_at IS nullable (NULL until soft-deleted) — match
269
+ // Go's `*time.Time` pointer with TS `?: DateTimeString | null`.
260
270
  if (schema.options?.softDelete) {
261
- properties.push({ name: 'deleted_at', type: 'DateTimeString', optional: true, readonly: false });
271
+ properties.push({ name: 'deleted_at', type: 'DateTimeString', optional: true, nullable: true, readonly: false });
262
272
  }
263
273
  // Collect dependencies
264
274
  const dependencySet = new Set();
@@ -304,10 +314,24 @@ export function schemaToInterface(schema, allSchemas, options) {
304
314
  enumDependencies: enumDependencySet.size > 0 ? Array.from(enumDependencySet).sort() : undefined,
305
315
  };
306
316
  }
307
- /** Format a TypeScript property line. */
317
+ /** Format a TypeScript property line.
318
+ *
319
+ * Nullable fields (DB column allows NULL) emit `field?: T | null` so
320
+ * consumers can accept the actual JSON value the backend serializes
321
+ * (`null`), not just an absent key. Mirrors the Go target's `*T`
322
+ * pointer convention — `*string` in Go ↔ `string | null` in TS.
323
+ * Pure-optional fields (e.g. payload DTOs that allow omitting) keep
324
+ * the `field?: T` shape. Issue #103 Phase 2 TS-Go nullability sync.
325
+ */
308
326
  export function formatProperty(property) {
309
327
  const optional = property.optional ? '?' : '';
310
- return ` ${property.name}${optional}: ${property.type};`;
328
+ // Already-nullable type strings (e.g. someone hand-wrote `T | null`
329
+ // upstream) are passed through unchanged so we don't end up with
330
+ // `T | null | null`.
331
+ const typeStr = property.nullable && !/\| null\b/.test(property.type)
332
+ ? `${property.type} | null`
333
+ : property.type;
334
+ return ` ${property.name}${optional}: ${typeStr};`;
311
335
  }
312
336
  /** Format a TypeScript interface. */
313
337
  export function formatInterface(iface) {
package/dist/types.d.ts CHANGED
@@ -392,6 +392,14 @@ export interface TSProperty {
392
392
  readonly optional: boolean;
393
393
  readonly readonly: boolean;
394
394
  readonly comment?: string;
395
+ /**
396
+ * The underlying schema column is `nullable: true` (DB allows NULL),
397
+ * NOT just "optional in some payload shape". When set, formatProperty
398
+ * emits `field?: T | null` so consumers can accept the actual JSON
399
+ * value the backend serializes (`null`, not `undefined`). Mirrors the
400
+ * Go target's `*T` pointer convention. Phase 2 of issue #103.
401
+ */
402
+ readonly nullable?: boolean;
395
403
  }
396
404
  /** TypeScript interface definition. */
397
405
  export interface TSInterface {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnifyjp/ts",
3
- "version": "5.8.36",
3
+ "version": "5.8.37",
4
4
  "description": "TypeScript model type generator from Omnify schemas.json",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",