Skip to content

Commit

Permalink
feat: add type checking to lens (nested) — WIP #1
Browse files Browse the repository at this point in the history
  • Loading branch information
geoffreytools committed Aug 6, 2023
1 parent 63facc0 commit 0bb9ac6
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 7 deletions.
31 changes: 24 additions & 7 deletions src/Audit.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
import { TypesMap, Generic, unwrap } from 'free-types-core';
import { Fn, Param, Output, Query, ILens, PathItem } from './types'
import { TypesMap, Generic, unwrap, Type, B } from 'free-types-core';
import { Fn, Param, Output, Query, ILens, PathItem, QueryItem } from './types'
import { Prev, Next } from './utils';
import { Lens } from './Lens'
import { FollowPath, NOT_FOUND } from './Follow';
import { MapOver } from 'free-types/essential/mappables/MapOver';

export type Audit<
Q extends Query,
Model,
I extends number = 0,
L extends ILens = Lens<Q>,
F = FollowPath<L['path'][I], Model, Model>
> = F extends NOT_FOUND ? ProperPath<Model, L, I>
> = F extends NOT_FOUND ? HandleError<Model, Q, L, I>
: Next<I> extends L['path']['length'] ? Query
: Audit<L, F, Next<I>>;
: Audit<Q, F, Next<I>, L>;

type ProperPath<Model, Q extends ILens, I extends number> =
[...LastPathItem<Q['path'], I>, NextPathItem<Model>]
type HandleError<
Model,
Q extends Query,
L extends ILens,
I extends number,
R extends QueryItem[] = ProperPath<Model, L, I>
> = Q extends ILens ? Lens<R>
: Q extends [ILens] ? [Lens<R>]
: Q extends QueryItem[] ? MapOver<R, $WrapIfLens<Q>>
: R

interface $WrapIfLens<Q extends QueryItem[]> extends Type<2> {
type: Q[B<this>] extends ILens ? Lens<this[0]> : this[0]
constraints: [QueryItem, number]
}

type ProperPath<Model, L extends ILens, I extends number> =
[...LastPathItem<L['path'], I>, NextPathItem<Model>]

type LastPathItem<P extends PathItem[], I extends number> =
I extends 0 ? [] : [P[Prev<I>]];
Expand All @@ -24,7 +41,7 @@ type NextPathItem<Model> =
readonly any[] extends Model ? number
: Model extends readonly unknown[] ? NumericArgs<Model>
: Model extends Fn ? Output | Param<SequenceTo<Prev<Parameters<Model>['length']>>>
: Model extends GenericFree ? TypesMap[unwrap<Model>['URI']]
: Model extends GenericFree ? unwrap<Model>['type']
: Model extends Record<PropertyKey, unknown> ? { [K in keyof Model]: K }[keyof Model]
: never

Expand Down
33 changes: 33 additions & 0 deletions tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,39 @@ test('Flat Lens type checking', t => [
>(),
])

test('Nested Lens type checking', t => [
OK(t)<Audit<Lens<['a', 0]>, { a: [1, 2, 3] }>>(),
OK(t)<Audit<[Lens<['a', 0]>], { a: [1, 2, 3] }>>(),
OK(t)<Audit<[Lens<'a'>, Lens<0>], { a: [1, 2, 3] }>>(),

t.equal<
Audit<Lens<['a', 'b']>, { a: [1, 2, 3] }>,
Lens<["a", 0 | 1 | 2]>
>(),
t.equal<
Audit<[Lens<['a', 'b']>], { a: [1, 2, 3] }>,
[Lens<["a", 0 | 1 | 2]>]
>(),
t.equal<
Audit<[Lens<'a'>, Lens<'b'>], { a: [1, 2, 3] }>,
[Lens<'a'>, Lens<0 | 1 | 2>]
>(),
t.equal<
Audit<['a', Lens<'b'>], { a: [1, 2, 3] }>,
['a', Lens<0 | 1 | 2>]
>(),
t.equal<
Audit<[Lens<'a'>, 'b'], { a: [1, 2, 3] }>,
[Lens<'a'>, 0 | 1 | 2]
>(),

// They are expected to flatten
t.equal<
Audit<Lens<[Lens<'a'>, Lens<'b'>]>, { a: [1, 2, 3] }>,
Lens<['a' , 0 | 1 | 2]>
>(),
]);

test('bare Get: tuple, object', t => [
found(t)<Get<0, [needle, 2, 3]>>(),
found(t)<Get<'a', { a: needle, b: 2 }>>(),
Expand Down

0 comments on commit 0bb9ac6

Please sign in to comment.