Skip to content

Commit

Permalink
refactor: Add If util configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
eythaann committed Oct 2, 2023
1 parent 521f23f commit 174d70b
Show file tree
Hide file tree
Showing 14 changed files with 137 additions and 57 deletions.
10 changes: 8 additions & 2 deletions lib/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import { AnyObject } from './modules/infrastructure';
import { ForceExtract } from './modules/app';
import { AnyObject, DefaultOnUnknown } from './modules/infrastructure';
import { IValidations, TestsCallback } from './readable-test-types';

declare global {
function describeType(description: string, cb: () => void): void;
function testType(description: string, tests: AnyObject | TestsCallback): void;
function assertType<T>(): IValidations<T>;

interface INTERNAL_RT_CONFIG {
development: DefaultOnUnknown<ForceExtract<RT_CONFIG, 'development'>, false>;
conditionWay: DefaultOnUnknown<ForceExtract<RT_CONFIG, 'conditionWay'>, 'natural'>;
}

interface RT_CONFIG {}

type RT_CONFIG_SCHEME<T extends {
development?: boolean;
ifConditionWay?: 'singleLine' | 'natural' | 'explicit'; // future 2.0
conditionWay?: 'singleLine' | 'natural' | 'explicit'; // future 2.0
}> = T;
}
5 changes: 4 additions & 1 deletion lib/modules/arrays-and-tuples/infrastructure.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ export type IsEmptyArray<T extends unknown[]> = T extends [] ? true : false;
* type C = IsTuple<never>;
* // ^? false
*/
export type IsTuple<T> = If<Or<[IsNever<T>, IsAny<T>]>, false, T extends [infer _A, ...(infer _B)] ? true : false> ;
export type IsTuple<T> = If<Or<[IsNever<T>, IsAny<T>]>, {
then: false;
else: T extends [infer _A, ...(infer _B)] ? true : false;
}>;

/**
* Checks if a tuple `T` includes a specific type `TypeToSearch`.
Expand Down
10 changes: 8 additions & 2 deletions lib/modules/booleans/infrastructure.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ export type Or<T extends nLengthTuple<boolean>> = Not<IsNever<Extract<T[number],
* // ^? false
*/
export type XOR<T extends boolean[], trueFinded extends boolean = false> = T extends [infer X, ...infer newT]
? If<And<[Cast<X, boolean>, trueFinded]>, false, XOR<Cast<newT, boolean[]>, Or<[Cast<X, boolean>, trueFinded]>>>
? If<And<[Cast<X, boolean>, trueFinded]>, {
then: false;
else: XOR<Cast<newT, boolean[]>, Or<[Cast<X, boolean>, trueFinded]>>;
}>
: trueFinded;

/**
Expand Down Expand Up @@ -90,7 +93,10 @@ export type Not<T extends boolean> = T extends true ? false : true;
* type D = NotIf<false, false>;
* // ^? false
*/
export type NotIf<Condition extends boolean, T extends boolean> = If<Condition, Not<T>, T>;
export type NotIf<Condition extends boolean, T extends boolean> = If<Condition, {
then: Not<T>;
else: T;
}>;

