Skip to content

Commit

Permalink
tuple spreading (#17)
Browse files Browse the repository at this point in the history
* tuple spreading

* more dx tests

* some changes

* latest

* cleanup

* decided to drop implementation of rest tuples

* update build outputs
  • Loading branch information
nicu-chiciuc authored Mar 2, 2024
1 parent 63449f4 commit 0a4fb87
Show file tree
Hide file tree
Showing 12 changed files with 541 additions and 97 deletions.
5 changes: 4 additions & 1 deletion packages/sure/esm/array.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
import { Sure, InferBad, InferGood, MetaNever, MetaObj } from './core.js';
export declare function array<TPropFail, TPropGood, TSchema extends Sure<TPropFail, TPropGood, unknown, MetaObj | MetaNever>>(schema: TSchema): Sure<Array<InferBad<TSchema> | undefined>, Array<InferGood<TSchema>>, unknown, MetaObj<TSchema>>;
export declare function array<TPropFail, TPropGood, TSchema extends Sure<TPropFail, TPropGood, unknown, MetaObj | MetaNever>>(schema: TSchema): Sure<Array<InferBad<TSchema> | undefined>, Array<InferGood<TSchema>>, unknown, MetaObj<{
parent: typeof array;
schema: TSchema;
}>>;
5 changes: 4 additions & 1 deletion packages/sure/esm/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,10 @@ export function array(schema) {
return bad(bads);
}
return good(goods);
}, schema);
}, {
parent: array,
schema,
});
// @ts-expect-error Expected error
return struct;
}
4 changes: 4 additions & 0 deletions packages/sure/esm/object.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ type PickNonOptionals<T extends KVPair<Sure>> = T extends {
v: InferGood<T['v']>;
};
export type InferSchemaGood<T extends Record<string, Sure>> = Prettify<Partial<Objectify<PickOptionalsGood<Unionize<T>>>> & Objectify<PickNonOptionals<Unionize<T>>>>;
/**
Necessary because `typeof x` is not a type guard.
*/
export declare function isObject(x: unknown): x is Record<string, unknown>;
/**
* Makes a object property `optional`
* It doesn't make it nullable or undefinedable
Expand Down
2 changes: 1 addition & 1 deletion packages/sure/esm/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { sure, good, bad } from './core.js';
/**
Necessary because `typeof x` is not a type guard.
*/
function isObject(x) {
export function isObject(x) {
return typeof x === 'object' && x !== null;
}
/**
Expand Down
40 changes: 33 additions & 7 deletions packages/sure/esm/tuple.d.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
import { MetaNever, MetaObj, Sure } from './core.js';
export type TupleInferGoods<T> = T extends readonly [infer First, ...infer InferRest] ? First extends Sure<unknown, infer Good, any, MetaObj | MetaNever> ? [Good, ...TupleInferGoods<InferRest>] : [] : [];
export type TupleInferBads<T> = T extends readonly [infer First, ...infer InferRest] ? First extends Sure<infer Bad, unknown, any, MetaObj | MetaNever> ? [Bad | undefined, ...TupleInferBads<InferRest>] : [] : [];
export declare function tupleRest<Arr extends Sure<unknown, unknown[], unknown>>(struct: Arr): Sure<unknown, unknown[], unknown, MetaObj<{
func: typeof tupleRest;
initial: unknown;
import { InferBad, InferGood, MetaNever, MetaObj, Sure } from './core.js';
export type TupleInferGoods_old<T> = T extends readonly [infer First, ...infer InferRest] ? First extends Sure<unknown, infer Good, any, MetaObj | MetaNever> ? [Good, ...TupleInferGoods<InferRest>] : [] : [];
export type TupleInferGoods<T> = T extends readonly [infer First, ...infer InferRest] ? First extends Sure<unknown, infer Good, any, infer Meta> ? Meta extends MetaObj<{
parent: typeof spread_NOT_IMPLEMENTED;
}> ? Good extends readonly unknown[] ? [...Good, ...TupleInferGoods<InferRest>] : [
Good,
...TupleInferGoods<InferRest>
] : [Good, ...TupleInferGoods<InferRest>] : [] : [];
export type TupleInferBads_old<T> = T extends readonly [infer First, ...infer InferRest] ? First extends Sure<infer Bad, unknown, any, MetaObj | MetaNever> ? [Bad | undefined, ...TupleInferBads<InferRest>] : [] : [];
export type TupleInferBads<T> = T extends readonly [infer First, ...infer InferRest] ? First extends Sure<infer Bad, unknown, any, infer Meta> ? Meta extends MetaObj<{
parent: typeof spread_NOT_IMPLEMENTED;
}> ? Bad extends readonly unknown[] ? [...Bad, ...TupleInferBads<InferRest>] : [
Bad | undefined,
...TupleInferBads<InferRest>
] : [Bad | undefined, ...TupleInferBads<InferRest>] : [] : [];
/**
* @deprecated
*
* It's possible to add a spread operator.
* But it's seems that it would add a lot of complexity and will have to link the library too much.
*
* A tuple would have to know about both the spread and an array.
*
* It made sense for `object` and `optional`, since they're linked.
* But for tuples it can be more clearly implemented as a separate user-land function.
*/
export declare function spread_NOT_IMPLEMENTED<Arr extends Sure<unknown, unknown[], unknown>>(schema: Arr): Sure<InferBad<Arr>, InferGood<Arr>, unknown, MetaObj<{
parent: typeof spread_NOT_IMPLEMENTED;
schema: typeof schema;
}>>;
export declare function tuple<Arr extends [Sure<unknown, unknown, any>, ...Sure<unknown, unknown, any>[]] | []>(arr: Arr): Sure<TupleInferBads<Arr>, TupleInferGoods<Arr>, unknown, MetaObj<{
parent: typeof tuple;
schema: Arr;
}>>;
export declare function tuple<Arr extends [Sure<unknown, unknown, any>, ...Sure<unknown, unknown, any>[]] | []>(arr: Arr): Sure<TupleInferBads<Arr>, TupleInferGoods<Arr>, unknown, MetaObj<Arr>>;
27 changes: 22 additions & 5 deletions packages/sure/esm/tuple.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
import { bad, good, sure } from './core.js';
export function tupleRest(struct) {
const val = sure(struct, {
func: tupleRest,
initial: struct.meta,
/**
* @deprecated
*
* It's possible to add a spread operator.
* But it's seems that it would add a lot of complexity and will have to link the library too much.
*
* A tuple would have to know about both the spread and an array.
*
* It made sense for `object` and `optional`, since they're linked.
* But for tuples it can be more clearly implemented as a separate user-land function.
*/
export function spread_NOT_IMPLEMENTED(schema) {
// IMPORTANT: It's important to pass a new function here
const val = sure(value => schema(value), {
parent: spread_NOT_IMPLEMENTED,
schema,
});
// @ts-expect-error - this is fine
return val;
}
export function tuple(arr) {
Expand Down Expand Up @@ -34,7 +47,11 @@ export function tuple(arr) {
return bad(bads);
}
return good(goods);
}, arr);
}, {
parent: tuple,
initial: arr,
});
struct.meta;
// @ts-expect-error
return struct;
}
6 changes: 5 additions & 1 deletion packages/sure/src/__tests__/array.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ assertEqual<InferredInput, unknown>(true)
assertEqual<
InferredMeta,
{
meta: Sure<'not number', number, unknown, MetaNever>
meta: {
parent: typeof array

schema: Sure<'not number', number, unknown, MetaNever>
}
}
>(true)

