From 53216ff1893a7933451a3441157e6df1538e99ac Mon Sep 17 00:00:00 2001 From: "Marcus S. Abildskov" <8391194+marcus-sa@users.noreply.github.com> Date: Fri, 9 Feb 2024 13:22:40 +0100 Subject: [PATCH 1/5] feat(type): allow type annotation options to be extracted Signed-off-by: Marcus S. Abildskov <8391194+marcus-sa@users.noreply.github.com> --- packages/type/src/reflection/type.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/type/src/reflection/type.ts b/packages/type/src/reflection/type.ts index e9e5bafa0..babcc48d1 100644 --- a/packages/type/src/reflection/type.ts +++ b/packages/type/src/reflection/type.ts @@ -1548,7 +1548,9 @@ export interface EntityOptions { * const param1 = typeToObject(data[0]); //yes * ``` */ -export type TypeAnnotation = { __meta?: never & [T, Options] }; +export type TypeAnnotation = { __meta?: never | [T, Options] }; + +export type ExtractAnnotationOptions> = Exclude, never>[1]; /** * Type to decorate an interface/object literal with entity information. From 19e973f3e7c6289bea64ce0e34ffc41295becd90 Mon Sep 17 00:00:00 2001 From: "Marcus S. Abildskov" <8391194+marcus-sa@users.noreply.github.com> Date: Fri, 9 Feb 2024 13:23:22 +0100 Subject: [PATCH 2/5] feat(type): allow type annotation options to be extracted Signed-off-by: Marcus S. Abildskov <8391194+marcus-sa@users.noreply.github.com> --- packages/type/src/reflection/type.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/type/src/reflection/type.ts b/packages/type/src/reflection/type.ts index babcc48d1..1ca72f59e 100644 --- a/packages/type/src/reflection/type.ts +++ b/packages/type/src/reflection/type.ts @@ -1550,7 +1550,7 @@ export interface EntityOptions { */ export type TypeAnnotation = { __meta?: never | [T, Options] }; -export type ExtractAnnotationOptions> = Exclude, never>[1]; +export type ExtractTypeAnnotationOptions> = Exclude, never>[1]; /** * Type to decorate an interface/object literal with entity information. From fa64d90cd3200ce7f74e882285375854d00c0150 Mon Sep 17 00:00:00 2001 From: marcus-sa Date: Fri, 9 Feb 2024 13:29:38 +0100 Subject: [PATCH 3/5] test(type): extract type annotation options --- packages/type/tests/integration4.spec.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/type/tests/integration4.spec.ts b/packages/type/tests/integration4.spec.ts index f8eb70413..5ce769ec0 100644 --- a/packages/type/tests/integration4.spec.ts +++ b/packages/type/tests/integration4.spec.ts @@ -9,7 +9,7 @@ */ import { expect, test } from '@jest/globals'; -import { assertType, AutoIncrement, Group, groupAnnotation, PrimaryKey, ReflectionKind } from '../src/reflection/type.js'; +import { assertType, AutoIncrement, ExtractTypeAnnotationOptions, Group, groupAnnotation, PrimaryKey, ReflectionKind, TypeAnnotation } from '../src/reflection/type.js'; import { typeOf } from '../src/reflection/reflection.js'; import { cast } from '../src/serializer-facade.js'; @@ -143,3 +143,13 @@ test('union loosely', () => { expect(cast({ id: 2 })).toEqual({ id: 2 }); expect(cast({ id: '3' })).toEqual({ id: 3 }); }); + +test('extract type annotation options', () => { + type Skip = TypeAnnotation<'skip', { if: boolean }>; + + type SkipOptions = ExtractTypeAnnotationOptions; + + const options: SkipOptions = { + if: true, + }; +}); From 465f995db5b3441ee67b3a161552b821982c531f Mon Sep 17 00:00:00 2001 From: marcus-sa Date: Fri, 9 Feb 2024 13:37:40 +0100 Subject: [PATCH 4/5] feat(type): allow type annotation options to be extracted attempt 2 --- packages/type/src/reflection/type.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/type/src/reflection/type.ts b/packages/type/src/reflection/type.ts index 1ca72f59e..57e555d67 100644 --- a/packages/type/src/reflection/type.ts +++ b/packages/type/src/reflection/type.ts @@ -1548,9 +1548,9 @@ export interface EntityOptions { * const param1 = typeToObject(data[0]); //yes * ``` */ -export type TypeAnnotation = { __meta?: never | [T, Options] }; +export type TypeAnnotation = { __meta?: [T, never | Options] }; -export type ExtractTypeAnnotationOptions> = Exclude, never>[1]; +export type ExtractTypeAnnotationOptions> = Exclude[1], never>; /** * Type to decorate an interface/object literal with entity information. From 346fa3cb93a454ec388cca88f9509c0e6fabe938 Mon Sep 17 00:00:00 2001 From: marcus-sa Date: Fri, 9 Feb 2024 15:11:31 +0100 Subject: [PATCH 5/5] feat(type): allow type annotation options to be extracted attempt 3 --- packages/type/src/reflection/type.ts | 31 ++++++++------------- packages/type/tests/annotations.spec.ts | 34 ++++++++++++++++++++++++ packages/type/tests/integration4.spec.ts | 10 ------- 3 files changed, 45 insertions(+), 30 deletions(-) create mode 100644 packages/type/tests/annotations.spec.ts diff --git a/packages/type/src/reflection/type.ts b/packages/type/src/reflection/type.ts index 57e555d67..e94c142e5 100644 --- a/packages/type/src/reflection/type.ts +++ b/packages/type/src/reflection/type.ts @@ -1996,34 +1996,25 @@ export function registerTypeDecorator(decorator: TypeDecorator) { * type MyAnnotation1 = TypeAnnotation<'myAnnotation', T> * * //under the hood it is: - * type lowLevel1 = { __meta?: never & ['myAnnotation'] } - * type lowLevel2 = { __meta?: never & ['myAnnotation', T] } + * type lowLevel1 = { __meta?: ['myAnnotation', never] } + * type lowLevel2 = { __meta?: ['myAnnotation', never | T] } * ``` */ export function getAnnotationMeta(type: TypeObjectLiteral): { id: string, params: Type[] } | undefined { const meta = getProperty(type, '__meta'); - if (!meta || !meta.optional) return; - let tuple: TypeTuple | undefined = undefined; - if (meta.type.kind === ReflectionKind.intersection) { - if (meta.type.types.length === 1 && meta.type.types[0].kind === ReflectionKind.tuple) { - tuple = meta.type.types[0] as TypeTuple; - } - if (!tuple && meta.type.types.length === 2) { - tuple = meta.type.types.find(v => v.kind === ReflectionKind.tuple) as TypeTuple | undefined; - if (tuple && !meta.type.types.find(v => v.kind === ReflectionKind.never)) { - tuple = undefined; - } - } - } else if (meta.type.kind === ReflectionKind.tuple) { - tuple = meta.type; - } + if (!meta || !meta.optional) return; + if (meta.type.kind !== ReflectionKind.tuple) return; - if (!tuple) return; + const id = meta.type.types[0]; - const id = tuple.types[0]; if (!id || id.type.kind !== ReflectionKind.literal || 'string' !== typeof id.type.literal) return; - const params = tuple.types.slice(1).map(v => v.type); + + const options = meta.type.types[1]; + if (options.type.kind !== ReflectionKind.union) return; + if (options.type.types[0].kind !== ReflectionKind.never) return; + + const params = options.type.types.slice(1); return { id: id.type.literal, params }; } diff --git a/packages/type/tests/annotations.spec.ts b/packages/type/tests/annotations.spec.ts new file mode 100644 index 000000000..3c013b003 --- /dev/null +++ b/packages/type/tests/annotations.spec.ts @@ -0,0 +1,34 @@ +import { test, expect } from '@jest/globals'; +import { ExtractTypeAnnotationOptions, getAnnotationMeta, TypeAnnotation, TypeObjectLiteral } from '../src/reflection/type.js'; +import { typeOf } from '../src/reflection/reflection.js'; +import { expectEqualType } from './utils.js'; + +test('extract type annotation options', () => { + type Skip = TypeAnnotation<'skip', { if: boolean }>; + + type SkipOptions = ExtractTypeAnnotationOptions; + + const options: SkipOptions = { + if: true, + }; +}); + + +test('getAnnotationMeta', () => { + type LevelOptions = { do: boolean }; + + const levelOptionsType = typeOf(); + + type Level = TypeAnnotation<'level', LevelOptions>; + + const levelType = typeOf() as TypeObjectLiteral; + + const meta = getAnnotationMeta(levelType); + + expect(meta).toBeDefined(); + + expect(meta!.id).toBe('level'); + + expectEqualType(meta!.params[0], levelOptionsType); + +}); diff --git a/packages/type/tests/integration4.spec.ts b/packages/type/tests/integration4.spec.ts index 5ce769ec0..73e051f37 100644 --- a/packages/type/tests/integration4.spec.ts +++ b/packages/type/tests/integration4.spec.ts @@ -143,13 +143,3 @@ test('union loosely', () => { expect(cast({ id: 2 })).toEqual({ id: 2 }); expect(cast({ id: '3' })).toEqual({ id: 3 }); }); - -test('extract type annotation options', () => { - type Skip = TypeAnnotation<'skip', { if: boolean }>; - - type SkipOptions = ExtractTypeAnnotationOptions; - - const options: SkipOptions = { - if: true, - }; -});