Skip to content

Commit 1fe5813

Browse files
committed
feat: add typescript support for structs
- Allow [GenerateTypeScript] on struct declarations (managed and unmanaged) - Managed structs use object-header wire format with non-nullable TS signatures; deserializeCore throws on null header instead of returning null - Unmanaged structs use raw memory blit: field offsets and padding are computed at generation time and emitted as writeZeros/skipBytes calls - Struct members in other types generate non-nullable TypeScript types - Add MEMPACK043 diagnostic for Nullable<struct> members (use struct directly) - Add writeZeros/skipBytes helpers to MemoryPackWriter/MemoryPackReader TS runtime - Add BoundingBox (struct-in-struct) and GameObject (class + mixed structs) to sandbox - Add comprehensive testStructs covering all struct combinations end-to-end
1 parent 612a481 commit 1fe5813

48 files changed

Lines changed: 1180 additions & 60 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,10 +1211,13 @@ There are a few restrictions on the types that can be generated. Among the primi
12111211
| `: ICollection<T>` | `T[] \| null` | Supports all `ICollection<T>` implemented type like `List<T>`
12121212
| `: ISet<T>` | `Set<T> \| null` | Supports all `ISet<T>` implemented type like `HashSet<T>`
12131213
| `: IDictionary<K,V>` | `Map<K, V> \| null` | Supports all `IDictionary<K,V>` implemented type like `Dictionary<K,V>`.
1214-
| `[MemoryPackable]` | `class` | Supports class only
1214+
| `[MemoryPackable]` | `class` | Supports class and struct (see notes below)
12151215
| `[MemoryPackUnion]` | `abstract class` |
12161216

1217-
`[GenerateTypeScript]` can only be applied to classes and is currently not supported by struct.
1217+
`[GenerateTypeScript]` supports both classes and structs. There are two serialization modes depending on the struct type:
1218+
1219+
- **Managed struct** (contains reference-type fields, e.g. `struct Foo { string Name; int Value; }`): uses the same object-header wire format as classes. The generated TypeScript `class` has non-nullable serialize signatures and omits the null-object-header write, matching the C# value-type semantics.
1220+
- **Unmanaged struct** (contains only unmanaged fields, e.g. `struct Point { int X; int Y; }`): serialized as a raw memory blit with no object header, matching `WriteUnmanaged`/`ReadUnmanaged` in C#. The generated TypeScript `class` writes each field individually at its correct byte offset (including any padding required by natural alignment), and deserialize always returns a non-nullable value.
12181221

12191222
### Configure import file extension and member name casing
12201223

sandbox/SandboxWebApp/Controllers/MemoryPackController.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using MemoryPack;
1+
using MemoryPack;
22
using Microsoft.AspNetCore.Mvc;
33

44
namespace SandboxWebApp.Controllers;
@@ -26,4 +26,16 @@ public NullableFloatTest PostNullableTest([FromBody] NullableFloatTest input)
2626

2727
return ret;
2828
}
29+
30+
[Route("vector3")]
31+
[HttpPost]
32+
public Vector3 PostVector3([FromBody] Vector3 value) => value;
33+
34+
[Route("colorTag")]
35+
[HttpPost]
36+
public ColorTag PostColorTag([FromBody] ColorTag value) => value;
37+
38+
[Route("gameObject")]
39+
[HttpPost]
40+
public GameObject PostGameObject([FromBody] GameObject value) => value;
2941
}

sandbox/SandboxWebApp/Models.cs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using MemoryPack;
1+
using MemoryPack;
22
using System.ComponentModel;
33
using System.Security.Principal;
44

@@ -205,3 +205,38 @@ public partial class NullableFloatTest
205205
public float? NullableFloat { get; set; }
206206
public double? NullableDouble { get; set; }
207207
}
208+
209+
[MemoryPackable]
210+
[GenerateTypeScript]
211+
public partial struct Vector3
212+
{
213+
public float X { get; set; }
214+
public float Y { get; set; }
215+
public float Z { get; set; }
216+
}
217+
218+
[MemoryPackable]
219+
[GenerateTypeScript]
220+
public partial struct ColorTag
221+
{
222+
public string? Name { get; set; }
223+
public int Code { get; set; }
224+
}
225+
226+
[MemoryPackable]
227+
[GenerateTypeScript]
228+
public partial struct BoundingBox
229+
{
230+
public Vector3 Min { get; set; }
231+
public Vector3 Max { get; set; }
232+
}
233+
234+
[MemoryPackable]
235+
[GenerateTypeScript]
236+
public partial class GameObject
237+
{
238+
public string? Name { get; set; }
239+
public Vector3 Position { get; set; }
240+
public BoundingBox Bounds { get; set; }
241+
public ColorTag Tag { get; set; }
242+
}

sandbox/SandboxWebApp/Pages/Index.cshtml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@page
1+
@page
22
@model IndexModel
33
@{
44
ViewData["Title"] = "Home page";
@@ -11,6 +11,9 @@
1111
import { testNullableFloatWithNulls } from "./js/file.js";
1212
import { hoge } from "./js/file.js";
1313
import { huga } from "./js/file.js";
14+
import { testVector3 } from "./js/file.js";
15+
import { testColorTag } from "./js/file.js";
16+
import { testStructs } from "./js/file.js";
1417
1518
//hoge();
1619
//huga();
@@ -20,6 +23,10 @@
2023
testNullableFloatWithValues();
2124
testNullableFloatWithNulls();
2225
26+
testVector3();
27+
testColorTag();
28+
testStructs();
29+
2330
</script>
2431

2532
<div class="text-center">

sandbox/SandboxWebApp/wwwroot/js/file.js

Lines changed: 92 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)