This is an automated email from the ASF dual-hosted git repository.

wangweipeng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git


The following commit(s) were added to refs/heads/main by this push:
     new 27f2b64b8 feat(xlang): add unsigned integer type support for 
JavaScript (#3139)
27f2b64b8 is described below

commit 27f2b64b845129f30a115d01788c7bd4369b8680
Author: Ayush Kumar <[email protected]>
AuthorDate: Thu Jan 15 08:23:50 2026 +0530

    feat(xlang): add unsigned integer type support for JavaScript (#3139)
    
    ## Why?
    
    As requested in Issue #3130, JavaScript implementation was missing
    support for unsigned integer types that are already available in other
    Fory language implementations (Rust, Go, C++, Python). This PR adds
    complete unsigned integer support for JavaScript.
    
    ## What does this PR do?
    
    This PR implements schema-based support for unsigned integer types in
    the Fory JavaScript/TypeScript library:
    
    ### Added Types
    
    - `uint8`
    - `uint16`
    - `uint32`
    - `uint64`
    - `var_uint32`
    - `var_uint64`
    - `tagged_uint64`
    
    ### Changes Made
    
    #### 1. `lib/type.ts`
    Added 7 unsigned integer types to the `InternalSerializerType` enum:
    
    ```typescript
    export enum InternalSerializerType {
      UINT8,
      UINT16,
      UINT32,
      VAR_UINT32,
      UINT64,
      VAR_UINT64,
      TAGGED_UINT64,
      // ...
    }
    ```
    
    #### 2. `lib/typeInfo.ts`
    **Added Type helper functions** for user-facing API:
    
    ```typescript
    export const Type = {
      uint8() {
        return TypeInfo.fromNonParam(InternalSerializerType.UINT8 as const, 
TypeId.UINT8);
      },
      uint16() {
        return TypeInfo.fromNonParam(InternalSerializerType.UINT16 as const, 
TypeId.UINT16);
      },
      // ...
    }
    ```
    
    **Updated TypeScript type hints** for proper type inference:
    
    - Added unsigned types to HintInput number type union (returns `number`
    for 8/16/32-bit, `bigint` for 64-bit)
    - Added unsigned types to HintResult type union for proper
    deserialization type inference
    
    #### 3. `lib/gen/number.ts`
    
    Registered serializers for all 7 unsigned integer types using the
    existing buildNumberSerializer pattern:
    
    ```typescript
    CodegenRegistry.register(InternalSerializerType.UINT8,
      buildNumberSerializer(
        (builder, accessor) => builder.writer.uint8(accessor),
        builder => builder.reader.uint8()
      )
    );
    
    CodegenRegistry.register(InternalSerializerType.UINT16,
      buildNumberSerializer(
        (builder, accessor) => builder.writer.uint16(accessor),
        builder => builder.reader.uint16()
      )
    );
    //....
    ```
    
    #### 4. `test/number.test.ts`
    
    Added comprehensive tests for all unsigned types:
    
    ```typescript
    test('should uint8 work', () => {
      const fory = new Fory({ refTracking: true });
      const serializer = fory.registerSerializer(Type.struct({
        typeName: "example.foo"
      }, {
        a: Type.uint8()
      })).serializer;
      const input = fory.serialize({ a: 255 }, serializer);
      const result = fory.deserialize(input);
      expect(result).toEqual({ a: 255 });
    });
    
    test('should uint16 work', () => {
      const fory = new Fory({ refTracking: true });
      const serializer = fory.registerSerializer(Type.struct({
        typeName: "example.foo"
      }, {
        a: Type.uint16()
      })).serializer;
      const input = fory.serialize({ a: 65535 }, serializer);
      const result = fory.deserialize(input);
      expect(result).toEqual({ a: 65535 });
    });
    
    \\...
    ```
    
    ### Usage Example
    
    ```typescript
    import { Fory, Type } from '@apache-fory/fory';
    
    const fory = new Fory();
    const serializer = fory.registerSerializer(Type.struct({
      typeName: 'Foo'
    }, {
      f1: Type.uint32(),
      f2: Type.varUInt32(),
      f3: Type.uint64()
    })).serializer;
    
    const data = { f1: 4294967295, f2: 1000, f3: 18446744073709551615n };
    const bytes = fory.serialize(data, serializer);
    const result = fory.deserialize(bytes);
    ```
    
    ## Related issues
    
    - Closes #3130
    
    ## Does this PR introduce any user-facing change?
    
    - [x] **Does this PR introduce any public API change?**
    - **Yes**: Adds 7 new Type helper functions (`Type.uint8()`,
    `Type.uint16()`, `Type.uint32()`, `Type.varUInt32()`, `Type.uint64()`,
    `Type.varUInt64()`, `Type.taggedUInt64()`)
    - These are **additive changes only** - no breaking changes to existing
    APIs
    
    - [x] **Does this PR introduce any binary protocol compatibility
    change?**
    - **Yes**: Adds support for 7 new TypeId values (9-15) for unsigned
    integers
    - **Cross-language compatible**: Uses the same TypeId values as other
    Fory implementations
      - **Backward compatible**: Existing serialized data remains compatible
    
    ## Benchmark
    
    N/A - this PR adds new functionality without modifying existing
    serialization paths. The underlying writer/reader methods `uint8()`,
    `uint16()` were already implemented and optimized; this PR only exposes
    them through the type system.
    
    ---
---
 javascript/.eslintrc.cjs                   |   2 +-
 javascript/packages/fory/lib/gen/number.ts |  49 +++++
 javascript/packages/fory/lib/type.ts       |   7 +
 javascript/packages/fory/lib/typeInfo.ts   | 308 +++++++++++++++++------------
 javascript/test/number.test.ts             |  84 ++++++++
 5 files changed, 324 insertions(+), 126 deletions(-)

diff --git a/javascript/.eslintrc.cjs b/javascript/.eslintrc.cjs
index eebadd856..16291aee4 100644
--- a/javascript/.eslintrc.cjs
+++ b/javascript/.eslintrc.cjs
@@ -38,5 +38,5 @@ module.exports = {
     "@typescript-eslint/no-explicit-any": "off",
     "@typescript-eslint/no-var-requires": "off",
   },
-  ignorePatterns: ["test/*", "dist/*", "*.js", "murmurHash3.ts", 
"packages/**/dist/*", "packages/**/build/*"],
+  ignorePatterns: ["test/*", "dist/*", "*.js", "murmurHash3.ts", 
"packages/**/dist/*", "packages/**/build/*", "**/typeInfo.ts"],
 };
diff --git a/javascript/packages/fory/lib/gen/number.ts 
b/javascript/packages/fory/lib/gen/number.ts
index 23297b141..a5a122c5b 100644
--- a/javascript/packages/fory/lib/gen/number.ts
+++ b/javascript/packages/fory/lib/gen/number.ts
@@ -110,3 +110,52 @@ CodegenRegistry.register(InternalSerializerType.FLOAT64,
     builder => builder.reader.float64()
   )
 );
