goscript 0.0.25 → 0.0.28

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.
Files changed (190) hide show
  1. package/README.md +4 -4
  2. package/cmd/goscript/cmd_compile.go +0 -3
  3. package/cmd/goscript/deps.go +11 -0
  4. package/compiler/analysis.go +259 -55
  5. package/compiler/assignment.go +2 -2
  6. package/compiler/builtin_test.go +1 -1
  7. package/compiler/compiler.go +201 -49
  8. package/compiler/compiler_test.go +53 -0
  9. package/compiler/composite-lit.go +32 -8
  10. package/compiler/decl.go +6 -6
  11. package/compiler/expr-call.go +83 -0
  12. package/compiler/expr.go +1 -1
  13. package/compiler/protobuf.go +557 -0
  14. package/compiler/spec-struct.go +4 -0
  15. package/compiler/spec-value.go +11 -3
  16. package/compiler/spec.go +18 -1
  17. package/compiler/stmt-assign.go +35 -0
  18. package/compiler/type-assert.go +87 -0
  19. package/compiler/type.go +5 -2
  20. package/dist/gs/builtin/builtin.d.ts +19 -1
  21. package/dist/gs/builtin/builtin.js +85 -5
  22. package/dist/gs/builtin/builtin.js.map +1 -1
  23. package/dist/gs/builtin/channel.js.map +1 -1
  24. package/dist/gs/builtin/slice.d.ts +1 -1
  25. package/dist/gs/builtin/slice.js +59 -26
  26. package/dist/gs/builtin/slice.js.map +1 -1
  27. package/dist/gs/cmp/index.js.map +1 -1
  28. package/dist/gs/context/context.d.ts +1 -1
  29. package/dist/gs/context/context.js +20 -11
  30. package/dist/gs/context/context.js.map +1 -1
  31. package/dist/gs/errors/errors.d.ts +7 -0
  32. package/dist/gs/errors/errors.js +190 -0
  33. package/dist/gs/errors/errors.js.map +1 -0
  34. package/dist/gs/errors/index.d.ts +1 -0
  35. package/dist/gs/errors/index.js +2 -0
  36. package/dist/gs/errors/index.js.map +1 -0
  37. package/dist/gs/internal/goarch/index.js +1 -1
  38. package/dist/gs/internal/goarch/index.js.map +1 -1
  39. package/dist/gs/io/index.d.ts +1 -0
  40. package/dist/gs/io/index.js +2 -0
  41. package/dist/gs/io/index.js.map +1 -0
  42. package/dist/gs/io/io.d.ts +107 -0
  43. package/dist/gs/io/io.js +385 -0
  44. package/dist/gs/io/io.js.map +1 -0
  45. package/dist/gs/iter/iter.js.map +1 -1
  46. package/dist/gs/math/bits/index.js +34 -32
  47. package/dist/gs/math/bits/index.js.map +1 -1
  48. package/dist/gs/runtime/runtime.d.ts +1 -0
  49. package/dist/gs/runtime/runtime.js +15 -18
  50. package/dist/gs/runtime/runtime.js.map +1 -1
  51. package/dist/gs/slices/slices.d.ts +1 -1
  52. package/dist/gs/slices/slices.js +1 -1
  53. package/dist/gs/slices/slices.js.map +1 -1
  54. package/dist/gs/strings/builder.d.ts +18 -0
  55. package/dist/gs/strings/builder.js +205 -0
  56. package/dist/gs/strings/builder.js.map +1 -0
  57. package/dist/gs/strings/clone.d.ts +1 -0
  58. package/dist/gs/strings/clone.js +16 -0
  59. package/dist/gs/strings/clone.js.map +1 -0
  60. package/dist/gs/strings/compare.d.ts +1 -0
  61. package/dist/gs/strings/compare.js +14 -0
  62. package/dist/gs/strings/compare.js.map +1 -0
  63. package/dist/gs/strings/index.d.ts +2 -0
  64. package/dist/gs/strings/index.js +3 -0
  65. package/dist/gs/strings/index.js.map +1 -0
  66. package/dist/gs/strings/iter.d.ts +8 -0
  67. package/dist/gs/strings/iter.js +160 -0
  68. package/dist/gs/strings/iter.js.map +1 -0
  69. package/dist/gs/strings/reader.d.ts +34 -0
  70. package/dist/gs/strings/reader.js +418 -0
  71. package/dist/gs/strings/reader.js.map +1 -0
  72. package/dist/gs/strings/replace.d.ts +106 -0
  73. package/dist/gs/strings/replace.js +1136 -0
  74. package/dist/gs/strings/replace.js.map +1 -0
  75. package/dist/gs/strings/search.d.ts +24 -0
  76. package/dist/gs/strings/search.js +169 -0
  77. package/dist/gs/strings/search.js.map +1 -0
  78. package/dist/gs/strings/strings.d.ts +47 -0
  79. package/dist/gs/strings/strings.js +418 -0
  80. package/dist/gs/strings/strings.js.map +1 -0
  81. package/dist/gs/stringslite/index.d.ts +1 -0
  82. package/dist/gs/stringslite/index.js +2 -0
  83. package/dist/gs/stringslite/index.js.map +1 -0
  84. package/dist/gs/stringslite/strings.d.ts +11 -0
  85. package/dist/gs/stringslite/strings.js +67 -0
  86. package/dist/gs/stringslite/strings.js.map +1 -0
  87. package/dist/gs/sync/index.d.ts +1 -0
  88. package/dist/gs/sync/index.js +2 -0
  89. package/dist/gs/sync/index.js.map +1 -0
  90. package/dist/gs/sync/sync.d.ts +79 -0
  91. package/dist/gs/sync/sync.js +392 -0
  92. package/dist/gs/sync/sync.js.map +1 -0
  93. package/dist/gs/time/time.js +7 -7
  94. package/dist/gs/time/time.js.map +1 -1
  95. package/dist/gs/unicode/index.d.ts +1 -0
  96. package/dist/gs/unicode/index.js +2 -0
  97. package/dist/gs/unicode/index.js.map +1 -0
  98. package/dist/gs/unicode/unicode.d.ts +105 -0
  99. package/dist/gs/unicode/unicode.js +332 -0
  100. package/dist/gs/unicode/unicode.js.map +1 -0
  101. package/dist/gs/unicode/utf8/index.d.ts +1 -0
  102. package/dist/gs/unicode/utf8/index.js +3 -0
  103. package/dist/gs/unicode/utf8/index.js.map +1 -0
  104. package/dist/gs/unicode/utf8/utf8.d.ts +20 -0
  105. package/dist/gs/unicode/utf8/utf8.js +196 -0
  106. package/dist/gs/unicode/utf8/utf8.js.map +1 -0
  107. package/dist/gs/unsafe/index.d.ts +1 -0
  108. package/dist/gs/unsafe/index.js +2 -0
  109. package/dist/gs/unsafe/index.js.map +1 -0
  110. package/dist/gs/unsafe/unsafe.d.ts +11 -0
  111. package/dist/gs/unsafe/unsafe.js +44 -0
  112. package/dist/gs/unsafe/unsafe.js.map +1 -0
  113. package/go.mod +2 -1
  114. package/go.sum +6 -2
  115. package/gs/README.md +6 -0
  116. package/gs/builtin/builtin.ts +158 -0
  117. package/gs/builtin/channel.ts +683 -0
  118. package/gs/builtin/defer.ts +58 -0
  119. package/gs/builtin/index.ts +1 -0
  120. package/gs/builtin/io.ts +22 -0
  121. package/gs/builtin/map.ts +50 -0
  122. package/gs/builtin/slice.ts +1030 -0
  123. package/gs/builtin/type.ts +1106 -0
  124. package/gs/builtin/varRef.ts +25 -0
  125. package/gs/cmp/godoc.txt +8 -0
  126. package/gs/cmp/index.ts +29 -0
  127. package/gs/context/context.ts +401 -0
  128. package/gs/context/godoc.txt +69 -0
  129. package/gs/context/index.ts +1 -0
  130. package/gs/errors/errors.ts +223 -0
  131. package/gs/errors/godoc.txt +63 -0
  132. package/gs/errors/index.ts +1 -0
  133. package/gs/internal/goarch/godoc.txt +39 -0
  134. package/gs/internal/goarch/index.ts +18 -0
  135. package/gs/io/godoc.txt +61 -0
  136. package/gs/io/index.ts +1 -0
  137. package/gs/io/io.go +75 -0
  138. package/gs/io/io.ts +546 -0
  139. package/gs/iter/godoc.txt +203 -0
  140. package/gs/iter/index.ts +1 -0
  141. package/gs/iter/iter.ts +117 -0
  142. package/gs/math/bits/index.ts +356 -0
  143. package/gs/math/godoc.txt +76 -0
  144. package/gs/runtime/godoc.txt +331 -0
  145. package/gs/runtime/index.ts +1 -0
  146. package/gs/runtime/runtime.ts +178 -0
  147. package/gs/slices/godoc.txt +44 -0
  148. package/gs/slices/index.ts +1 -0
  149. package/gs/slices/slices.ts +22 -0
  150. package/gs/strings/builder.test.ts +121 -0
  151. package/gs/strings/builder.ts +223 -0
  152. package/gs/strings/clone.test.ts +43 -0
  153. package/gs/strings/clone.ts +17 -0
  154. package/gs/strings/compare.test.ts +84 -0
  155. package/gs/strings/compare.ts +13 -0
  156. package/gs/strings/godoc.txt +66 -0
  157. package/gs/strings/index.ts +2 -0
  158. package/gs/strings/iter.test.ts +343 -0
  159. package/gs/strings/iter.ts +171 -0
  160. package/gs/strings/reader.test.ts +243 -0
  161. package/gs/strings/reader.ts +451 -0
  162. package/gs/strings/replace.test.ts +181 -0
  163. package/gs/strings/replace.ts +1310 -0
  164. package/gs/strings/search.test.ts +214 -0
  165. package/gs/strings/search.ts +213 -0
  166. package/gs/strings/strings.test.ts +477 -0
  167. package/gs/strings/strings.ts +510 -0
  168. package/gs/stringslite/godoc.txt +17 -0
  169. package/gs/stringslite/index.ts +1 -0
  170. package/gs/stringslite/strings.ts +82 -0
  171. package/gs/sync/godoc.txt +21 -0
  172. package/gs/sync/index.ts +1 -0
  173. package/gs/sync/sync.go +64 -0
  174. package/gs/sync/sync.ts +449 -0
  175. package/gs/time/godoc.md +116 -0
  176. package/gs/time/godoc.txt +116 -0
  177. package/gs/time/index.ts +1 -0
  178. package/gs/time/time.ts +272 -0
  179. package/gs/unicode/godoc.txt +52 -0
  180. package/gs/unicode/index.ts +1 -0
  181. package/gs/unicode/unicode.go +38 -0
  182. package/gs/unicode/unicode.ts +418 -0
  183. package/gs/unicode/utf8/godoc.txt +22 -0
  184. package/gs/unicode/utf8/index.ts +2 -0
  185. package/gs/unicode/utf8/utf8.ts +227 -0
  186. package/gs/unsafe/godoc.txt +19 -0
  187. package/gs/unsafe/index.ts +1 -0
  188. package/gs/unsafe/unsafe.test.ts +68 -0
  189. package/gs/unsafe/unsafe.ts +77 -0
  190. package/package.json +6 -4
