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, - }; -});