/*
-
Expand Down
36 changes: 27 additions & 9 deletions lib/modules/conditions/app.d.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
import { ForceExtract } from '../app';

export type ConditionObject = {
type Explicit = {
condition: boolean;
type: unknown;
then: unknown;
else: unknown;
};

export type ConditionCaseMap = {
'true': 'type';
'false': 'else';
'boolean': 'type' | 'else';
type Natural = {
then: unknown;
else: unknown;
};

// TODO improve inference of types for generics
export type IfObject<Condition extends ConditionObject> = Condition[ConditionCaseMap[`${Condition['condition']}`]];
export type ExplicitCondition<Condition> = [ForceExtract<Condition, 'condition'>] extends [false] ? ForceExtract<Condition, 'else'> : ForceExtract<Condition, 'then'>;

export type NaturalCondition<Condition, Obj> = [Condition] extends [false] ? ForceExtract<Obj, 'else'> : ForceExtract<Obj, 'then'>;

export type SingleLineCondition<Condition, T, F = never> = [Condition] extends [false] ? F : T;

export type IfSingleLine<Condition, T, F = never> = [Condition] extends [true] ? T : F;
export type ExtendsCaseMapA = {
'singleLine': boolean;
'natural': boolean;
'explicit': Explicit;
};

export type ExtendsCaseMapB = {
'singleLine': unknown;
'natural': Natural;
'explicit': never;
};

export type ExtendsCaseMapC = {
'singleLine': unknown;
'natural': never;
'explicit': never;
};
19 changes: 12 additions & 7 deletions lib/modules/conditions/infrastructure.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { ConditionCaseMap, ConditionObject } from './app';
import { ForceExtract } from '../app';
import { ExplicitCondition, ExtendsCaseMapA, ExtendsCaseMapB, ExtendsCaseMapC, NaturalCondition, SingleLineCondition } from './app';

type IfMode = ForceExtract<INTERNAL_RT_CONFIG, 'conditionWay'>;

/*
! WARNING: This utility has an internal implementation.
Expand All @@ -17,9 +20,11 @@ import { ConditionCaseMap, ConditionObject } from './app';
* // ^ Type C = string | number
*/
export type If<
Condition extends boolean | ConditionObject,
TrueCase = never,
FalseCase = never
> = Condition extends ConditionObject
? Condition[ConditionCaseMap[`${Condition['condition']}`]]
: Condition extends true ? TrueCase : FalseCase;
A extends ExtendsCaseMapA[IfMode],
B extends ExtendsCaseMapB[IfMode] = never,
C extends ExtendsCaseMapC[IfMode] = never
> = {
'singleLine': SingleLineCondition<A, B, C>;
'natural': NaturalCondition<A, B>;
'explicit': ExplicitCondition<A>;
}[IfMode];
36 changes: 21 additions & 15 deletions lib/modules/conditions/infrastructure.spec-types.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
import { If } from './infrastructure';
import { ExplicitCondition, NaturalCondition, SingleLineCondition } from './app';

