diff --git a/packages/type/src/path.ts b/packages/type/src/path.ts index 8a0b58360..9841a457b 100644 --- a/packages/type/src/path.ts +++ b/packages/type/src/path.ts @@ -18,6 +18,34 @@ function pathResolverCode(type: Type, compilerContext: CompilerContext, jitStack ${pathResolverCode(type.type, compilerContext, jitStack)} } `; + } else if (type.kind === ReflectionKind.tupleMember) { + return ` + if (!path) return ${compilerContext.reserveVariable('type', type)}; + ${pathResolverCode(type.type, compilerContext, jitStack)}; + `; + } else if (type.kind === ReflectionKind.tuple) { + const cases: string[] = []; + for (let i = 0; i < type.types.length; i++) { + cases.push(` + case "${i}": { + ${pathResolverCode(type.types[i], compilerContext, jitStack)} + } + `); + } + + return ` + { + const dotIndex = path.indexOf('.'); + const segment = dotIndex === -1 ? path : path.substr(0, dotIndex); + path = dotIndex === -1 ? '' : path.substr(dotIndex + 1); + switch (segment) { + ${cases.join('\n')} + default: { + return undefined; + } + } + } + `; } else if (type.kind === ReflectionKind.class && type.classType === Set) { } else if (type.kind === ReflectionKind.class && type.classType === Map) { } else if (type.kind === ReflectionKind.union) { @@ -72,6 +100,8 @@ export function pathResolver(type?: ReceiveType, jitStack: JitStack = new const pathName = dotIndex === -1 ? path : path.substr(0, dotIndex); path = dotIndex === -1 ? '' : path.substr(dotIndex + 1); + if (!pathName) return ${compilerContext.reserveVariable('type', type)}; + switch(pathName) { ${lines.join('\n')} default: { diff --git a/packages/type/tests/path.spec.ts b/packages/type/tests/path.spec.ts index dd7ee6f8d..2d470f1db 100644 --- a/packages/type/tests/path.spec.ts +++ b/packages/type/tests/path.spec.ts @@ -50,6 +50,22 @@ test('pathResolver deep array object', () => { expect(resolver('b.0.0.c')).toMatchObject({ kind: ReflectionKind.propertySignature, type: { kind: ReflectionKind.boolean } }); }); +test('pathResolver deep array object', () => { + type t = { a: [string, number, [boolean, string, { b: number }]?] }; + + const resolver = pathResolver(); + + expect(resolver('a')).toMatchObject({ kind: ReflectionKind.propertySignature, type: { kind: ReflectionKind.tuple } }); + + expect(resolver('a.0')).toMatchObject({ kind: ReflectionKind.tupleMember, type: { kind: ReflectionKind.string } }); + expect(resolver('a.1')).toMatchObject({ kind: ReflectionKind.tupleMember, type: { kind: ReflectionKind.number } }); + + expect(resolver('a.2.0')).toMatchObject({ kind: ReflectionKind.tupleMember, type: { kind: ReflectionKind.boolean } }); + expect(resolver('a.2.1')).toMatchObject({ kind: ReflectionKind.tupleMember, type: { kind: ReflectionKind.string } }); + expect(resolver('a.2.2')).toMatchObject({ kind: ReflectionKind.tupleMember, type: { kind: ReflectionKind.objectLiteral } }); + expect(resolver('a.2.2.b')).toMatchObject({ kind: ReflectionKind.propertySignature }); +}); + test('pathResolver deep class', () => { interface Config { name: string; diff --git a/packages/type/tests/typeguard.spec.ts b/packages/type/tests/typeguard.spec.ts index 130edcedf..e85a7a942 100644 --- a/packages/type/tests/typeguard.spec.ts +++ b/packages/type/tests/typeguard.spec.ts @@ -10,6 +10,9 @@ import { expect, test } from '@jest/globals'; import { float, float32, int8, integer, PrimaryKey, Reference } from '../src/reflection/type.js'; import { is } from '../src/typeguard.js'; +import { Serializer } from '../src/serializer.js'; +import { cast } from '../src/serializer-facade.js'; +import { isReferenceInstance } from '../src/reference.js'; test('primitive string', () => { expect(is('a')).toEqual(true); @@ -198,10 +201,10 @@ test('array any', () => { expect(is(true)).toEqual(false); expect(is({})).toEqual(false); - expect(is({length:1})).toEqual(false); - expect(is({length:0})).toEqual(false); - expect(is({length:null})).toEqual(false); - expect(is({length:undefined})).toEqual(false); + expect(is({ length: 1 })).toEqual(false); + expect(is({ length: 0 })).toEqual(false); + expect(is({ length: null })).toEqual(false); + expect(is({ length: undefined })).toEqual(false); }); test('union', () => { @@ -397,8 +400,8 @@ test('class with literal and default', () => { readConcernLevel: 'local' = 'local'; } - expect(is({readConcernLevel: 'local'})).toBe(true); - expect(is({readConcernLevel: 'local2'})).toBe(false); + expect(is({ readConcernLevel: 'local' })).toBe(true); + expect(is({ readConcernLevel: 'local2' })).toBe(false); }); test('union literal', () => { @@ -406,6 +409,41 @@ test('union literal', () => { readConcernLevel: 'local' | 'majority' | 'linearizable' | 'available' = 'majority'; } - expect(is({readConcernLevel: 'majority'})).toBe(true); - expect(is({readConcernLevel: 'majority2'})).toBe(false); + expect(is({ readConcernLevel: 'majority' })).toBe(true); + expect(is({ readConcernLevel: 'majority2' })).toBe(false); +}); + +test('union classes with generic', () => { + class Group { + id: number & PrimaryKey = 0; + second: string = ''; + } + + class User { + id: number = 0; + groups: (Group & Reference)[] = []; + } + + const serializer = new Serializer(); + + const newGroup = cast({ id: 1, second: 'a' }); + + const a = cast({ + id: 1, + groups: [newGroup], + }, undefined, serializer); + + expect(a.groups[0]).toBeInstanceOf(Group); + expect(isReferenceInstance(a.groups[0])).toBe(false); + + const b = cast({ + id: 1, + groups: [1], + }, undefined, serializer); + + expect(b.groups[0]).toBeInstanceOf(Group); + expect(isReferenceInstance(b.groups[0])).toBe(true); + if (isReferenceInstance(b.groups[0])) { + //do something with this instance and fully load it + } });