Repository navigation
Expand file tree
/
Copy pathTypeScriptInterfaceGenerator.cs
More file actions
105 lines (96 loc) · 4.01 KB
/
Copy pathTypeScriptInterfaceGenerator.cs
File metadata and controls
105 lines (96 loc) · 4.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#nullable enable
#if UNITY_EDITOR
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Reflection;
namespace UnityAngularBridge
{
/// <summary>
/// Emits TypeScript interfaces for [Serializable] DTO classes referenced via JsonType
/// on [AngularExposed] / [JSLibExport] attributes.
/// Supported fields (mirroring JsonUtility): public instance fields of string, int, long,
/// float, double, bool, arrays/List<T> of those, and nested [Serializable] classes.
/// Properties, dictionaries, and polymorphism are not supported.
/// </summary>
internal static class TypeScriptInterfaceGenerator
{
/// <summary>
/// Writes TS interfaces for the given DTO types (plus any nested DTO types they reference).
/// Throws on unsupported field types, non-[Serializable] types, and short-name collisions.
/// </summary>
internal static void WriteInterfaces(IndentedTextWriter writer, IEnumerable<Type> dtoTypes)
{
Dictionary<string, Type> emittedByName = new();
List<Type> queue = new(dtoTypes);
HashSet<Type> queued = new(queue);
for (int i = 0; i < queue.Count; i++)
{
Type type = queue[i];
if (!type.IsSerializable)
{
throw new InvalidOperationException(
$"[UnityAngularBridge] JsonType {type.FullName} must be marked [Serializable] so JsonUtility can handle it.");
}
if (emittedByName.TryGetValue(type.Name, out Type? existing))
{
if (existing == type) continue;
throw new InvalidOperationException(
$"[UnityAngularBridge] Two DTO types share the short name '{type.Name}' ({existing.FullName} and {type.FullName}). Rename one of them.");
}
emittedByName[type.Name] = type;
writer.WriteLine($"export interface {type.Name} {{");
writer.Indent++;
foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.Instance))
{
writer.WriteLine($"{field.Name}: {ToTypeScriptType(field, queue, queued)};");
}
writer.Indent--;
writer.WriteLine("}");
writer.WriteLine();
}
}
private static string ToTypeScriptType(FieldInfo field, List<Type> queue, HashSet<Type> queued)
{
Type type = field.FieldType;
bool isArray = false;
if (type.IsArray)
{
isArray = true;
type = type.GetElementType()!;
}
else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
{
isArray = true;
type = type.GetGenericArguments()[0];
}
string tsType;
if (type == typeof(string))
{
tsType = "string";
}
else if (type == typeof(int) || type == typeof(long) || type == typeof(float) || type == typeof(double))
{
tsType = "number";
}
else if (type == typeof(bool))
{
tsType = "boolean";
}
else if (type.IsClass && type.IsSerializable)
{
// Nested DTO — queue it for its own interface.
if (queued.Add(type)) queue.Add(type);
tsType = type.Name;
}
else
{
throw new InvalidOperationException(
$"[UnityAngularBridge] Field '{field.DeclaringType?.Name}.{field.Name}' has unsupported type {type}. " +
"Supported: string, int, long, float, double, bool, arrays/List<T> of those, and nested [Serializable] classes.");
}
return isArray ? $"{tsType}[]" : tsType;
}
}
}
#endif