Expand Down
52 changes: 52 additions & 0 deletions packages/sure/src/__tests__/learning/tuple_rest_stuff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { MetaObj, Sure, array, number, spread_NOT_IMPLEMENTED, string, tuple } from '../../index.js'

// Doesn't work
export type TupleInferGoods01<T> = //
T extends readonly [infer First, ...infer InferRest]
? First extends Sure<unknown, infer Good, any, infer Meta>
? Meta extends { parent: typeof spread_NOT_IMPLEMENTED }
? // try spreading the rest if it's an array
Good extends readonly unknown[]
? [...Good, ...TupleInferGoods01<InferRest>]
: // otherwise just return the good
[Good, ...TupleInferGoods01<InferRest>]
: [Good, ...TupleInferGoods01<InferRest>]
: []
: []

const sample = [
//
string,
spread_NOT_IMPLEMENTED(array(number)),
string,
] as const

type Sample = typeof sample

// [unknown[]]
type Check01 = TupleInferGoods01<Sample>

// ## Try fixing
export type TupleInferGoods_02<T> = //
T extends readonly [infer First, ...infer InferRest]
? First extends Sure<unknown, infer Good, any, infer Meta>
? Meta extends MetaObj<{ parent: typeof spread_NOT_IMPLEMENTED }>
? // try spreading the rest if it's an array
Good extends readonly unknown[]
? [...Good, ...TupleInferGoods_02<InferRest>]
: // otherwise just return the good
[Good, ...TupleInferGoods_02<InferRest>]
: [Good, ...TupleInferGoods_02<InferRest>]
: []
: []

// works
type Check02 = TupleInferGoods_02<Sample>

// Check for full tuple
const schema = [
//
string,
spread_NOT_IMPLEMENTED(array(number)),
string,
] as const
Loading

0 comments on commit 0a4fb87

Please sign in to comment.