@@ -0,0 +1,1106 @@
1
+ /**
2
+ * Represents the kinds of Go types that can be registered at runtime.
3
+ */
4
+ export enum TypeKind {
5
+ Basic = 'basic',
6
+ Interface = 'interface',
7
+ Struct = 'struct',
8
+ Map = 'map',
9
+ Slice = 'slice',
10
+ Array = 'array',
11
+ Pointer = 'pointer',
12
+ Function = 'function',
13
+ Channel = 'channel',
14
+ }
15
+
16
+ /**
17
+ * Base type information shared by all type kinds
18
+ */
19
+ export interface BaseTypeInfo {
20
+ name?: string
21
+ kind: TypeKind
22
+ zeroValue?: any
23
+ }
24
+
25
+ /**
26
+ * Represents an argument or a return value of a method.
27
+ */
28
+ export interface MethodArg {
29
+ name?: string // Name of the argument/return value, if available
30
+ type: TypeInfo | string // TypeInfo object or string name of the type
31
+ }
32
+
33
+ /**
34
+ * Represents the signature of a method, including its name, arguments, and return types.
35
+ */
36
+ export interface MethodSignature {
37
+ name: string
38
+ args: MethodArg[]
39
+ returns: MethodArg[]
40
+ }
41
+
42
+ /**
43
+ * Type information for struct types
44
+ */
45
+ export interface StructTypeInfo extends BaseTypeInfo {
46
+ kind: TypeKind.Struct
47
+ methods: MethodSignature[] // Array of method signatures
48
+ ctor?: new (...args: any[]) => any
49
+ fields: Record<string, TypeInfo | string> // Field names and types for struct fields
50
+ }
51
+
52
+ /**
53
+ * Type information for interface types
54
+ */
55
+ export interface InterfaceTypeInfo extends BaseTypeInfo {
56
+ kind: TypeKind.Interface
57
+ methods: MethodSignature[] // Array of method signatures
58
+ }
59
+
60
+ /**
61
+ * Type information for basic types (string, number, boolean)
62
+ */
63
+ export interface BasicTypeInfo extends BaseTypeInfo {
64
+ kind: TypeKind.Basic
65
+ }
66
+
67
+ /**
68
+ * Type information for map types
69
+ */
70
+ export interface MapTypeInfo extends BaseTypeInfo {
71
+ kind: TypeKind.Map
72
+ keyType?: string | TypeInfo
73
+ elemType?: string | TypeInfo
74
+ }
75
+
76
+ /**
77
+ * Type information for slice types
78
+ */
79
+ export interface SliceTypeInfo extends BaseTypeInfo {
80
+ kind: TypeKind.Slice
81
+ elemType?: string | TypeInfo
82
+ }
83
+
84
+ /**
85
+ * Type information for array types
86
+ */
87
+ export interface ArrayTypeInfo extends BaseTypeInfo {
88
+ kind: TypeKind.Array
89
+ elemType?: string | TypeInfo
90
+ length: number
91
+ }
92
+
93
+ /**
94
+ * Type information for pointer types
95
+ */
96
+ export interface PointerTypeInfo extends BaseTypeInfo {
97
+ kind: TypeKind.Pointer
98
+ elemType?: string | TypeInfo
99
+ }
100
+
101
+ /**
102
+ * Type information for function types
103
+ */
104
+ export interface FunctionTypeInfo extends BaseTypeInfo {
105
+ kind: TypeKind.Function
106
+ params?: (string | TypeInfo)[]
107
+ results?: (string | TypeInfo)[]
108
+ isVariadic?: boolean // True if the function is variadic (e.g., ...T)
109
+ }
110
+
111
+ /**
112
+ * Type information for channel types
113
+ */
114
+ export interface ChannelTypeInfo extends BaseTypeInfo {
115
+ kind: TypeKind.Channel
116
+ elemType?: string | TypeInfo
117
+ direction?: 'send' | 'receive' | 'both'
118
+ }
119
+
120
+ /**
121
+ * TypeInfo is used for runtime type checking.
122
+ * Can be a registered type (from typeRegistry) or an ad-hoc type description.
123
+ * When used as input to typeAssert, it can be a string (type name) or a structured description.
124
+ */
125
+ export type TypeInfo =
126
+ | StructTypeInfo
127
+ | InterfaceTypeInfo
128
+ | BasicTypeInfo
129
+ | MapTypeInfo
130
+ | SliceTypeInfo
131
+ | ArrayTypeInfo
132
+ | PointerTypeInfo
133
+ | FunctionTypeInfo
134
+ | ChannelTypeInfo
135
+
136
+ // Type guard functions for TypeInfo variants
137
+ export function isStructTypeInfo(info: TypeInfo): info is StructTypeInfo {
138
+ return info.kind === TypeKind.Struct
139
+ }
140
+
141
+ export function isInterfaceTypeInfo(info: TypeInfo): info is InterfaceTypeInfo {
142
+ return info.kind === TypeKind.Interface
143
+ }
144
+
145
+ export function isBasicTypeInfo(info: TypeInfo): info is BasicTypeInfo {
146
+ return info.kind === TypeKind.Basic
147
+ }
148
+
149
+ export function isMapTypeInfo(info: TypeInfo): info is MapTypeInfo {
150
+ return info.kind === TypeKind.Map
151
+ }
152
+
153
+ export function isSliceTypeInfo(info: TypeInfo): info is SliceTypeInfo {
154
+ return info.kind === TypeKind.Slice
155
+ }
156
+
157
+ export function isArrayTypeInfo(info: TypeInfo): info is ArrayTypeInfo {
158
+ return info.kind === TypeKind.Array
159
+ }
160
+
161
+ export function isPointerTypeInfo(info: TypeInfo): info is PointerTypeInfo {
162
+ return info.kind === TypeKind.Pointer
163
+ }
164
+
165
+ export function isFunctionTypeInfo(info: TypeInfo): info is FunctionTypeInfo {
166
+ return info.kind === TypeKind.Function
167
+ }
168
+
169
+ export function isChannelTypeInfo(info: TypeInfo): info is ChannelTypeInfo {
170
+ return info.kind === TypeKind.Channel
171
+ }
172
+
173
+ /**
174
+ * Comparable interface for Go's comparable constraint.
175
+ * Types that implement this can be compared with == and !=.
176
+ */
177
+ export interface Comparable {
178
+ // This is a marker interface - any type that can be compared implements this
179
+ }
180
+
181
+ // Registry to store runtime type information
182
+ const typeRegistry = new Map<string, TypeInfo>()
183
+
184
+ /**
185
+ * Registers a struct type with the runtime type system.
186
+ *
187
+ * @param name The name of the type.
188
+ * @param zeroValue The zero value for the type.
189
+ * @param methods Array of method signatures for the struct.
190
+ * @param ctor Constructor for the struct.
191
+ * @param fields Record of field names and their types.
192
+ * @returns The struct type information object.
193
+ */
194
+ export const registerStructType = (
195
+ name: string,
196
+ zeroValue: any,
197
+ methods: MethodSignature[],
198
+ ctor: new (...args: any[]) => any,
199
+ fields: Record<string, TypeInfo | string> = {},
200
+ ): StructTypeInfo => {
201
+ const typeInfo: StructTypeInfo = {
202
+ name,
203
+ kind: TypeKind.Struct,
204
+ zeroValue,
205
+ methods,
206
+ ctor,
207
+ fields,
208
+ }
209
+ typeRegistry.set(name, typeInfo)
210
+ return typeInfo
211
+ }
212
+
213
+ /**
214
+ * Registers an interface type with the runtime type system.
215
+ *
216
+ * @param name The name of the type.
217
+ * @param zeroValue The zero value for the type (usually null).
218
+ * @param methods Array of method signatures for the interface.
219
+ * @returns The interface type information object.
220
+ */
221
+ export const registerInterfaceType = (
222
+ name: string,
223
+ zeroValue: any,
224
+ methods: MethodSignature[],
225
+ ): InterfaceTypeInfo => {
226
+ const typeInfo: InterfaceTypeInfo = {
227
+ name,
228
+ kind: TypeKind.Interface,
229
+ zeroValue,
230
+ methods,
231
+ }
232
+ typeRegistry.set(name, typeInfo)
233
+ return typeInfo
234
+ }
235
+
236
+ /**
237
+ * Represents the result of a type assertion.
238
+ */
239
+ export interface TypeAssertResult<T> {
240
+ value: T
241
+ ok: boolean
242
+ }
243
+
244
+ /**
245
+ * Normalizes a type info to a structured TypeInfo object.
246
+ *
247
+ * @param info The type info or name.
248
+ * @returns A normalized TypeInfo object.
249
+ */
250
+ function normalizeTypeInfo(info: string | TypeInfo): TypeInfo {
251
+ if (typeof info === 'string') {
252
+ const typeInfo = typeRegistry.get(info)
253
+ if (typeInfo) {
254
+ return typeInfo
255
+ }
256
+ return {
257
+ kind: TypeKind.Basic,
258
+ name: info,
259
+ }
260
+ }
261
+
262
+ return info
263
+ }
264
+
265
+ function compareOptionalTypeInfo(
266
+ type1?: string | TypeInfo,
267
+ type2?: string | TypeInfo,
268
+ ): boolean {
269
+ if (type1 === undefined && type2 === undefined) return true
270
+ if (type1 === undefined || type2 === undefined) return false
271
+ // Assuming areTypeInfosIdentical will handle normalization if needed,
272
+ // but type1 and type2 here are expected to be direct fields from TypeInfo objects.
273
+ return areTypeInfosIdentical(type1, type2)
274
+ }
275
+
276
+ function areFuncParamOrResultArraysIdentical(
277
+ arr1?: (string | TypeInfo)[],
278
+ arr2?: (string | TypeInfo)[],
279
+ ): boolean {
280
+ if (arr1 === undefined && arr2 === undefined) return true
281
+ if (arr1 === undefined || arr2 === undefined) return false
282
+ if (arr1.length !== arr2.length) return false
283
+ for (let i = 0; i < arr1.length; i++) {
284
+ if (!areTypeInfosIdentical(arr1[i], arr2[i])) {
285
+ return false
286
+ }
287
+ }
288
+ return true
289
+ }
290
+
291
+ function areFuncSignaturesIdentical(
292
+ func1: FunctionTypeInfo,
293
+ func2: FunctionTypeInfo,
294
+ ): boolean {
295
+ if ((func1.isVariadic || false) !== (func2.isVariadic || false)) {
296
+ return false
297
+ }
298
+ return (
299
+ areFuncParamOrResultArraysIdentical(func1.params, func2.params) &&
300
+ areFuncParamOrResultArraysIdentical(func1.results, func2.results)
301
+ )
302
+ }
303
+
304
+ function areMethodArgsArraysIdentical(
305
+ args1?: MethodArg[],
306
+ args2?: MethodArg[],
307
+ ): boolean {
308
+ if (args1 === undefined && args2 === undefined) return true
309
+ if (args1 === undefined || args2 === undefined) return false
310
+ if (args1.length !== args2.length) return false
311
+ for (let i = 0; i < args1.length; i++) {
312
+ // Compare based on type only, names of args/results don't affect signature identity here.
313
+ if (!areTypeInfosIdentical(args1[i].type, args2[i].type)) {
314
+ return false
315
+ }
316
+ }
317
+ return true
318
+ }
319
+
320
+ export function areTypeInfosIdentical(
321
+ type1InfoOrName: string | TypeInfo,
322
+ type2InfoOrName: string | TypeInfo,
323
+ ): boolean {
324
+ const t1Norm = normalizeTypeInfo(type1InfoOrName)
325
+ const t2Norm = normalizeTypeInfo(type2InfoOrName)
326
+
327
+ if (t1Norm === t2Norm) return true // Object identity
328
+ if (t1Norm.kind !== t2Norm.kind) return false
329
+
330
+ // If types have names, the names must match for identity.
331
+ // If one has a name and the other doesn't, they are not identical.
332
+ if (t1Norm.name !== t2Norm.name) return false
333
+
334
+ // If both are named and names match, for Basic, Struct, Interface, this is sufficient for identity.
335
+ if (
336
+ t1Norm.name !== undefined /* && t2Norm.name is also defined and equal */
337
+ ) {
338
+ if (
339
+ t1Norm.kind === TypeKind.Basic ||
340
+ t1Norm.kind === TypeKind.Struct ||
341
+ t1Norm.kind === TypeKind.Interface
342
+ ) {
343
+ return true
344
+ }
345
+ }
346
+ // For other types (Pointer, Slice, etc.), or if both are anonymous (name is undefined),
347
+ // structural comparison is needed.
348
+
349
+ switch (t1Norm.kind) {
350
+ case TypeKind.Basic:
351
+ // Names matched if they were defined, or both undefined (which means true by t1Norm.name !== t2Norm.name being false)
352
+ return true
353
+ case TypeKind.Pointer:
354
+ return compareOptionalTypeInfo(
355
+ (t1Norm as PointerTypeInfo).elemType,
356
+ (t2Norm as PointerTypeInfo).elemType,
357
+ )
358
+ case TypeKind.Slice:
359
+ return compareOptionalTypeInfo(
360
+ (t1Norm as SliceTypeInfo).elemType,
361
+ (t2Norm as SliceTypeInfo).elemType,
362
+ )
363
+ case TypeKind.Array:
364
+ return (
365
+ (t1Norm as ArrayTypeInfo).length === (t2Norm as ArrayTypeInfo).length &&
366
+ compareOptionalTypeInfo(
367
+ (t1Norm as ArrayTypeInfo).elemType,
368
+ (t2Norm as ArrayTypeInfo).elemType,
369
+ )
370
+ )
371
+ case TypeKind.Map:
372
+ return (
373
+ compareOptionalTypeInfo(
374
+ (t1Norm as MapTypeInfo).keyType,
375
+ (t2Norm as MapTypeInfo).keyType,
376
+ ) &&
377
+ compareOptionalTypeInfo(
378
+ (t1Norm as MapTypeInfo).elemType,
379
+ (t2Norm as MapTypeInfo).elemType,
380
+ )
381
+ )
382
+ case TypeKind.Channel:
383
+ return (
384
+ // Ensure direction property exists before comparing, or handle undefined if it can be
385
+ ((t1Norm as ChannelTypeInfo).direction || 'both') ===
386
+ ((t2Norm as ChannelTypeInfo).direction || 'both') &&
387
+ compareOptionalTypeInfo(
388
+ (t1Norm as ChannelTypeInfo).elemType,
389
+ (t2Norm as ChannelTypeInfo).elemType,
390
+ )
391
+ )
392
+ case TypeKind.Function:
393
+ return areFuncSignaturesIdentical(
394
+ t1Norm as FunctionTypeInfo,
395
+ t2Norm as FunctionTypeInfo,
396
+ )
397
+ case TypeKind.Struct:
398
+ case TypeKind.Interface:
399
+ // If we reach here, names were undefined (both anonymous) or names matched but was not Basic/Struct/Interface.
400
+ // For anonymous Struct/Interface, strict identity means full structural comparison.
401
+ // For now, we consider anonymous types not identical unless they are the same object (caught above).
402
+ // If they were named and matched, 'return true' was hit earlier for these kinds.
403
+ return false
404
+ default:
405
+ return false
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Validates that a map key matches the expected type info.
411
+ *
412
+ * @param key The key to validate
413
+ * @param keyTypeInfo The normalized type info for the key
414
+ * @returns True if the key matches the type info, false otherwise
415
+ */
416
+ function validateMapKey(key: any, keyTypeInfo: TypeInfo): boolean {
417
+ if (keyTypeInfo.kind === TypeKind.Basic) {
418
+ // For string keys
419
+ if (keyTypeInfo.name === 'string') {
420
+ return typeof key === 'string'
421
+ } else if (
422
+ keyTypeInfo.name === 'int' ||
423
+ keyTypeInfo.name === 'float64' ||
424
+ keyTypeInfo.name === 'number'
425
+ ) {
426
+ if (typeof key === 'string') {
427
+ return /^-?\d+(\.\d+)?$/.test(key)
428
+ } else {
429
+ return typeof key === 'number'
430
+ }
431
+ }
432
+ }
433
+ return false
434
+ }
435
+
436
+ /**
437
+ * Checks if a value matches a basic type info.
438
+ *
439
+ * @param value The value to check.
440
+ * @param info The basic type info to match against.
441
+ * @returns True if the value matches the basic type, false otherwise.
442
+ */
443
+ function matchesBasicType(value: any, info: TypeInfo): boolean {
444
+ if (info.name === 'string') return typeof value === 'string'
445
+ if (info.name === 'number' || info.name === 'int' || info.name === 'float64')
446
+ return typeof value === 'number'
447
+ if (info.name === 'boolean' || info.name === 'bool')
448
+ return typeof value === 'boolean'
449
+ return false
450
+ }
451
+
452
+ /**
453
+ * Checks if a value matches a struct type info.
454
+ *
455
+ * @param value The value to check.
456
+ * @param info The struct type info to match against.
457
+ * @returns True if the value matches the struct type, false otherwise.
458
+ */
459
+ function matchesStructType(value: any, info: TypeInfo): boolean {
460
+ if (!isStructTypeInfo(info)) return false
461
+
462
+ // For structs, use instanceof with the constructor
463
+ if (info.ctor && value instanceof info.ctor) {
464
+ return true
465
+ }
466
+
467
+ // Check if the value has all methods defined in the struct's TypeInfo
468
+ // This is a structural check, not a signature check here.
469
+ // Signature checks are more relevant for interface satisfaction.
470
+ if (info.methods && typeof value === 'object' && value !== null) {
471
+ const allMethodsExist = info.methods.every(
472
+ (methodSig) => typeof (value as any)[methodSig.name] === 'function',
473
+ )
474
+ if (!allMethodsExist) {
475
+ return false
476
+ }
477
+ // Further signature checking could be added here if needed for struct-to-struct assignability
478
+ }
479
+
480
+ if (typeof value === 'object' && value !== null && info.fields) {
481
+ const fieldNames = Object.keys(info.fields || {})
482
+ const valueFields = Object.keys(value)
483
+
484
+ const fieldsExist = fieldNames.every((field) => field in value)
485
+ const sameFieldCount = valueFields.length === fieldNames.length
486
+ const allFieldsInStruct = valueFields.every((field) =>
487
+ fieldNames.includes(field),
488
+ )
489
+
490
+ if (fieldsExist && sameFieldCount && allFieldsInStruct) {
491
+ return Object.entries(info.fields).every(([fieldName, fieldType]) => {
492
+ return matchesType(
493
+ value[fieldName],
494
+ normalizeTypeInfo(fieldType as TypeInfo | string),
495
+ )
496
+ })
497
+ }
498
+
499
+ return false
500
+ }
501
+
502
+ return false
503
+ }
504
+
505
+ /**
506
+ * Checks if a value matches an interface type info by verifying it implements
507
+ * all required methods with compatible signatures.
508
+ *
509
+ * @param value The value to check.
510
+ * @param info The interface type info to match against.
511
+ * @returns True if the value matches the interface type, false otherwise.
512
+ */
513
+ function matchesInterfaceType(value: any, info: TypeInfo): boolean {
514
+ // Check basic conditions first
515
+ if (
516
+ !isInterfaceTypeInfo(info) ||
517
+ typeof value !== 'object' ||
518
+ value === null
519
+ ) {
520
+ return false
521
+ }
522
+
523
+ // For interfaces, check if the value has all the required methods with compatible signatures
524
+ return info.methods.every((requiredMethodSig) => {
525
+ const actualMethod = (value as any)[requiredMethodSig.name]
526
+
527
+ // Method must exist and be a function
528
+ if (typeof actualMethod !== 'function') {
529
+ return false
530
+ }
531
+
532
+ // Check parameter count (basic arity check)
533
+ // Note: This is a simplified check as JavaScript functions can have optional/rest parameters
534
+ const declaredParamCount = actualMethod.length
535
+ const requiredParamCount = requiredMethodSig.args.length
536
+
537
+ // Strict arity checking can be problematic in JS, so we'll be lenient
538
+ // A method with fewer params than required is definitely incompatible
539
+ if (declaredParamCount < requiredParamCount) {
540
+ return false
541
+ }
542
+
543
+ // Check return types if we can determine them
544
+ // This is challenging in JavaScript without runtime type information
545
+
546
+ // If the value has a __goTypeName property, it might be a registered type
547
+ // with more type information available
548
+ if (value.__goTypeName) {
549
+ const valueTypeInfo = typeRegistry.get(value.__goTypeName)
550
+ if (valueTypeInfo && isStructTypeInfo(valueTypeInfo)) {
551
+ // Find the matching method in the value's type info
552
+ const valueMethodSig = valueTypeInfo.methods.find(
553
+ (m) => m.name === requiredMethodSig.name,
554
+ )
555
+
556
+ if (valueMethodSig) {
557
+ // Compare return types
558
+ if (
559
+ valueMethodSig.returns.length !== requiredMethodSig.returns.length
560
+ ) {
561
+ return false
562
+ }
563
+
564
+ // Compare each return type for compatibility
565
+ for (let i = 0; i < requiredMethodSig.returns.length; i++) {
566
+ const requiredReturnType = normalizeTypeInfo(
567
+ requiredMethodSig.returns[i].type,
568
+ )
569
+ const valueReturnType = normalizeTypeInfo(
570
+ valueMethodSig.returns[i].type,
571
+ )
572
+
573
+ // For interface return types, we need to check if the value's return type
574
+ // implements the required interface
575
+ if (isInterfaceTypeInfo(requiredReturnType)) {
576
+ // This would be a recursive check, but we'll simplify for now
577
+ // by just checking if the types are the same or if the value type
578
+ // is registered as implementing the interface
579
+ if (requiredReturnType.name !== valueReturnType.name) {
580
+ // Check if valueReturnType implements requiredReturnType
581
+ // This would require additional implementation tracking
582
+ return false
583
+ }
584
+ }
585
+ // For non-interface types, check direct type compatibility
586
+ else if (requiredReturnType.name !== valueReturnType.name) {
587
+ return false
588
+ }
589
+ }
590
+
591
+ // Similarly, we could check parameter types for compatibility
592
+ // but we'll skip that for brevity
593
+ }
594
+ }
595
+ }
596
+
597
+ // If we can't determine detailed type information, we'll accept the method
598
+ // as long as it exists with a compatible arity
599
+ return true
600
+ })
601
+ }
602
+
603
+ /**
604
+ * Checks if a value matches a map type info.
605
+ *
606
+ * @param value The value to check.
607
+ * @param info The map type info to match against.
608
+ * @returns True if the value matches the map type, false otherwise.
609
+ */
610
+ function matchesMapType(value: any, info: TypeInfo): boolean {
611
+ if (typeof value !== 'object' || value === null) return false
612
+ if (!isMapTypeInfo(info)) return false
613
+
614
+ if (info.keyType || info.elemType) {
615
+ let entries: [any, any][] = []
616
+
617
+ if (value instanceof Map) {
618
+ entries = Array.from(value.entries())
619
+ } else {
620
+ entries = Object.entries(value)
621
+ }
622
+
623
+ if (entries.length === 0) return true // Empty map matches any map type
624
+
625
+ const sampleSize = Math.min(5, entries.length)
626
+ for (let i = 0; i < sampleSize; i++) {
627
+ const [k, v] = entries[i]
628
+
629
+ if (info.keyType) {
630
+ if (
631
+ !validateMapKey(
632
+ k,
633
+ normalizeTypeInfo(info.keyType as string | TypeInfo),
634
+ )
635
+ ) {
636
+ return false
637
+ }
638
+ }
639
+
640
+ if (
641
+ info.elemType &&
642
+ !matchesType(v, normalizeTypeInfo(info.elemType as string | TypeInfo))
643
+ ) {
644
+ return false
645
+ }
646
+ }
647
+ }
648
+
649
+ return true
650
+ }
651
+
652
+ /**
653
+ * Checks if a value matches an array or slice type info.
654
+ *
655
+ * @param value The value to check.
656
+ * @param info The array or slice type info to match against.
657
+ * @returns True if the value matches the array or slice type, false otherwise.
658
+ */
659
+ function matchesArrayOrSliceType(value: any, info: TypeInfo): boolean {
660
+ // For slices and arrays, check if the value is an array and sample element types
661
+ if (!Array.isArray(value)) return false
662
+ if (!isArrayTypeInfo(info) && !isSliceTypeInfo(info)) return false
663
+
664
+ if (info.elemType) {
665
+ const arr = value as any[]
666
+ if (arr.length === 0) return true // Empty array matches any array type
667
+
668
+ const sampleSize = Math.min(5, arr.length)
669
+ for (let i = 0; i < sampleSize; i++) {
670
+ if (
671
+ !matchesType(
672
+ arr[i],
673
+ normalizeTypeInfo(info.elemType as string | TypeInfo),
674
+ )
675
+ ) {
676
+ return false
677
+ }
678
+ }
679
+ }
680
+
681
+ return true
682
+ }
683
+
684
+ /**
685
+ * Checks if a value matches a pointer type info.
686
+ *
687
+ * @param value The value to check.
688
+ * @param info The pointer type info to match against.
689
+ * @returns True if the value matches the pointer type, false otherwise.
690
+ */
691
+ function matchesPointerType(value: any, info: TypeInfo): boolean {
692
+ // Allow null/undefined values to match pointer types to support nil pointer assertions
693
+ // This enables Go's nil pointer type assertions like `ptr, ok := i.(*SomeType)` to work correctly
694
+ if (value === null || value === undefined) {
695
+ return true
696
+ }
697
+
698
+ // Check if the value is a VarRef (has a 'value' property)
699
+ if (typeof value !== 'object' || !('value' in value)) {
700
+ return false
701
+ }
702
+
703
+ if (!isPointerTypeInfo(info)) return false
704
+
705
+ if (info.elemType) {
706
+ const elemTypeInfo = normalizeTypeInfo(info.elemType as string | TypeInfo)
707
+ return matchesType(value.value, elemTypeInfo)
708
+ }
709
+
710
+ return true
711
+ }
712
+
713
+ /**
714
+ * Checks if a value matches a function type info.
715
+ *
716
+ * @param value The value to check.
717
+ * @param info The function type info to match against.
718
+ * @returns True if the value matches the function type, false otherwise.
719
+ */
720
+ function matchesFunctionType(value: any, info: FunctionTypeInfo): boolean {
721
+ // First check if the value is a function
722
+ if (typeof value !== 'function') {
723
+ return false
724
+ }
725
+
726
+ // This is important for named function types
727
+ if (info.name && value.__goTypeName) {
728
+ return info.name === value.__goTypeName
729
+ }
730
+
731
+ return true
732
+ }
733
+
734
+ /**
735
+ * Checks if a value matches a channel type info.
736
+ *
737
+ * @param value The value to check.
738
+ * @param info The channel type info to match against.
739
+ * @returns True if the value matches the channel type, false otherwise.
740
+ */
741
+ function matchesChannelType(value: any, info: ChannelTypeInfo): boolean {
742
+ // First check if it's a channel or channel reference
743
+ if (typeof value !== 'object' || value === null) {
744
+ return false
745
+ }
746
+
747
+ // If it's a ChannelRef, get the underlying channel
748
+ let channel = value
749
+ let valueDirection = 'both'
750
+
751
+ if ('channel' in value && 'direction' in value) {
752
+ channel = value.channel
753
+ valueDirection = value.direction
754
+ }
755
+
756
+ // Check if it has channel methods
757
+ if (
758
+ !('send' in channel) ||
759
+ !('receive' in channel) ||
760
+ !('close' in channel) ||
761
+ typeof channel.send !== 'function' ||
762
+ typeof channel.receive !== 'function' ||
763
+ typeof channel.close !== 'function'
764
+ ) {
765
+ return false
766
+ }
767
+
768
+ if (info.elemType) {
769
+ if (
770
+ info.elemType === 'string' &&
771
+ 'zeroValue' in channel &&
772
+ channel.zeroValue !== ''
773
+ ) {
774
+ return false
775
+ }
776
+
777
+ if (
778
+ info.elemType === 'number' &&
779
+ 'zeroValue' in channel &&
780
+ typeof channel.zeroValue !== 'number'
781
+ ) {
782
+ return false
783
+ }
784
+ }
785
+
786
+ if (info.direction) {
787
+ return valueDirection === info.direction
788
+ }
789
+
790
+ return true
791
+ }
792
+
793
+ /**
794
+ * Checks if a value matches a type info.
795
+ *
796
+ * @param value The value to check.
797
+ * @param info The type info to match against.
798
+ * @returns True if the value matches the type info, false otherwise.
799
+ */
800
+ function matchesType(value: any, info: TypeInfo): boolean {
801
+ if (value === null || value === undefined) {
802
+ return false
803
+ }
804
+
805
+ switch (info.kind) {
806
+ case TypeKind.Basic:
807
+ return matchesBasicType(value, info)
808
+
809
+ case TypeKind.Struct:
810
+ return matchesStructType(value, info)
811
+
812
+ case TypeKind.Interface:
813
+ return matchesInterfaceType(value, info)
814
+
815
+ case TypeKind.Map:
816
+ return matchesMapType(value, info)
817
+
818
+ case TypeKind.Slice:
819
+ case TypeKind.Array:
820
+ return matchesArrayOrSliceType(value, info)
821
+
822
+ case TypeKind.Pointer:
823
+ return matchesPointerType(value, info)
824
+
825
+ case TypeKind.Function:
826
+ return matchesFunctionType(value, info as FunctionTypeInfo)
827
+
828
+ case TypeKind.Channel:
829
+ return matchesChannelType(value, info)
830
+
831
+ default:
832
+ console.warn(
833
+ `Type matching for kind '${(info as TypeInfo).kind}' not implemented.`,
834
+ )
835
+ return false
836
+ }
837
+ }
838
+
839
+ /**
840
+ * Performs a type assertion on a value against a specified type.
841
+ * Returns an object containing the value (cast to type T) and a boolean indicating success.
842
+ * This is used to implement Go's type assertion with comma-ok idiom: value, ok := x.(Type)
843
+ *
844
+ * @param value The value to check against the type
845
+ * @param typeInfo The type information to check against (can be a string name or TypeInfo object)
846
+ * @returns An object with the asserted value and a boolean indicating if the assertion succeeded
847
+ */
848
+ export function typeAssert<T>(
849
+ value: any,
850
+ typeInfo: string | TypeInfo,
851
+ ): TypeAssertResult<T> {
852
+ const normalizedType = normalizeTypeInfo(typeInfo)
853
+
854
+ if (isPointerTypeInfo(normalizedType) && value === null) {
855
+ return { value: null as unknown as T, ok: true }
856
+ }
857
+
858
+ if (
859
+ isStructTypeInfo(normalizedType) &&
860
+ normalizedType.methods &&
861
+ normalizedType.methods.length > 0 &&
862
+ typeof value === 'object' &&
863
+ value !== null
864
+ ) {
865
+ // Check if the value implements all methods of the struct type with compatible signatures.
866
+ // This is more for interface satisfaction by a struct.
867
+ // For struct-to-struct assertion, usually instanceof or field checks are primary.
868
+ const allMethodsMatch = normalizedType.methods.every(
869
+ (requiredMethodSig) => {
870
+ const actualMethod = (value as any)[requiredMethodSig.name]
871
+ if (typeof actualMethod !== 'function') {
872
+ return false
873
+ }
874
+ const valueTypeInfoVal = (value as any).$typeInfo
875
+ if (valueTypeInfoVal) {
876
+ const normalizedValueType = normalizeTypeInfo(valueTypeInfoVal)
877
+ if (
878
+ isStructTypeInfo(normalizedValueType) ||
879
+ isInterfaceTypeInfo(normalizedValueType)
880
+ ) {
881
+ const actualValueMethodSig = normalizedValueType.methods.find(
882
+ (m) => m.name === requiredMethodSig.name,
883
+ )
884
+ if (actualValueMethodSig) {
885
+ // Perform full signature comparison using MethodSignatures
886
+ const paramsMatch = areMethodArgsArraysIdentical(
887
+ requiredMethodSig.args,
888
+ actualValueMethodSig.args,
889
+ )
890
+ const resultsMatch = areMethodArgsArraysIdentical(
891
+ requiredMethodSig.returns,
892
+ actualValueMethodSig.returns,
893
+ )
894
+ return paramsMatch && resultsMatch
895
+ } else {
896
+ // Value has TypeInfo listing methods, but this specific method isn't listed.
897
+ // This implies a mismatch for strict signature check based on TypeInfo.
898
+ return false
899
+ }
900
+ }
901
+ }
902
+
903
+ // If the function exists and there's no type info, assume it matches.
904
+ // TODO check this
905
+ return true
906
+ },
907
+ )
908
+
909
+ if (allMethodsMatch) {
910
+ return { value: value as T, ok: true }
911
+ }
912
+ }
913
+
914
+ if (
915
+ isStructTypeInfo(normalizedType) &&
916
+ normalizedType.fields &&
917
+ typeof value === 'object' &&
918
+ value !== null
919
+ ) {
920
+ const fieldNames = Object.keys(normalizedType.fields)
921
+ const valueFields = Object.keys(value)
922
+
923
+ // For struct type assertions, we need exact field matching
924
+ const structFieldsMatch =
925
+ fieldNames.length === valueFields.length &&
926
+ fieldNames.every((field: string) => field in value) &&
927
+ valueFields.every((field) => fieldNames.includes(field))
928
+
929
+ if (structFieldsMatch) {
930
+ const typesMatch = Object.entries(normalizedType.fields).every(
931
+ ([fieldName, fieldType]) => {
932
+ return matchesType(
933
+ value[fieldName],
934
+ normalizeTypeInfo(fieldType as TypeInfo | string),
935
+ )
936
+ },
937
+ )
938
+
939
+ return { value: value as T, ok: typesMatch }
940
+ } else {
941
+ return { value: null as unknown as T, ok: false }
942
+ }
943
+ }
944
+
945
+ if (
946
+ isMapTypeInfo(normalizedType) &&
947
+ typeof value === 'object' &&
948
+ value !== null
949
+ ) {
950
+ if (normalizedType.keyType || normalizedType.elemType) {
951
+ let entries: [any, any][] = []
952
+
953
+ if (value instanceof Map) {
954
+ entries = Array.from(value.entries())
955
+ } else {
956
+ entries = Object.entries(value)
957
+ }
958
+
959
+ if (entries.length === 0) {
960
+ return { value: value as T, ok: true }
961
+ }
962
+
963
+ const sampleSize = Math.min(5, entries.length)
964
+ for (let i = 0; i < sampleSize; i++) {
965
+ const [k, v] = entries[i]
966
+
967
+ if (normalizedType.keyType) {
968
+ if (
969
+ !validateMapKey(
970
+ k,
971
+ normalizeTypeInfo(normalizedType.keyType as string | TypeInfo),
972
+ )
973
+ ) {
974
+ return { value: null as unknown as T, ok: false }
975
+ }
976
+ }
977
+
978
+ if (normalizedType.elemType) {
979
+ const elemTypeInfo = normalizeTypeInfo(
980
+ normalizedType.elemType as string | TypeInfo,
981
+ )
982
+ if (!matchesType(v, elemTypeInfo)) {
983
+ return { value: null as unknown as T, ok: false }
984
+ }
985
+ }
986
+ }
987
+
988
+ // If we get here, the map type assertion passes
989
+ return { value: value as T, ok: true }
990
+ }
991
+ }
992
+
993
+ const matches = matchesType(value, normalizedType)
994
+
995
+ if (matches) {
996
+ return { value: value as T, ok: true }
997
+ }
998
+
999
+ // If we get here, the assertion failed
1000
+ // For registered types, use the zero value from the registry
1001
+ if (typeof typeInfo === 'string') {
1002
+ const registeredType = typeRegistry.get(typeInfo)
1003
+ if (registeredType && registeredType.zeroValue !== undefined) {
1004
+ return { value: registeredType.zeroValue as T, ok: false }
1005
+ }
1006
+ } else if (normalizedType.zeroValue !== undefined) {
1007
+ return { value: normalizedType.zeroValue as T, ok: false }
1008
+ }
1009
+
1010
+ return { value: null as unknown as T, ok: false }
1011
+ }
1012
+
1013
+ /**
1014
+ * Performs a type assertion on a value against a specified type.
1015
+ * Returns the value (cast to type T) if the assertion is successful,
1016
+ * otherwise throws a runtime error.
1017
+ * This is used to implement Go's single-value type assertion: value := x.(Type)
1018
+ *
1019
+ * @param value The value to check against the type
1020
+ * @param typeInfo The type information to check against (can be a string name or TypeInfo object)
1021
+ * @returns The asserted value if the assertion succeeded
1022
+ * @throws Error if the type assertion fails
1023
+ */
1024
+ export function mustTypeAssert<T>(value: any, typeInfo: string | TypeInfo): T {
1025
+ const { value: assertedValue, ok } = typeAssert<T>(value, typeInfo)
1026
+ if (!ok) {
1027
+ const targetTypeName =
1028
+ typeof typeInfo === 'string' ? typeInfo : (
1029
+ typeInfo.name || JSON.stringify(typeInfo)
1030
+ )
1031
+ let valueTypeName: string | 'nil' = typeof value
1032
+ if (value && value.constructor && value.constructor.name) {
1033
+ valueTypeName = value.constructor.name
1034
+ }
1035
+ if (value === null) {
1036
+ valueTypeName = 'nil'
1037
+ }
1038
+ throw new Error(
1039
+ `inline type conversion panic: value is ${valueTypeName}, not ${targetTypeName}`,
1040
+ )
1041
+ }
1042
+ return assertedValue
1043
+ }
1044
+
1045
+ /**
1046
+ * Checks if a value is of a specific type.
1047
+ * Similar to typeAssert but only returns a boolean without extracting the value.
1048
+ *
1049
+ * @param value The value to check
1050
+ * @param typeInfo The type information to check against
1051
+ * @returns True if the value matches the type, false otherwise
1052
+ */
1053
+ export function is(value: any, typeInfo: string | TypeInfo): boolean {
1054
+ return matchesType(value, normalizeTypeInfo(typeInfo))
1055
+ }
1056
+
1057
+ /**
1058
+ * Represents a case in a type switch statement.
1059
+ * Each case matches against one or more types and contains a body function to execute when matched.
1060
+ */
1061
+ export interface TypeSwitchCase {
1062
+ types: (string | TypeInfo)[] // Array of types for this case (e.g., case int, string:)
1063
+ body: (value?: any) => void // Function representing the case body. 'value' is the asserted value if applicable.
1064
+ }
1065
+
1066
+ /**
1067
+ * Helper for Go's type switch statement.
1068
+ * Executes the body of the first case whose type matches the value.
1069
+ *
1070
+ * @param value The value being switched upon.
1071
+ * @param cases An array of TypeSwitchCase objects.
1072
+ * @param defaultCase Optional function for the default case.
1073
+ */
1074
+ export function typeSwitch(
1075
+ value: any,
1076
+ cases: TypeSwitchCase[],
1077
+ defaultCase?: () => void,
1078
+ ): void {
1079
+ for (const caseObj of cases) {
1080
+ // For cases with multiple types (case T1, T2:), use $.is
1081
+ if (caseObj.types.length > 1) {
1082
+ const matchesAny = caseObj.types.some((typeInfo) => is(value, typeInfo))
1083
+ if (matchesAny) {
1084
+ // For multi-type cases, the case variable (if any) gets the original value
1085
+ caseObj.body(value)
1086
+ return // Found a match, exit switch
1087
+ }
1088
+ } else if (caseObj.types.length === 1) {
1089
+ // For single-type cases (case T:), use $.typeAssert to get the typed value and ok status
1090
+ const typeInfo = caseObj.types[0]
1091
+ const { value: assertedValue, ok } = typeAssert(value, typeInfo)
1092
+ if (ok) {
1093
+ // Pass the asserted value to the case body function
1094
+ caseObj.body(assertedValue)
1095
+
1096
+ return // Found a match, exit switch
1097
+ }
1098
+ }
1099
+ // Note: Cases with 0 types are not valid in Go type switches
1100
+ }
1101
+
1102
+ // If no case matched and a default case exists, execute it
1103
+ if (defaultCase) {
1104
+ defaultCase()
1105
+ }
1106
+ }