describeType('If', () => {
testType('Should return TrueCase if Condition is true', [
assertType<If<true, string, number>>().equals<string>(),
assertType<If<true, '1234', 1234>>().equals<'1234'>(),
assertType<If<true, any, unknown>>().toBeAny(),
assertType<SingleLineCondition<true, string, number>>().equals<string>(),
assertType<SingleLineCondition<true, '1234', 1234>>().equals<'1234'>(),
assertType<SingleLineCondition<true, any, unknown>>().toBeAny(),
]);

testType('Should return FalseCase if Condition is false', [
assertType<If<false, string, number>>().equals<number>(),
assertType<If<false, '1234', 1234>>().equals<1234>(),
assertType<If<false, any, unknown>>().toBeUnknow(),
assertType<SingleLineCondition<false, string, number>>().equals<number>(),
assertType<SingleLineCondition<false, '1234', 1234>>().equals<1234>(),
assertType<SingleLineCondition<false, any, unknown>>().toBeUnknow(),
]);

testType('Should return TrueCase | FalseCase if Condition is boolean', [
assertType<If<boolean, string, number>>().equals<string | number>(),
assertType<If<boolean, '1234', 1234>>().equals<'1234' | 1234>(),
assertType<If<boolean, any, unknown>>().toBeAny(), // in this case any | unknown resolve it as any
testType('Should return TrueCase if Condition is boolean', [
assertType<SingleLineCondition<boolean, string, number>>().equals<string>(),
assertType<SingleLineCondition<boolean, '1234', 1234>>().equals<'1234'>(),
assertType<SingleLineCondition<boolean, any, unknown>>().toBeAny(),
]);

testType('Should use condition object (variant 1)', [
assertType<If<{ condition: true; type: string; else: number }>>().equals<string>(),
assertType<If<{ condition: false; type: string; else: number }>>().equals<number>(),
assertType<If<{ condition: boolean; type: string; else: number }>>().equals<string | number>(),
testType('Should use condition object', [
assertType<ExplicitCondition<{ condition: true; then: string; else: number }>>().equals<string>(),
assertType<ExplicitCondition<{ condition: false; then: string; else: number }>>().equals<number>(),
assertType<ExplicitCondition<{ condition: boolean; then: string; else: number }>>().equals<string>(),
]);

testType('Should use Natural condition object', [
assertType<NaturalCondition<true, { then: string; else: number }>>().equals<string>(),
assertType<NaturalCondition<false, { then: string; else: number }>>().equals<number>(),
assertType<NaturalCondition<boolean, { then: string; else: number }>>().equals<string>(),
]);
});
5 changes: 4 additions & 1 deletion lib/modules/numbers/math/app/addition.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ type GetSumCarry<A, B> = B extends ForceExtract<CarryOnAddition, A> ? 1 : 0;

type sumDecimal<A, B, CarryIn> = {
result: GetSum<GetSum<A, B>, CarryIn>;
carryOut: If<And<[Equals<CarryIn, 1>, Equals<A, 9>]>, 1, GetSumCarry<GetSum<A, CarryIn>, B>>;
carryOut: If<And<[Equals<CarryIn, 1>, Equals<A, 9>]>, {
then: 1;
else: GetSumCarry<GetSum<A, CarryIn>, B>;
}>;
};

type _next<
Expand Down
5 changes: 4 additions & 1 deletion lib/modules/numbers/math/app/sustraction.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ type getSustractCarry<A, B> = B extends ForceExtract<CarryOnAddition, ForceExtra

type sustractDecimal<A, B, CarryIn, Result = GetSustract<GetSustract<A, CarryIn>, B>> = {
result: Result;
carryOut: If<And<[Equals<CarryIn, 1>, Equals<B, 9>]>, 1, getSustractCarry<A, Result>>;
carryOut: If<And<[Equals<CarryIn, 1>, Equals<B, 9>]>, {
then: 1;
else: getSustractCarry<A, Result>;
}>;
};

type _SubstractOnShifted<A, B, CarryIn = 0> = sustractDecimal<ToDecimal<ForceExtract<A, 'extracted'>>, ToDecimal<ForceExtract<B, 'extracted'>>, CarryIn>;
Expand Down
35 changes: 20 additions & 15 deletions lib/modules/objects/infrastructure.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { IsUnknown } from '../unknow/infrastructure';
import { AnyFunction } from '../functions/infrastructure';
import { KeysOfUnion } from '../generals/infrastructure';
import { NonUndefined } from '../undefined/infrastructure';
import { IfSingleLine } from '../conditions/app';
import { SingleLineCondition } from '../conditions/app';

export * from './app/ModifyPlusOrderedCombinations';
export * from './app/ModifyPlusCombinations';
Expand Down Expand Up @@ -47,9 +47,8 @@ export type Modify<T, U> = IsStrictObject<T> extends true
* Allow modify interfaces or object types without the restrictions of use `extends` or `&` operator
* Creates a Union Discrimated Type with the overrides + the keys pased for modify the object.
*/
export type ModifyByKey<T, U, KeyToDiscrimitate extends KeyOfObject = '__key'> = If<{
condition: And<[IsStrictObject<T>, IsStrictObject<U>]>;
type: Prettify<{ [_ in KeyToDiscrimitate]?: undefined } & T | { [Key in keyof U]: { [_ in KeyToDiscrimitate]: Key } & Modify<T, U[Key]> }[keyof U]>;
export type ModifyByKey<T, U, KeyToDiscrimitate extends KeyOfObject = '__key'> = If<And<[IsStrictObject<T>, IsStrictObject<U>]>, {
then: Prettify<{ [_ in KeyToDiscrimitate]?: undefined } & T | { [Key in keyof U]: { [_ in KeyToDiscrimitate]: Key } & Modify<T, U[Key]> }[keyof U]>;
else: T;
}>;

Expand Down Expand Up @@ -82,7 +81,7 @@ export type PickByValue<
? Prettify<_result>
: ValuesToPick extends [infer X, ...infer Rest]
? PickByValue<Type, Rest, _result & {
[Key in keyof Type as IfSingleLine<Equals<Type[Key], X>, Key>]: Type[Key]
[Key in keyof Type as SingleLineCondition<Equals<Type[Key], X>, Key>]: Type[Key]
}>
: never;

Expand All @@ -103,10 +102,10 @@ export type CanBeEmptyObject<Type> = {} extends Type ? true : false;
* // ^? "b"
*/
export type ReadonlyKeys<Type> = {
[Key in keyof Type]-?: If<Equals<
{ readonly [_ in Key]: Type[Key] },
{ [_ in Key]: Type[Key] }
>, Key>
[Key in keyof Type]-?: If<Equals<{ readonly [_ in Key]: Type[Key] }, { [_ in Key]: Type[Key] }>, {
then: Key;
else: never;
}>
}[keyof Type];

/**
Expand All @@ -116,10 +115,10 @@ export type ReadonlyKeys<Type> = {
* // ^? "a"
*/
export type NoReadonlyKeys<Type> = {
[Key in keyof Type]-?: If<Equals<
{ -readonly [_ in Key]: Type[Key] },
{ [_ in Key]: Type[Key] }
>, Key>
[Key in keyof Type]-?: If<Equals<{ -readonly [_ in Key]: Type[Key] }, { [_ in Key]: Type[Key] }>, {
then: Key;
else: never;
}>
}[keyof Type];

/**
Expand All @@ -129,7 +128,10 @@ export type NoReadonlyKeys<Type> = {
* // ^? "b" | "c"
*/
export type RequiredKeys<Type> = {
[Key in keyof Type]-?: If<Not<CanBeEmptyObject<{ [_ in Key]: Type[Key] }>>, Key>
[Key in keyof Type]-?: If<Not<CanBeEmptyObject<{ [_ in Key]: Type[Key] }>>, {
then: Key;
else: never;
}>
}[keyof Type];

/**
Expand All @@ -139,7 +141,10 @@ export type RequiredKeys<Type> = {
* // ^? "a" | "b"
*/
export type OptionalKeys<Type> = {
[Key in keyof Type]-?: If<CanBeEmptyObject<{ [_ in Key]: Type[Key] }>, Key>
[Key in keyof Type]-?: If<CanBeEmptyObject<{ [_ in Key]: Type[Key] }>, {
then: Key;
else: never;
}>
}[keyof Type];

/**
Expand Down
5 changes: 4 additions & 1 deletion lib/modules/promises/infrastructure.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ import { IsNever } from '../never/infrastructure';
* type C = IsPromise<never>;
* // ^? false
*/
export type IsPromise<T> = If<IsNever<T>, false, T extends Promise<any> ? true : false>;
export type IsPromise<T> = If<IsNever<T>, {
then: false;
else: T extends Promise<any> ? true : false;
}>;
8 changes: 8 additions & 0 deletions lib/modules/unknow/infrastructure.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,11 @@ import { IsAny } from '../any/infrastructure';
If you are modifying this method, ensure to also update its corresponding internal implementation.
*/
export type IsUnknown<Type> = IsAny<Type> extends true ? false : unknown extends Type ? true : false;

/**
* A utility type that substitutes a default type when the provided type is unknown.
* @example
* type A = DefaultOnUnknown<unknown, string>; // Result: string
* type B = DefaultOnUnknown<number, string>; // Result: number
*/
export type DefaultOnUnknown<Type, Default> = IsUnknown<Type> extends true ? Default : Type;
14 changes: 13 additions & 1 deletion lib/modules/unknow/infrastructure.spec-types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IsUnknown } from './infrastructure';
import { DefaultOnUnknown, IsUnknown } from './infrastructure';

describeType('IsUnknown', () => {
testType('Should return true only for type unknown', [
Expand All @@ -15,4 +15,16 @@ describeType('IsUnknown', () => {
assertType<IsUnknown<never>>().equals<false>(),
assertType<IsUnknown<unknown & string>>().equals<false>(),
]);

describeType('DefaultOnUnknown', () => {
testType('Should replace unknown type with the default type', () => {
type result = DefaultOnUnknown<unknown, string>;
assertType<result>().equals<string>();
});

testType('Should retain the original type if it is known', () => {
type result = DefaultOnUnknown<number, string>;
assertType<result>().equals<number>();
});
});
});
5 changes: 4 additions & 1 deletion lib/readable-test-types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,10 @@ type propertyCallableOnPass<
Result extends boolean,
Invert extends boolean,
keyToErrorsMsg extends keyof FailMsgs
> = If<InvertIf<Invert, Result>, () => void, FAIL<FailMsgs<Invert>[keyToErrorsMsg]>>;
> = If<InvertIf<Invert, Result>, {
then: () => void;
else: FAIL<FailMsgs<Invert>[keyToErrorsMsg]>;
}>;

interface IValidationsPublic<T, I extends boolean = false> {
not: IValidationsPublic<T, Not<I>>;
Expand Down
1 change: 0 additions & 1 deletion lib/rt.config.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
declare interface RT_CONFIG extends RT_CONFIG_SCHEME<{
development: true;
ifConditionWay: 'natural';
}> {}

0 comments on commit 174d70b

Please sign in to comment.