Skip to content

Commit

Permalink
fix(type): ensure union check in deserialize mode to handle property …
Browse files Browse the repository at this point in the history
…with default value correctly

closes #623
  • Loading branch information
marcj committed Feb 7, 2025
1 parent 452dc6a commit 11c2116
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 2 deletions.
10 changes: 8 additions & 2 deletions packages/type/src/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1404,7 +1404,11 @@ export function typeGuardObjectLiteral(type: TypeObjectLiteral | TypeClass, stat

lines.push(`let ${checkValid} = false;` + template);
} else {
const optionalCheck = member.optional
let optional = isOptional(member);
if (state.validation === 'loose' && member.kind === ReflectionKind.property && member.default) {
optional = true;
}
let optionalCheck = optional
? `${propertyAccessor} !== undefined && ` + (!isNullable(member) ? `${propertyAccessor} !== null && ` : '')
: '';
existing.push(readName);
Expand Down Expand Up @@ -1714,6 +1718,7 @@ export function serializePropertyOrParameter(type: TypePropertySignature | TypeP
export function validatePropertyOrParameter(type: TypePropertySignature | TypeProperty | TypeParameter, state: TemplateState) {
const optional = isOptional(type)
const hasDefault = hasDefaultValue(type);
throw new Error('asd');

state.addCode(`
if (${state.accessor} === undefined) {
Expand Down Expand Up @@ -1776,6 +1781,7 @@ export function handleUnion(type: TypeUnion, state: TemplateState) {

//when validation=true and not all specificalities are included, we only use 1, which is used for strict validation()/is().
if (state.validation === 'strict' && specificality !== 1) continue;
const validation = !state.validation ? 'loose' : state.validation;

for (const t of type.types) {
const fn = createTypeGuardFunction(
Expand All @@ -1784,7 +1790,7 @@ export function handleUnion(type: TypeUnion, state: TemplateState) {
.forRegistry(typeGuard)
//if validation is not set, we are in deserialize mode, so we need to activate validation
//for this state.
.withValidation(!state.validation ? 'loose' : state.validation)
.withValidation(validation)
.includeAllSpecificalities(state.registry.serializer.typeGuards),
undefined, false,
);
Expand Down
32 changes: 32 additions & 0 deletions packages/type/tests/type-spec.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -882,3 +882,35 @@ test('global symbol names as method names', () => {
const method = type.getMethod(Symbol.iterator);
expect(method).toBeDefined();
});

test('union with optional fields', () => {
class Foo {
__kind!: 'Foo';
a?: string;
}

class Bar {
__kind!: 'Bar';
}

expect(deserializeFromJson<Bar | Foo>({__kind: 'Bar'})).toEqual({__kind: 'Bar'});
expect(deserializeFromJson<Bar | Foo>({__kind: 'Foo', a: 'a'})).toEqual({__kind: 'Foo', a: 'a'});
expect(deserializeFromJson<Bar | Foo>({__kind: 'Foo'})).toEqual({__kind: 'Foo'});
});

test('union with default fields', () => {
class Foo {
__kind!: 'Foo';
a: string = 'a';
}

class Bar {
__kind!: 'Bar';
}

type t = Bar | Foo;

expect(deserializeFromJson<t>({__kind: 'Bar'})).toEqual({__kind: 'Bar'});
expect(deserializeFromJson<t>({__kind: 'Foo', a: 'b'})).toEqual({__kind: 'Foo', a: 'b'});
expect(deserializeFromJson<t>({__kind: 'Foo'})).toEqual({__kind: 'Foo', a: 'a'});
});

0 comments on commit 11c2116

Please sign in to comment.