Skip to content

Commit

Permalink
refactor: move all type utilities to one file (#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
imranbarbhuiya authored Jul 3, 2022
1 parent 75b1f9a commit 61cab3d
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 112 deletions.
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,3 @@ export * from './lib/errors/UnknownPropertyError';
export * from './lib/errors/ValidationError';
export * from './lib/Result';
export * from './type-exports';
export * from './utility-types';
4 changes: 1 addition & 3 deletions src/lib/Shapes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { TypedArray, TypedArrayName } from '../constraints/util/typedArray';
import type { Unwrap, UnwrapTuple } from '../lib/util-types';
import {
ArrayValidator,
BaseValidator,
Expand Down Expand Up @@ -161,6 +162,3 @@ export class Shapes {
return new MapValidator(keyValidator, valueValidator);
}
}

export type UnwrapTuple<T extends [...any[]]> = T extends [infer Head, ...infer Tail] ? [Unwrap<Head>, ...UnwrapTuple<Tail>] : [];
export type Unwrap<T> = T extends BaseValidator<infer V> ? V : never;
115 changes: 110 additions & 5 deletions src/lib/util-types.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,123 @@
import type { BaseValidator } from '../validators/BaseValidator';
import type { ObjectValidator } from '../validators/ObjectValidator';

export type Constructor<T> = (new (...args: readonly any[]) => T) | (abstract new (...args: readonly any[]) => T);

export type Type<V> = V extends BaseValidator<infer T> ? T : never;

type PickDefined<T> = { [K in keyof T as undefined extends T[K] ? never : K]: T[K] };
// eslint-disable-next-line @typescript-eslint/ban-types
export type NonNullObject = {} & object;

type PickUndefinedMakeOptional<T> = {
export type PickDefined<T> = { [K in keyof T as undefined extends T[K] ? never : K]: T[K] };

export type PickUndefinedMakeOptional<T> = {
[K in keyof T as undefined extends T[K] ? K : never]+?: Exclude<T[K], undefined>;
};

export type UndefinedToOptional<T> = PickDefined<T> & PickUndefinedMakeOptional<T>;

// eslint-disable-next-line @typescript-eslint/ban-types
export type NonNullObject = {} & object;

export type MappedObjectValidator<T> = { [key in keyof T]: BaseValidator<T[key]> };

/**
* An alias of {@link ObjectValidator} with a name more common among object validation libraries.
* This is the type of a schema after using `s.object({ ... })`
* @example
* ```typescript
* import { s, SchemaOf } from '@sapphire/shapeshift';
*
* interface IIngredient {
* ingredientId: string | undefined;
* name: string | undefined;
* }
*
* interface IInstruction {
* instructionId: string | undefined;
* message: string | undefined;
* }
*
* interface IRecipe {
* recipeId: string | undefined;
* title: string;
* description: string;
* instructions: IInstruction[];
* ingredients: IIngredient[];
* }
*
* type InstructionSchemaType = SchemaOf<IInstruction>;
* // Expected Type: ObjectValidator<IInstruction>
*
* type IngredientSchemaType = SchemaOf<IIngredient>;
* // Expected Type: ObjectValidator<IIngredient>
*
* type RecipeSchemaType = SchemaOf<IRecipe>;
* // Expected Type: ObjectValidator<IRecipe>
*
* const instructionSchema: InstructionSchemaType = s.object({
* instructionId: s.string.optional,
* message: s.string
* });
*
* const ingredientSchema: IngredientSchemaType = s.object({
* ingredientId: s.string.optional,
* name: s.string
* });
*
* const recipeSchema: RecipeSchemaType = s.object({
* recipeId: s.string.optional,
* title: s.string,
* description: s.string,
* instructions: s.array(instructionSchema),
* ingredients: s.array(ingredientSchema)
* });
* ```
*/
export type SchemaOf<T> = ObjectValidator<T>;

/**
* Infers the type of a schema object given `typeof schema`.
* The schema has to extend {@link ObjectValidator}.
* @example
* ```typescript
* import { InferType, s } from '@sapphire/shapeshift';
*
* const schema = s.object({
* foo: s.string,
* bar: s.number,
* baz: s.boolean,
* qux: s.bigint,
* quux: s.date
* });
*
* type Inferredtype = InferType<typeof schema>;
* // Expected type:
* // type Inferredtype = {
* // foo: string;
* // bar: number;
* // baz: boolean;
* // qux: bigint;
* // quux: Date;
* // };
* ```
*/
export type InferType<T extends ObjectValidator<any>> = T extends ObjectValidator<any, infer U> ? U : never;

//
export type UnwrapTuple<T extends [...any[]]> = T extends [infer Head, ...infer Tail] ? [Unwrap<Head>, ...UnwrapTuple<Tail>] : [];
export type Unwrap<T> = T extends BaseValidator<infer V> ? V : never;

//
export type UnshiftTuple<T extends [...any[]]> = T extends [T[0], ...infer Tail] ? Tail : never;
export type ExpandSmallerTuples<T extends [...any[]]> = T extends [T[0], ...infer Tail] ? T | ExpandSmallerTuples<Tail> : [];

// https://github.com/microsoft/TypeScript/issues/26223#issuecomment-755067958
export type Shift<A extends Array<any>> = ((...args: A) => void) extends (...args: [A[0], ...infer R]) => void ? R : never;

export type GrowExpRev<A extends Array<any>, N extends number, P extends Array<Array<any>>> = A['length'] extends N
? A
: GrowExpRev<[...A, ...P[0]][N] extends undefined ? [...A, ...P[0]] : A, N, Shift<P>>;

export type GrowExp<A extends Array<any>, N extends number, P extends Array<Array<any>>> = [...A, ...A][N] extends undefined
? GrowExp<[...A, ...A], N, [A, ...P]>
: GrowExpRev<A, N, P>;

export type Tuple<T, N extends number> = number extends N ? Array<T> : N extends 0 ? [] : N extends 1 ? [T] : GrowExp<[T], N, [[]]>;
6 changes: 3 additions & 3 deletions src/type-exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ export type { UnknownEnumValueError } from './lib/errors/UnknownEnumValueError';
export type { UnknownPropertyError } from './lib/errors/UnknownPropertyError';
export type { ValidationError } from './lib/errors/ValidationError';
//
export type { Shapes, Unwrap, UnwrapTuple } from './lib/Shapes';
export type { Shapes } from './lib/Shapes';
//
export type { Constructor, MappedObjectValidator, NonNullObject, Type } from './lib/util-types';
export * from './lib/util-types';
//
export type { ArrayValidator, ExpandSmallerTuples, GrowExp, GrowExpRev, Shift, Tuple, UnshiftTuple } from './validators/ArrayValidator';
export type { ArrayValidator } from './validators/ArrayValidator';
export type { BaseValidator, ValidatorError } from './validators/BaseValidator';
export type { BigIntValidator } from './validators/BigIntValidator';
export type { BooleanValidator } from './validators/BooleanValidator';
Expand Down
84 changes: 0 additions & 84 deletions src/utility-types.ts

This file was deleted.

17 changes: 1 addition & 16 deletions src/validators/ArrayValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type { BaseError } from '../lib/errors/BaseError';
import { CombinedPropertyError } from '../lib/errors/CombinedPropertyError';
import { ValidationError } from '../lib/errors/ValidationError';
import { Result } from '../lib/Result';
import type { ExpandSmallerTuples, Tuple, UnshiftTuple } from '../lib/util-types';
import { BaseValidator } from './imports';

export class ArrayValidator<T extends unknown[], I = T[number]> extends BaseValidator<T> {
Expand Down Expand Up @@ -96,19 +97,3 @@ export class ArrayValidator<T extends unknown[], I = T[number]> extends BaseVali
: Result.err(new CombinedPropertyError(errors));
}
}

export type UnshiftTuple<T extends [...any[]]> = T extends [T[0], ...infer Tail] ? Tail : never;
export type ExpandSmallerTuples<T extends [...any[]]> = T extends [T[0], ...infer Tail] ? T | ExpandSmallerTuples<Tail> : [];

// https://github.com/microsoft/TypeScript/issues/26223#issuecomment-755067958
export type Shift<A extends Array<any>> = ((...args: A) => void) extends (...args: [A[0], ...infer R]) => void ? R : never;

export type GrowExpRev<A extends Array<any>, N extends number, P extends Array<Array<any>>> = A['length'] extends N
? A
: GrowExpRev<[...A, ...P[0]][N] extends undefined ? [...A, ...P[0]] : A, N, Shift<P>>;

export type GrowExp<A extends Array<any>, N extends number, P extends Array<Array<any>>> = [...A, ...A][N] extends undefined
? GrowExp<[...A, ...A], N, [A, ...P]>
: GrowExpRev<A, N, P>;

export type Tuple<T, N extends number> = number extends N ? Array<T> : N extends 0 ? [] : N extends 1 ? [T] : GrowExp<[T], N, [[]]>;

0 comments on commit 61cab3d

Please sign in to comment.