From 8c128acaed52c690cad6f030a091aeacc8915370 Mon Sep 17 00:00:00 2001 From: Stephan Boyer Date: Tue, 18 Jun 2024 19:22:20 -0700 Subject: [PATCH] Require optional struct fields to be explicitly set to `undefined` to avoid setting them --- CHANGELOG.md | 5 + Cargo.lock | 2 +- Cargo.toml | 2 +- src/generate_typescript.rs | 8 +- test_data/types.ts | 280 ++++++++++++++++++------------------- 5 files changed, 151 insertions(+), 146 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 843dd49c..86470674 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.11.0] - 2024-06-18 + +### Changed +- Typical now requires optional struct fields to be explicitly set to `undefined` in TypeScript to avoid setting them. Previously, such fields could be omitted entirely, but that meant typos in optional field names could go unnoticed. + ## [0.10.0] - 2024-06-13 ### Added diff --git a/Cargo.lock b/Cargo.lock index f17542b9..76128c8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -202,7 +202,7 @@ dependencies = [ [[package]] name = "typical" -version = "0.10.0" +version = "0.11.0" dependencies = [ "clap", "colored", diff --git a/Cargo.toml b/Cargo.toml index 5e5cc78c..7dcd82dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "typical" -version = "0.10.0" +version = "0.11.0" authors = ["Stephan Boyer "] edition = "2021" description = "Data interchange with algebraic data types." diff --git a/src/generate_typescript.rs b/src/generate_typescript.rs index 0a869c5d..7536210b 100644 --- a/src/generate_typescript.rs +++ b/src/generate_typescript.rs @@ -1367,20 +1367,20 @@ fn write_struct( for field in fields { write_indentation(buffer, indentation + 1)?; write_identifier(buffer, &field.name, Camel, None)?; + write!(buffer, ": ")?; + write_type(buffer, imports, namespace, &field.r#type.variant, direction)?; match field.rule { schema::Rule::Asymmetric => match direction { Direction::Atlas | Direction::Out => {} Direction::In => { - write!(buffer, "?")?; + write!(buffer, " | undefined")?; } }, schema::Rule::Optional => { - write!(buffer, "?")?; + write!(buffer, " | undefined")?; } schema::Rule::Required => {} } - write!(buffer, ": ")?; - write_type(buffer, imports, namespace, &field.r#type.variant, direction)?; writeln!(buffer, ";")?; } diff --git a/test_data/types.ts b/test_data/types.ts index 938a54b5..2e21d450 100644 --- a/test_data/types.ts +++ b/test_data/types.ts @@ -849,33 +849,33 @@ export namespace Comprehensive { yAsymmetric: { $size: number; $elements: { $size: number; $elements: Uint8Array[] }[] }; zAsymmetric: { $size: number; $elements: { $size: number; $elements: Comprehensive.Types.LocalStructAtlas[] }[] }; aaAsymmetric: { $size: number; $elements: { $size: number; $elements: Degenerate.Types.EmptyStructAtlas[] }[] }; - aOptional?: number; - bOptional?: number; - cOptional?: number; - dOptional?: number; - eOptional?: number; - fOptional?: number; - gOptional?: Uint8Array; - hOptional?: Comprehensive.Types.LocalStructAtlas; - iOptional?: Degenerate.Types.EmptyStructAtlas; - jOptional?: number; - kOptional?: number; - lOptional?: number; - mOptional?: number; - nOptional?: number; - oOptional?: { $size: number; $elements: number[] }; - pOptional?: { $size: number; $elements: Uint8Array[] }; - qOptional?: { $size: number; $elements: Comprehensive.Types.LocalStructAtlas[] }; - rOptional?: { $size: number; $elements: Degenerate.Types.EmptyStructAtlas[] }; - sOptional?: { $size: number; $elements: number[] }; - tOptional?: { $size: number; $elements: number[] }; - uOptional?: { $size: number; $elements: number[] }; - vOptional?: { $size: number; $elements: number[] }; - wOptional?: { $size: number; $elements: number[] }; - xOptional?: { $size: number; $elements: { $size: number; $elements: number[] }[] }; - yOptional?: { $size: number; $elements: { $size: number; $elements: Uint8Array[] }[] }; - zOptional?: { $size: number; $elements: { $size: number; $elements: Comprehensive.Types.LocalStructAtlas[] }[] }; - aaOptional?: { $size: number; $elements: { $size: number; $elements: Degenerate.Types.EmptyStructAtlas[] }[] }; + aOptional: number | undefined; + bOptional: number | undefined; + cOptional: number | undefined; + dOptional: number | undefined; + eOptional: number | undefined; + fOptional: number | undefined; + gOptional: Uint8Array | undefined; + hOptional: Comprehensive.Types.LocalStructAtlas | undefined; + iOptional: Degenerate.Types.EmptyStructAtlas | undefined; + jOptional: number | undefined; + kOptional: number | undefined; + lOptional: number | undefined; + mOptional: number | undefined; + nOptional: number | undefined; + oOptional: { $size: number; $elements: number[] } | undefined; + pOptional: { $size: number; $elements: Uint8Array[] } | undefined; + qOptional: { $size: number; $elements: Comprehensive.Types.LocalStructAtlas[] } | undefined; + rOptional: { $size: number; $elements: Degenerate.Types.EmptyStructAtlas[] } | undefined; + sOptional: { $size: number; $elements: number[] } | undefined; + tOptional: { $size: number; $elements: number[] } | undefined; + uOptional: { $size: number; $elements: number[] } | undefined; + vOptional: { $size: number; $elements: number[] } | undefined; + wOptional: { $size: number; $elements: number[] } | undefined; + xOptional: { $size: number; $elements: { $size: number; $elements: number[] }[] } | undefined; + yOptional: { $size: number; $elements: { $size: number; $elements: Uint8Array[] }[] } | undefined; + zOptional: { $size: number; $elements: { $size: number; $elements: Comprehensive.Types.LocalStructAtlas[] }[] } | undefined; + aaOptional: { $size: number; $elements: { $size: number; $elements: Degenerate.Types.EmptyStructAtlas[] }[] } | undefined; }; export type FooOut = { @@ -933,33 +933,33 @@ export namespace Comprehensive { yAsymmetric: string[][]; zAsymmetric: Comprehensive.Types.LocalStructOut[][]; aaAsymmetric: Degenerate.Types.EmptyStructOut[][]; - aOptional?: null; - bOptional?: number; - cOptional?: bigint; - dOptional?: bigint; - eOptional?: boolean; - fOptional?: ArrayBuffer; - gOptional?: string; - hOptional?: Comprehensive.Types.LocalStructOut; - iOptional?: Degenerate.Types.EmptyStructOut; - jOptional?: null[]; - kOptional?: number[]; - lOptional?: bigint[]; - mOptional?: bigint[]; - nOptional?: boolean[]; - oOptional?: ArrayBuffer[]; - pOptional?: string[]; - qOptional?: Comprehensive.Types.LocalStructOut[]; - rOptional?: Degenerate.Types.EmptyStructOut[]; - sOptional?: null[][]; - tOptional?: number[][]; - uOptional?: bigint[][]; - vOptional?: bigint[][]; - wOptional?: boolean[][]; - xOptional?: ArrayBuffer[][]; - yOptional?: string[][]; - zOptional?: Comprehensive.Types.LocalStructOut[][]; - aaOptional?: Degenerate.Types.EmptyStructOut[][]; + aOptional: null | undefined; + bOptional: number | undefined; + cOptional: bigint | undefined; + dOptional: bigint | undefined; + eOptional: boolean | undefined; + fOptional: ArrayBuffer | undefined; + gOptional: string | undefined; + hOptional: Comprehensive.Types.LocalStructOut | undefined; + iOptional: Degenerate.Types.EmptyStructOut | undefined; + jOptional: null[] | undefined; + kOptional: number[] | undefined; + lOptional: bigint[] | undefined; + mOptional: bigint[] | undefined; + nOptional: boolean[] | undefined; + oOptional: ArrayBuffer[] | undefined; + pOptional: string[] | undefined; + qOptional: Comprehensive.Types.LocalStructOut[] | undefined; + rOptional: Degenerate.Types.EmptyStructOut[] | undefined; + sOptional: null[][] | undefined; + tOptional: number[][] | undefined; + uOptional: bigint[][] | undefined; + vOptional: bigint[][] | undefined; + wOptional: boolean[][] | undefined; + xOptional: ArrayBuffer[][] | undefined; + yOptional: string[][] | undefined; + zOptional: Comprehensive.Types.LocalStructOut[][] | undefined; + aaOptional: Degenerate.Types.EmptyStructOut[][] | undefined; }; export type FooIn = { @@ -990,60 +990,60 @@ export namespace Comprehensive { yRequired: string[][]; zRequired: Comprehensive.Types.LocalStructIn[][]; aaRequired: Degenerate.Types.EmptyStructIn[][]; - aAsymmetric?: null; - bAsymmetric?: number; - cAsymmetric?: bigint; - dAsymmetric?: bigint; - eAsymmetric?: boolean; - fAsymmetric?: ArrayBuffer; - gAsymmetric?: string; - hAsymmetric?: Comprehensive.Types.LocalStructIn; - iAsymmetric?: Degenerate.Types.EmptyStructIn; - jAsymmetric?: null[]; - kAsymmetric?: number[]; - lAsymmetric?: bigint[]; - mAsymmetric?: bigint[]; - nAsymmetric?: boolean[]; - oAsymmetric?: ArrayBuffer[]; - pAsymmetric?: string[]; - qAsymmetric?: Comprehensive.Types.LocalStructIn[]; - rAsymmetric?: Degenerate.Types.EmptyStructIn[]; - sAsymmetric?: null[][]; - tAsymmetric?: number[][]; - uAsymmetric?: bigint[][]; - vAsymmetric?: bigint[][]; - wAsymmetric?: boolean[][]; - xAsymmetric?: ArrayBuffer[][]; - yAsymmetric?: string[][]; - zAsymmetric?: Comprehensive.Types.LocalStructIn[][]; - aaAsymmetric?: Degenerate.Types.EmptyStructIn[][]; - aOptional?: null; - bOptional?: number; - cOptional?: bigint; - dOptional?: bigint; - eOptional?: boolean; - fOptional?: ArrayBuffer; - gOptional?: string; - hOptional?: Comprehensive.Types.LocalStructIn; - iOptional?: Degenerate.Types.EmptyStructIn; - jOptional?: null[]; - kOptional?: number[]; - lOptional?: bigint[]; - mOptional?: bigint[]; - nOptional?: boolean[]; - oOptional?: ArrayBuffer[]; - pOptional?: string[]; - qOptional?: Comprehensive.Types.LocalStructIn[]; - rOptional?: Degenerate.Types.EmptyStructIn[]; - sOptional?: null[][]; - tOptional?: number[][]; - uOptional?: bigint[][]; - vOptional?: bigint[][]; - wOptional?: boolean[][]; - xOptional?: ArrayBuffer[][]; - yOptional?: string[][]; - zOptional?: Comprehensive.Types.LocalStructIn[][]; - aaOptional?: Degenerate.Types.EmptyStructIn[][]; + aAsymmetric: null | undefined; + bAsymmetric: number | undefined; + cAsymmetric: bigint | undefined; + dAsymmetric: bigint | undefined; + eAsymmetric: boolean | undefined; + fAsymmetric: ArrayBuffer | undefined; + gAsymmetric: string | undefined; + hAsymmetric: Comprehensive.Types.LocalStructIn | undefined; + iAsymmetric: Degenerate.Types.EmptyStructIn | undefined; + jAsymmetric: null[] | undefined; + kAsymmetric: number[] | undefined; + lAsymmetric: bigint[] | undefined; + mAsymmetric: bigint[] | undefined; + nAsymmetric: boolean[] | undefined; + oAsymmetric: ArrayBuffer[] | undefined; + pAsymmetric: string[] | undefined; + qAsymmetric: Comprehensive.Types.LocalStructIn[] | undefined; + rAsymmetric: Degenerate.Types.EmptyStructIn[] | undefined; + sAsymmetric: null[][] | undefined; + tAsymmetric: number[][] | undefined; + uAsymmetric: bigint[][] | undefined; + vAsymmetric: bigint[][] | undefined; + wAsymmetric: boolean[][] | undefined; + xAsymmetric: ArrayBuffer[][] | undefined; + yAsymmetric: string[][] | undefined; + zAsymmetric: Comprehensive.Types.LocalStructIn[][] | undefined; + aaAsymmetric: Degenerate.Types.EmptyStructIn[][] | undefined; + aOptional: null | undefined; + bOptional: number | undefined; + cOptional: bigint | undefined; + dOptional: bigint | undefined; + eOptional: boolean | undefined; + fOptional: ArrayBuffer | undefined; + gOptional: string | undefined; + hOptional: Comprehensive.Types.LocalStructIn | undefined; + iOptional: Degenerate.Types.EmptyStructIn | undefined; + jOptional: null[] | undefined; + kOptional: number[] | undefined; + lOptional: bigint[] | undefined; + mOptional: bigint[] | undefined; + nOptional: boolean[] | undefined; + oOptional: ArrayBuffer[] | undefined; + pOptional: string[] | undefined; + qOptional: Comprehensive.Types.LocalStructIn[] | undefined; + rOptional: Degenerate.Types.EmptyStructIn[] | undefined; + sOptional: null[][] | undefined; + tOptional: number[][] | undefined; + uOptional: bigint[][] | undefined; + vOptional: bigint[][] | undefined; + wOptional: boolean[][] | undefined; + xOptional: ArrayBuffer[][] | undefined; + yOptional: string[][] | undefined; + zOptional: Comprehensive.Types.LocalStructIn[][] | undefined; + aaOptional: Degenerate.Types.EmptyStructIn[][] | undefined; }; export namespace Foo { @@ -15422,43 +15422,43 @@ export namespace SchemaEvolution { $size: number; requiredToRequired: Uint8Array; requiredToAsymmetric: Uint8Array; - requiredToOptional?: Uint8Array; + requiredToOptional: Uint8Array | undefined; asymmetricToRequired: Uint8Array; asymmetricToAsymmetric: Uint8Array; - asymmetricToOptional?: Uint8Array; + asymmetricToOptional: Uint8Array | undefined; optionalToRequired: Uint8Array; optionalToAsymmetric: Uint8Array; - optionalToOptional?: Uint8Array; + optionalToOptional: Uint8Array | undefined; nonexistentToAsymmetric: number; - nonexistentToOptional?: number; + nonexistentToOptional: number | undefined; }; export type ExampleStructOut = { requiredToRequired: string; requiredToAsymmetric: string; - requiredToOptional?: string; + requiredToOptional: string | undefined; asymmetricToRequired: string; asymmetricToAsymmetric: string; - asymmetricToOptional?: string; + asymmetricToOptional: string | undefined; optionalToRequired: string; optionalToAsymmetric: string; - optionalToOptional?: string; + optionalToOptional: string | undefined; nonexistentToAsymmetric: null; - nonexistentToOptional?: null; + nonexistentToOptional: null | undefined; }; export type ExampleStructIn = { requiredToRequired: string; - requiredToAsymmetric?: string; - requiredToOptional?: string; + requiredToAsymmetric: string | undefined; + requiredToOptional: string | undefined; asymmetricToRequired: string; - asymmetricToAsymmetric?: string; - asymmetricToOptional?: string; + asymmetricToAsymmetric: string | undefined; + asymmetricToOptional: string | undefined; optionalToRequired: string; - optionalToAsymmetric?: string; - optionalToOptional?: string; - nonexistentToAsymmetric?: null; - nonexistentToOptional?: null; + optionalToAsymmetric: string | undefined; + optionalToOptional: string | undefined; + nonexistentToAsymmetric: null | undefined; + nonexistentToOptional: null | undefined; }; export namespace ExampleStruct { @@ -16631,10 +16631,10 @@ export namespace SchemaEvolution { asymmetricToAsymmetric: Uint8Array; asymmetricToOptional: Uint8Array; asymmetricToNonexistent: Uint8Array; - optionalToRequired?: Uint8Array; - optionalToAsymmetric?: Uint8Array; - optionalToOptional?: Uint8Array; - optionalToNonexistent?: Uint8Array; + optionalToRequired: Uint8Array | undefined; + optionalToAsymmetric: Uint8Array | undefined; + optionalToOptional: Uint8Array | undefined; + optionalToNonexistent: Uint8Array | undefined; }; export type ExampleStructOut = { @@ -16646,10 +16646,10 @@ export namespace SchemaEvolution { asymmetricToAsymmetric: string; asymmetricToOptional: string; asymmetricToNonexistent: string; - optionalToRequired?: string; - optionalToAsymmetric?: string; - optionalToOptional?: string; - optionalToNonexistent?: string; + optionalToRequired: string | undefined; + optionalToAsymmetric: string | undefined; + optionalToOptional: string | undefined; + optionalToNonexistent: string | undefined; }; export type ExampleStructIn = { @@ -16657,14 +16657,14 @@ export namespace SchemaEvolution { requiredToAsymmetric: string; requiredToOptional: string; requiredToNonexistent: string; - asymmetricToRequired?: string; - asymmetricToAsymmetric?: string; - asymmetricToOptional?: string; - asymmetricToNonexistent?: string; - optionalToRequired?: string; - optionalToAsymmetric?: string; - optionalToOptional?: string; - optionalToNonexistent?: string; + asymmetricToRequired: string | undefined; + asymmetricToAsymmetric: string | undefined; + asymmetricToOptional: string | undefined; + asymmetricToNonexistent: string | undefined; + optionalToRequired: string | undefined; + optionalToAsymmetric: string | undefined; + optionalToOptional: string | undefined; + optionalToNonexistent: string | undefined; }; export namespace ExampleStruct {