Skip to content

Commit

Permalink
feat(type): allow type annotation options to be extracted attempt 3
Browse files Browse the repository at this point in the history
  • Loading branch information
marcus-sa committed Feb 9, 2024
1 parent 465f995 commit 346fa3c
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 30 deletions.
31 changes: 11 additions & 20 deletions packages/type/src/reflection/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1996,34 +1996,25 @@ export function registerTypeDecorator(decorator: TypeDecorator) {
* type MyAnnotation1<T> = TypeAnnotation<'myAnnotation', T>
*
* //under the hood it is:
* type lowLevel1 = { __meta?: never & ['myAnnotation'] }
* type lowLevel2<T> = { __meta?: never & ['myAnnotation', T] }
* type lowLevel1 = { __meta?: ['myAnnotation', never] }
* type lowLevel2<T> = { __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 };
}
Expand Down
34 changes: 34 additions & 0 deletions packages/type/tests/annotations.spec.ts
Original file line number Diff line number Diff line change
@@ -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<Skip>;

const options: SkipOptions = {
if: true,
};
});


test('getAnnotationMeta', () => {
type LevelOptions = { do: boolean };

const levelOptionsType = typeOf<LevelOptions>();

type Level = TypeAnnotation<'level', LevelOptions>;

const levelType = typeOf<Level>() as TypeObjectLiteral;

const meta = getAnnotationMeta(levelType);

expect(meta).toBeDefined();

expect(meta!.id).toBe('level');

expectEqualType(meta!.params[0], levelOptionsType);

});
10 changes: 0 additions & 10 deletions packages/type/tests/integration4.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,3 @@ test('union loosely', () => {
expect(cast<a>({ id: 2 })).toEqual({ id: 2 });
expect(cast<a>({ id: '3' })).toEqual({ id: 3 });
});

test('extract type annotation options', () => {
type Skip = TypeAnnotation<'skip', { if: boolean }>;

type SkipOptions = ExtractTypeAnnotationOptions<Skip>;

const options: SkipOptions = {
if: true,
};
});

0 comments on commit 346fa3c

Please sign in to comment.