From 9af344f4705943571bc0c18e73435b18c4819641 Mon Sep 17 00:00:00 2001 From: lionelhorn <50528276+lionelhorn@users.noreply.github.com> Date: Sun, 11 Aug 2024 20:20:47 +0200 Subject: [PATCH] fix(type): add scope in setter code to prevent `variable already declared` #603 (#606) - When a tuple definition contained "duplicated" union types like [number|null, number|null], the jitted function couldln't be build as some variable like `oldErrors` were already declared. Probably one block per number|null --- packages/type/src/serializer.ts | 20 ++++--- packages/type/tests/issues/tuples.spec.ts | 69 +++++++++++++++++++++++ 2 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 packages/type/tests/issues/tuples.spec.ts diff --git a/packages/type/src/serializer.ts b/packages/type/src/serializer.ts index 4f39d2f78..7b0aee376 100644 --- a/packages/type/src/serializer.ts +++ b/packages/type/src/serializer.ts @@ -1792,16 +1792,18 @@ export function handleUnion(type: TypeUnion, state: TemplateState) { ` : ''; state.addCodeForSetter(` - const oldErrors = state.errors; - if (state.errors) state.errors = []; - - //type guard for union - if (false) {} ${lines.join(' ')} - else { - ${handleErrors} - ${state.assignValidationError('type', 'No valid union member found. Valid: ' + stringifyResolvedType(type))} + { + const oldErrors = state.errors; + if (state.errors) state.errors = []; + + //type guard for union + if (false) {} ${lines.join(' ')} + else { + ${handleErrors} + ${state.assignValidationError('type', 'No valid union member found. Valid: ' + stringifyResolvedType(type))} + } + state.errors = oldErrors; } - state.errors = oldErrors; `); } diff --git a/packages/type/tests/issues/tuples.spec.ts b/packages/type/tests/issues/tuples.spec.ts new file mode 100644 index 000000000..2b74c8c8c --- /dev/null +++ b/packages/type/tests/issues/tuples.spec.ts @@ -0,0 +1,69 @@ +import { expect, test } from '@jest/globals'; +import { cast, validate } from '@deepkit/type'; + +test('cast literal obj having typed tuple [number | null, number | null] as nested prop', () => { + type MinMax = [number | null, number | null]; + + class T { + building!: { + area: MinMax + }; + } + + // const d = JSON.parse('{"building":{"area":[120,null]}}'); + const d = { + building: { + area: [120, null], + }, + }; + + const errors = validate(d); + expect(errors.length).toBe(0); + + const casted: T = cast(d); + + expect(casted.building.area[0]).toBe(120); + expect(casted.building.area[1]).toBe(null); +}); + +test('cast literal obj to T containing typed tuple', () => { + type SomeData = [string | null, string, string | null]; + + class T { + tuple!: SomeData; + } + + const d = { + tuple: [null, 'z', null], + }; + + const errors = validate(d); + expect(errors.length).toBe(0); + + const casted: T = cast(d); + + expect(casted.tuple[0]).toBe(null); + expect(casted.tuple[1]).toBe('z'); + expect(casted.tuple[2]).toBe(null); +}); + +test("cast literal obj having typed tuple [number | null, number | null] as nested prop", () => { + type MinMax = [min: number | null, max: number | null]; + + class T { + building?: { + area?: MinMax + } + } + + // const d = JSON.parse('{"building":{"area":[120,null]}}'); + + const data: T = cast({ + building: { + area: [120, null] + } + }); + + const errors = validate(data); + expect(errors.length).toBe(0); +});