+
+CodegenRegistry.register(InternalSerializerType.UINT8,
+  buildNumberSerializer(
+    (builder, accessor) => builder.writer.uint8(accessor),
+    builder => builder.reader.uint8()
+  )
+);
+
+CodegenRegistry.register(InternalSerializerType.UINT16,
+  buildNumberSerializer(
+    (builder, accessor) => builder.writer.uint16(accessor),
+    builder => builder.reader.uint16()
+  )
+);
+
+CodegenRegistry.register(InternalSerializerType.UINT32,
+  buildNumberSerializer(
+    (builder, accessor) => builder.writer.uint32(accessor),
+    builder => builder.reader.uint32()
+  )
+);
+
+CodegenRegistry.register(InternalSerializerType.VAR_UINT32,
+  buildNumberSerializer(
+    (builder, accessor) => builder.writer.varUInt32(accessor),
+    builder => builder.reader.varUInt32()
+  )
+);
+
+CodegenRegistry.register(InternalSerializerType.UINT64,
+  buildNumberSerializer(
+    (builder, accessor) => builder.writer.uint64(accessor),
+    builder => builder.reader.uint64()
+  )
+);
+
+CodegenRegistry.register(InternalSerializerType.VAR_UINT64,
+  buildNumberSerializer(
+    (builder, accessor) => builder.writer.varUInt64(accessor),
+    builder => builder.reader.varUInt64()
+  )
+);
+
+CodegenRegistry.register(InternalSerializerType.TAGGED_UINT64,
+  buildNumberSerializer(
+    (builder, accessor) => builder.writer.varUInt64(accessor),
+    builder => builder.reader.varUInt64()
+  )
+);
diff --git a/javascript/packages/fory/lib/type.ts 
b/javascript/packages/fory/lib/type.ts
index aafb9c47c..185b83d98 100644
--- a/javascript/packages/fory/lib/type.ts
+++ b/javascript/packages/fory/lib/type.ts
@@ -142,6 +142,13 @@ export enum InternalSerializerType {
   INT64,
   VARINT64,
   TAGGED_INT64,
+  UINT8,
+  UINT16,
+  UINT32,
+  VAR_UINT32,
+  UINT64,
+  VAR_UINT64,
+  TAGGED_UINT64,
   FLOAT16,
   FLOAT32,
   FLOAT64,
diff --git a/javascript/packages/fory/lib/typeInfo.ts 
b/javascript/packages/fory/lib/typeInfo.ts
index e98c7da02..6a73d88e0 100644
--- a/javascript/packages/fory/lib/typeInfo.ts
+++ b/javascript/packages/fory/lib/typeInfo.ts
@@ -29,12 +29,12 @@ const initMeta = (target: new () => any, typeInfo: 
TypeInfo) => {
       ...(
         TypeId.IS_NAMED_TYPE(typeInfo.typeId)
           ? {
-              namespace: typeInfo.namespace,
-              typeName: typeInfo.typeName,
-            }
+            namespace: typeInfo.namespace,
+            typeName: typeInfo.typeName,
+          }
           : {
-              typeId: typeInfo.typeId,
-            }
+            typeId: typeInfo.typeId,
+          }
       ),
     }, targetFields.get(target) || {}, {
       withConstructor: true,
@@ -253,8 +253,8 @@ type Props<T> = T extends {
   };
 }
   ? {
-      [P in keyof T2]?: (InputType<T2[P]> | null);
-    }
+    [P in keyof T2]?: (InputType<T2[P]> | null);
+  }
   : unknown;
 
 type InnerProps<T> = T extends {
@@ -298,8 +298,8 @@ type OneofProps<T> = T extends {
   };
 }
   ? {
-      [P in keyof T2]?: (InputType<T2[P]> | null);
-    }
+    [P in keyof T2]?: (InputType<T2[P]> | null);
+  }
   : unknown;
 
 type OneofResult<T> = T extends {
@@ -327,66 +327,74 @@ export type HintInput<T> = T extends unknown ? any : T 
extends {
   : T extends {
     type: InternalSerializerType.STRING;
   }
-    ? string
-    : T extends {
-      type: InternalSerializerType.TUPLE;
-    }
-      ? TupleProps<T>
-      : T extends {
-        type:
-          | InternalSerializerType.INT8
-          | InternalSerializerType.INT16
-          | InternalSerializerType.INT32
-          | InternalSerializerType.VARINT32
-          | InternalSerializerType.FLOAT16
-          | InternalSerializerType.FLOAT32
-          | InternalSerializerType.FLOAT64;
-      }
-        ? number
+  ? string
+  : T extends {
+    type: InternalSerializerType.TUPLE;
+  }
+  ? TupleProps<T>
+  : T extends {
+    type:
+    | InternalSerializerType.INT8
+    | InternalSerializerType.INT16
+    | InternalSerializerType.INT32
+    | InternalSerializerType.VARINT32
+    | InternalSerializerType.UINT8
+    | InternalSerializerType.UINT16
+    | InternalSerializerType.UINT32
+    | InternalSerializerType.VAR_UINT32
+    | InternalSerializerType.FLOAT16
+    | InternalSerializerType.FLOAT32
+    | InternalSerializerType.FLOAT64;
+  }
+  ? number
 
-        : T extends {
-          type: InternalSerializerType.VARINT64
-            | InternalSerializerType.TAGGED_INT64
-            | InternalSerializerType.INT64;
-        }
-          ? bigint
-          : T extends {
-            type: InternalSerializerType.MAP;
-          }
-            ? MapProps<T>
-            : T extends {
-              type: InternalSerializerType.SET;
-            }
-              ? SetProps<T>
-              : T extends {
-                type: InternalSerializerType.ARRAY;
-              }
-                ? InnerProps<T>
-                : T extends {
-                  type: InternalSerializerType.BOOL;
-                }
-                  ? boolean
-                  : T extends {
-                    type: InternalSerializerType.DURATION;
-                  }
-                    ? Date
-                    : T extends {
-                      type: InternalSerializerType.TIMESTAMP;
-                    }
-                      ? number
-                      : T extends {
-                        type: InternalSerializerType.ANY;
-                      }
-                        ? any
-                        : T extends { type: InternalSerializerType.BINARY;
-                        }
-                          ? Uint8Array
-                          : T extends {
-                            type: InternalSerializerType.ENUM;
-                          }
-                            ? EnumProps<T> : T extends {
-                              type: InternalSerializerType.ONEOF;
-                            } ? OneofProps<T> : unknown;
+  : T extends {
+    type: InternalSerializerType.VARINT64
+    | InternalSerializerType.TAGGED_INT64
+    | InternalSerializerType.INT64
+    | InternalSerializerType.UINT64
+    | InternalSerializerType.VAR_UINT64
+    | InternalSerializerType.TAGGED_UINT64;
+  }
+  ? bigint
+  : T extends {
+    type: InternalSerializerType.MAP;
+  }
+  ? MapProps<T>
+  : T extends {
+    type: InternalSerializerType.SET;
+  }
+  ? SetProps<T>
+  : T extends {
+    type: InternalSerializerType.ARRAY;
+  }
+  ? InnerProps<T>
+  : T extends {
+    type: InternalSerializerType.BOOL;
+  }
+  ? boolean
+  : T extends {
+    type: InternalSerializerType.DURATION;
+  }
+  ? Date
+  : T extends {
+    type: InternalSerializerType.TIMESTAMP;
+  }
+  ? number
+  : T extends {
+    type: InternalSerializerType.ANY;
+  }
+  ? any
+  : T extends {
+    type: InternalSerializerType.BINARY;
+  }
+  ? Uint8Array
+  : T extends {
+    type: InternalSerializerType.ENUM;
+  }
+  ? EnumProps<T> : T extends {
+    type: InternalSerializerType.ONEOF;
+  } ? OneofProps<T> : unknown;
 
 export type ResultType<T> = T extends TypeInfo<infer M> ? HintResult<M> : 
HintResult<T>;
 
@@ -397,64 +405,72 @@ export type HintResult<T> = T extends never ? any : T 
extends {
   : T extends {
     type: InternalSerializerType.STRING;
   }
-    ? string
-    : T extends {
-      type: InternalSerializerType.TUPLE;
-    }
-      ? TupleProps<T>
-      : T extends {
-        type:
-          | InternalSerializerType.INT8
-          | InternalSerializerType.INT16
-          | InternalSerializerType.INT32
-          | InternalSerializerType.VARINT32
-          | InternalSerializerType.FLOAT16
-          | InternalSerializerType.FLOAT32
-          | InternalSerializerType.FLOAT64;
-      }
-        ? number
+  ? string
+  : T extends {
+    type: InternalSerializerType.TUPLE;
+  }
+  ? TupleProps<T>
+  : T extends {
+    type:
+    | InternalSerializerType.INT8
+    | InternalSerializerType.INT16
+    | InternalSerializerType.INT32
+    | InternalSerializerType.VARINT32
+    | InternalSerializerType.UINT8
+    | InternalSerializerType.UINT16
+    | InternalSerializerType.UINT32
+    | InternalSerializerType.VAR_UINT32
+    | InternalSerializerType.FLOAT16
+    | InternalSerializerType.FLOAT32
+    | InternalSerializerType.FLOAT64;
+  }
+  ? number
 
-        : T extends {
-          type: InternalSerializerType.TAGGED_INT64
-            | InternalSerializerType.INT64;
-        }
-          ? bigint
-          : T extends {
-            type: InternalSerializerType.MAP;
-          }
-            ? MapProps<T>
-            : T extends {
-              type: InternalSerializerType.SET;
-            }
-              ? SetProps<T>
-              : T extends {
-                type: InternalSerializerType.ARRAY;
-              }
-                ? InnerProps<T>
-                : T extends {
-                  type: InternalSerializerType.BOOL;
-                }
-                  ? boolean
-                  : T extends {
-                    type: InternalSerializerType.DURATION;
-                  }
-                    ? Date
-                    : T extends {
-                      type: InternalSerializerType.TIMESTAMP;
-                    }
-                      ? number
-                      : T extends { type: InternalSerializerType.BINARY;
-                      }
-                        ? Uint8Array : T extends {
-                          type: InternalSerializerType.ANY;
-                        }
-                          ? any
-                          : T extends {
-                            type: InternalSerializerType.ENUM;
-                          }
-                            ? EnumProps<T> : T extends {
-                              type: InternalSerializerType.ONEOF;
-                            } ? OneofResult<T> : unknown;
+  : T extends {
+    type: InternalSerializerType.TAGGED_INT64
+    | InternalSerializerType.INT64
+    | InternalSerializerType.UINT64
+    | InternalSerializerType.VAR_UINT64
+    | InternalSerializerType.TAGGED_UINT64;
+  }
+  ? bigint
+  : T extends {
+    type: InternalSerializerType.MAP;
+  }
+  ? MapProps<T>
+  : T extends {
+    type: InternalSerializerType.SET;
+  }
+  ? SetProps<T>
+  : T extends {
+    type: InternalSerializerType.ARRAY;
+  }
+  ? InnerProps<T>
+  : T extends {
+    type: InternalSerializerType.BOOL;
+  }
+  ? boolean
+  : T extends {
+    type: InternalSerializerType.DURATION;
+  }
+  ? Date
+  : T extends {
+    type: InternalSerializerType.TIMESTAMP;
+  }
+  ? number
+  : T extends {
+    type: InternalSerializerType.BINARY;
+  }
+  ? Uint8Array : T extends {
+    type: InternalSerializerType.ANY;
+  }
+  ? any
+  : T extends {
+    type: InternalSerializerType.ENUM;
+  }
+  ? EnumProps<T> : T extends {
+    type: InternalSerializerType.ONEOF;
+  } ? OneofResult<T> : unknown;
 
 export const Type = {
   any() {
@@ -593,6 +609,48 @@ export const Type = {
 
     );
   },
+  uint8() {
+    return TypeInfo.fromNonParam(
+      InternalSerializerType.UINT8 as const,
+      (TypeId.UINT8),
+    );
+  },
+  uint16() {
+    return TypeInfo.fromNonParam(
+      InternalSerializerType.UINT16 as const,
+      (TypeId.UINT16),
+    );
+  },
+  uint32() {
+    return TypeInfo.fromNonParam(
+      InternalSerializerType.UINT32 as const,
+      (TypeId.UINT32),
+    );
+  },
+  varUInt32() {
+    return TypeInfo.fromNonParam(
+      InternalSerializerType.VAR_UINT32 as const,
+      (TypeId.VAR_UINT32),
+    );
+  },
+  uint64() {
+    return TypeInfo.fromNonParam(
+      InternalSerializerType.UINT64 as const,
+      (TypeId.UINT64),
+    );
+  },
+  varUInt64() {
+    return TypeInfo.fromNonParam(
+      InternalSerializerType.VAR_UINT64 as const,
+      (TypeId.VAR_UINT64),
+    );
+  },
+  taggedUInt64() {
+    return TypeInfo.fromNonParam(
+      InternalSerializerType.TAGGED_UINT64 as const,
+      (TypeId.TAGGED_UINT64),
+    );
+  },
   binary() {
     return TypeInfo.fromNonParam(
       InternalSerializerType.BINARY as const,
diff --git a/javascript/test/number.test.ts b/javascript/test/number.test.ts
index 2835478e5..dd5d54080 100644
--- a/javascript/test/number.test.ts
+++ b/javascript/test/number.test.ts
@@ -107,6 +107,90 @@ describe('number', () => {
     );
     expect(result.a).toBeCloseTo(1.2)
   });
+
+  test('should uint8 work', () => {
+    const fory = new Fory({ refTracking: true });
+    const serializer = fory.registerSerializer(Type.struct({
+      typeName: "example.foo"
+    }, {
+      a: Type.uint8()
+    })).serializer;
+    const input = fory.serialize({ a: 255 }, serializer);
+    const result = fory.deserialize(input);
+    expect(result).toEqual({ a: 255 });
+  });
+
+  test('should uint16 work', () => {
+    const fory = new Fory({ refTracking: true });
+    const serializer = fory.registerSerializer(Type.struct({
+      typeName: "example.foo"
+    }, {
+      a: Type.uint16()
+    })).serializer;
+    const input = fory.serialize({ a: 65535 }, serializer);
+    const result = fory.deserialize(input);
+    expect(result).toEqual({ a: 65535 });
+  });
+
+  test('should uint32 work', () => {
+    const fory = new Fory({ refTracking: true });
+    const serializer = fory.registerSerializer(Type.struct({
+      typeName: "example.foo"
+    }, {
+      a: Type.uint32()
+    })).serializer;
+    const input = fory.serialize({ a: 4294967295 }, serializer);
+    const result = fory.deserialize(input);
+    expect(result).toEqual({ a: 4294967295 });
+  });
+
+  test('should varUInt32 work', () => {
+    const fory = new Fory({ refTracking: true });
+    const serializer = fory.registerSerializer(Type.struct({
+      typeName: "example.foo"
+    }, {
+      a: Type.varUInt32()
+    })).serializer;
+    const input = fory.serialize({ a: 1000000 }, serializer);
+    const result = fory.deserialize(input);
+    expect(result).toEqual({ a: 1000000 });
+  });
+
+  test('should uint64 work', () => {
+    const fory = new Fory({ refTracking: true });
+    const serializer = fory.registerSerializer(Type.struct({
+      typeName: "example.foo"
+    }, {
+      a: Type.uint64()
+    })).serializer;
+    const input = fory.serialize({ a: 18446744073709551615n }, serializer);
+    const result = fory.deserialize(input);
+    expect(result).toEqual({ a: 18446744073709551615n });
+  });
+
+  test('should varUInt64 work', () => {
+    const fory = new Fory({ refTracking: true });
+    const serializer = fory.registerSerializer(Type.struct({
+      typeName: "example.foo"
+    }, {
+      a: Type.varUInt64()
+    })).serializer;
+    const input = fory.serialize({ a: 1n }, serializer);
+    const result = fory.deserialize(input);
+    expect(result).toEqual({ a: 1n });
+  });
+
+  test('should taggedUInt64 work', () => {
+    const fory = new Fory({ refTracking: true });
+    const serializer = fory.registerSerializer(Type.struct({
+      typeName: "example.foo"
+    }, {
+      a: Type.taggedUInt64()
+    })).serializer;
+    const input = fory.serialize({ a: 1n }, serializer);
+    const result = fory.deserialize(input);
+    expect(result).toEqual({ a: 1n });
+  });
 });
 
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to