fix: Uint8Array are not being filled in plainToClassFromExist
Description
Using plainToClassFromExist on classes with properties of type Uint8Array leads to data loss - the properties in the resulting class contain empty arrays.
Minimal code-snippet showcasing the problem
import { plainToClassFromExist, Type } from 'class-transformer';
class SomeClass {
@Type(() => Uint8Array)
public arr: Uint8Array;
}
const raw: SomeClass = {
arr: new Uint8Array([1, 2, 3]),
};
console.log(raw, plainToClassFromExist(SomeClass, raw));
Expected behavior
{ arr: Uint8Array(3) [ 1, 2, 3 ] } [class SomeClass] { arr: Uint8Array(3) [ 1, 2, 3 ] }
Actual behavior
{ arr: Uint8Array(3) [ 1, 2, 3 ] } [class SomeClass] { arr: Uint8Array(0) [] }
^ data being lost
Hi,
I'm encountering the same issue reported here where plainToInstance seems to incorrectly handle Uint8Array properties, causing the data to be lost during transformation.
Context:
We recently updated our project based on Prisma's recommendation (starting from v6) to use Uint8Array instead of Buffer for byte data, aligning with modern JavaScript runtimes.
Problem:
After migrating our entities to use Uint8Array for binary data fields, our unit tests involving class-transformer's plainToInstance started failing. Specifically, when transforming a plain object containing a Uint8Array property to a class instance, the resulting instance has an empty Uint8Array for that property, even though the input object contained valid data.
Minimal Reproduction Example:
Here's a simplified example demonstrating the issue:
Class Definition (TagEntity.ts):
import { Exclude, Expose } from 'class-transformer';
import { IsInt, IsString, Length } from 'class-validator'; // Assuming class-validator is also used
@Exclude()
export class TagEntity {
@Expose()
@IsString()
@Length(1, 30)
name: string;
@Expose()
nameBin: Uint8Array; // The property causing issues
@Expose()
@IsInt()
id: number;
}
Transformation Code:
import { plainToInstance } from 'class-transformer';
// Assume 'tagObject' is the input data shown below
const transformedTag = plainToInstance(TagEntity, tagObject);
console.log(transformedTag);
Input Data (Object passed to plainToInstance):
const tagObject = {
id: 2,
uuid: 'fa07ce40-7fab-4dd2-9ba2-19727d76f600', // This property is excluded, which is fine
name: 'テスト',
nameBin: Uint8Array.from([ // Example: Creating Uint8Array from an array
227, 131, 134,
227, 130, 185,
227, 131, 136
]),
description: null, // Excluded
createdAt: new Date('2025-04-19T04:29:49.000Z'), // Excluded
updatedAt: new Date('2025-04-19T04:29:49.000Z') // Excluded
};
Actual Result (Output from plainToInstance):
TagEntity { name: 'テスト', nameBin: Uint8Array(0) [], id: 2 }
// Note: nameBin is an empty Uint8Array
Expected Result:
The nameBin property in the resulting TagEntity instance should contain the same Uint8Array data as the input object.
TagEntity {
name: 'テスト',
nameBin: Uint8Array(9) [
227, 131, 134,
227, 130, 185,
227, 131, 136
],
id: 2
}
Could you please investigate this behavior? Thank you.
Hi @takakikasuga,
Have you found a workaround for this issue?