Repository: 1Password/typeshare (rust)
Issue: Typescript: `unit` seems to be wrongly defined as `undefined` in Typescript
Hi,

I've the following Rust code:

```rust
#[derive(Debug, serde::Serialize)]
#[typeshare::typeshare]
#[serde(tag = "type", content = "content")]
pub enum Cmd {
    StartBrowser(StartBrowser),
}

#[derive(Debug, serde::Serialize)]
#[typeshare::typeshare]
pub struct StartBrowser(pub ());
```

...which generates the following Typescript code:

```typescript
export type StartBrowser = undefined;

export type Cmd = 
	| { type: "StartBrowser", content: StartBrowser };
```

...however, `serde_json` serializes `Cmd::StartBrowser(StartBrowser(()))` as `{"type":"StartBrowser","content":null}` (please note the `null` instead of the above defined `undefined`).

I'm not 100% sure, but I believe `serde_json` uses `undefined` to represent optional values (i.e., Rust's `Option` type).
Pull request description: Generated Typescript now uses `null` to represent unit types
`()` is represented as `null` instead of `undefined` - this also applies to unit enum variants.

Fixes #43.

Patch:
diff --git a/core/src/language/typescript.rs b/core/src/language/typescript.rs
index f487611..c337753 100644
--- a/core/src/language/typescript.rs
+++ b/core/src/language/typescript.rs
@@ -41,7 +41,7 @@ impl Language for TypeScript {
                 },
                 self.format_type(rtype2, generic_types)?
             )),
-            SpecialRustType::Unit => Ok("undefined".into()),
+            SpecialRustType::Unit => Ok("null".into()),
             SpecialRustType::String => Ok("string".into()),
             SpecialRustType::I8
             | SpecialRustType::U8
@@ -177,7 +177,7 @@ impl TypeScript {
                 match v {
                     RustEnumVariant::Unit(shared) => write!(
                         w,
-                        "\t| {{ {}: {:?}, {}?: undefined }}",
+                        "\t| {{ {}: {:?}, {}: null }}",
                         tag_key, shared.id.renamed, content_key
                     ),
                     RustEnumVariant::Tuple { ty, shared } => {


Test patch:
diff --git a/core/data/tests/boxed_value/output.ts b/core/data/tests/boxed_value/output.ts
index 7c8a223..0b45e70 100644
--- a/core/data/tests/boxed_value/output.ts
+++ b/core/data/tests/boxed_value/output.ts
@@ -1,6 +1,6 @@
 /** This is a comment. */
 export type Colors = 
-	| { type: "Red", content?: undefined }
-	| { type: "Blue", content?: undefined }
+	| { type: "Red", content: null }
+	| { type: "Blue", content: null }
 	| { type: "Green", content: string };
 
diff --git a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.ts b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.ts
index 3b48f75..0ef9293 100644
--- a/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.ts
+++ b/core/data/tests/can_generate_algebraic_enum_with_skipped_variants/output.ts
@@ -1,4 +1,4 @@
 export type SomeEnum = 
-	| { type: "A", content?: undefined }
+	| { type: "A", content: null }
 	| { type: "C", content: number };
 
diff --git a/core/data/tests/can_generate_empty_algebraic_enum/output.ts b/core/data/tests/can_generate_empty_algebraic_enum/output.ts
index 10b3ce7..3d2273f 100644
--- a/core/data/tests/can_generate_empty_algebraic_enum/output.ts
+++ b/core/data/tests/can_generate_empty_algebraic_enum/output.ts
@@ -3,5 +3,5 @@ export interface AddressDetails {
 
 export type Address = 
 	| { type: "FixedAddress", content: AddressDetails }
-	| { type: "NoFixedAddress", content?: undefined };
+	| { type: "NoFixedAddress", content: null };
 
diff --git a/core/data/tests/can_generate_generic_struct/output.ts b/core/data/tests/can_generate_generic_struct/output.ts
index 38a129b..0b6bc2c 100644
--- a/core/data/tests/can_generate_generic_struct/output.ts
+++ b/core/data/tests/can_generate_generic_struct/output.ts
@@ -13,5 +13,5 @@ export type EnumUsingGenericStruct =
 	| { type: "VariantA", content: GenericStruct<string, number> }
 	| { type: "VariantB", content: GenericStruct<string, number> }
 	| { type: "VariantC", content: GenericStruct<string, boolean> }
-	| { type: "VariantD", content: GenericStructUsingGenericStruct<undefined> };
+	| { type: "VariantD", content: GenericStructUsingGenericStruct<null> };
 
diff --git a/core/data/tests/can_handle_anonymous_struct/output.ts b/core/data/tests/can_handle_anonymous_struct/output.ts
index 4417ec5..b33267f 100644
--- a/core/data/tests/can_handle_anonymous_struct/output.ts
+++ b/core/data/tests/can_handle_anonymous_struct/output.ts
@@ -15,13 +15,13 @@ export type AutofilledBy =
 
 /** This is a comment (yareek sameek wuz here) */
 export type EnumWithManyVariants = 
-	| { type: "UnitVariant", content?: undefined }
+	| { type: "UnitVariant", content: null }
 	| { type: "TupleVariantString", content: string }
 	| { type: "AnonVariant", content: {
 	uuid: string;
 }}
 	| { type: "TupleVariantInt", content: number }
-	| { type: "AnotherUnitVariant", content?: undefined }
+	| { type: "AnotherUnitVariant", content: null }
 	| { type: "AnotherAnonVariant", content: {
 	uuid: string;
 	thing: number;
diff --git a/core/data/tests/can_handle_unit_type/output.ts b/core/data/tests/can_handle_unit_type/output.ts
index 430935d..d7508d1 100644
--- a/core/data/tests/can_handle_unit_type/output.ts
+++ b/core/data/tests/can_handle_unit_type/output.ts
@@ -1,9 +1,9 @@
 /** This struct has a unit field */
 export interface StructHasVoidType {
-	thisIsAUnit: undefined;
+	thisIsAUnit: null;
 }
 
 /** This enum has a variant associated with unit data */
 export type EnumHasVoidType = 
-	| { type: "hasAUnit", content: undefined };
+	| { type: "hasAUnit", content: null };