Skip to content

Commit

Permalink
feat(types): Added & modified types & tests;
Browse files Browse the repository at this point in the history
  • Loading branch information
YLfjuk committed Dec 18, 2024
1 parent 6a9d50b commit ab9dbde
Show file tree
Hide file tree
Showing 24 changed files with 624 additions and 24 deletions.
3 changes: 3 additions & 0 deletions packages/types/src/abs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type Abs<N extends number> = `${N}` extends `-${infer P extends number}`
? P
: N;
22 changes: 22 additions & 0 deletions packages/types/src/array-of-n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//? maybe change the order to check if eq before limit (would lead to change in less-than -> less-than-eq-to)

import type { Abs } from './abs';

/**
* @param Amount - amount of zeros
* @param Limit - exclusive upper bound
* @param Acc - A prior accumulation of 0[]
* @param Fill - A number to fill the array with
*/
export type ArrayOfN<
Amount extends number,
Limit extends number = -1,
Acc extends Fill[] = [],
Fill extends number = 0
> = Abs<Amount> extends Amount
? Acc['length'] extends Limit
? never
: Acc['length'] extends Amount
? Acc
: ArrayOfN<Amount, Limit, [...Acc, Fill], Fill>
: never;
7 changes: 7 additions & 0 deletions packages/types/src/at-least-one.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { Prettify } from './prettify';

export type AtLeastOne<T> = Prettify<
{
[K in keyof T]-?: Partial<T> & Required<Pick<T, K>>;
}[keyof T]
>;
9 changes: 9 additions & 0 deletions packages/types/src/exactly-one.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { Prettify } from './prettify';

export type ExactlyOne<T> = Prettify<
{
[K in keyof T]-?: Required<Pick<T, K>> & {
[I in keyof T as I extends K ? never : I]?: never;
};
}[keyof T]
>;
2 changes: 1 addition & 1 deletion packages/types/src/extract-literals.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Primitive } from './primitive';

export type ExtractLiteral<T extends string> =
T extends `${infer U extends Exclude<Primitive, string>}` ? U : T;
T extends `${infer U extends Exclude<Primitive, string | symbol>}` ? U : T;
44 changes: 26 additions & 18 deletions packages/types/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
export * from './deep-dict';
export * from './empty-object';
export * from './extend';
export * from './extract-literals';
export * from './extract-values';
export * from './fn';
export * from './inverse-extract';
export * from './is-disjoint-union';
export * from './mapped-enum';
export * from './mask-literals';
export * from './maybe';
export * from './non-empty-array';
export * from './prettify';
export * from './primitive';
export * from './strict-omit';
export * from './suggest';
export * from './union-to-intersection';
export * from './value-of';
export type * from './abs';
export type * from './array-of-n';
export type * from './at-least-one';
export type * from './deep-dict';
export type * from './empty-object';
export type * from './exactly-one';
export type * from './extend';
export type * from './extract-literals';
export type * from './extract-values';
export type * from './fn';
export type * from './inverse-extract';
export type * from './is-disjoint-union';
export type * from './is-natural';
export type * from './is-tuple';
export type * from './less-than';
export type * from './mapped-enum';
export type * from './mask-literals';
export type * from './maybe';
export type * from './non-empty-array';
export type * from './pick-by';
export type * from './prettify';
export type * from './primitive';
export type * from './strict-omit';
export type * from './suggest';
export type * from './union-to-intersection';
export type * from './value-of';
8 changes: 8 additions & 0 deletions packages/types/src/is-natural.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { Abs } from './abs';

/**
* @description
* return N ∈ ℕ;
* returns whether N is an element of ℕ
*/
export type IsNatural<N extends number> = Abs<N> extends N ? true : false;
3 changes: 3 additions & 0 deletions packages/types/src/is-tuple.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type IsTuple<T extends readonly unknown[]> = number extends T['length']
? false
: true;
22 changes: 22 additions & 0 deletions packages/types/src/less-than.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { Abs } from './abs';
import type { ArrayOfN } from './array-of-n';
import type { IsNatural } from './is-natural';

/**
* @description
* A, B - ℤ (integers)
*
* @note does not work for floats
*/
export type LessThan<
A extends number,
B extends number
> = IsNatural<A> extends true
? IsNatural<B> extends true
? ArrayOfN<A, B> extends never
? false
: true
: false
: IsNatural<B> extends true
? true
: LessThan<Abs<B>, Abs<A>>;
2 changes: 1 addition & 1 deletion packages/types/src/mask-literals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type { Primitive } from './primitive';
* `K extends boolean ? boolean : K` is used since boolean is often spread into `true | false`
*/
export type MaskLiterals<T> = {
[K in Primitive as `${Primitive}`]: InverseExtract<
[K in Primitive as string]: InverseExtract<
T,
K extends boolean ? boolean : K
>;
Expand Down
3 changes: 3 additions & 0 deletions packages/types/src/pick-by.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type PickBy<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};
9 changes: 8 additions & 1 deletion packages/types/src/primitive.ts
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
export type Primitive = string | number | bigint | boolean | null | undefined;
export type Primitive =
| string
| number
| bigint
| boolean
| symbol
| null
| undefined;
32 changes: 32 additions & 0 deletions packages/types/tests/abs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { describe, expectTypeOf, test } from 'vitest';
import type { Abs } from '../src/abs';

describe('returns the absolute value of a number', () => {
test('positive number', () => {
type Actual = Abs<1>;
type Expected = 1;

expectTypeOf<Actual>().toEqualTypeOf<Expected>();
});

test('negative number', () => {
type Actual = Abs<-1>;
type Expected = 1;

expectTypeOf<Actual>().toEqualTypeOf<Expected>();
});

test('zero', () => {
type Actual = Abs<0>;
type Expected = 0;

expectTypeOf<Actual>().toEqualTypeOf<Expected>();
});

test('"negative" zero', () => {
type Actual = Abs<-0>;
type Expected = 0;

expectTypeOf<Actual>().toEqualTypeOf<Expected>();
});
});
80 changes: 80 additions & 0 deletions packages/types/tests/array-of-n.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { describe, expectTypeOf, test } from 'vitest';
import type { ArrayOfN } from '../src/array-of-n';

describe('Array of n', () => {
test('array of positive length', () => {
type Actual = ArrayOfN<1>;
type Expected = [0];

expectTypeOf<Actual>().toEqualTypeOf<Expected>();
});

test('array of 0 length', () => {
type Actual = ArrayOfN<0>;
type Expected = [];

expectTypeOf<Actual>().toEqualTypeOf<Expected>();
});

test('array of "negative" length', () => {
type Actual = ArrayOfN<-1>;
type Expected = never;

expectTypeOf<Actual>().toEqualTypeOf<Expected>();
});

test('Amount < Limit', () => {
type Actual = ArrayOfN<2, 3>;
type Expected = [0, 0];

expectTypeOf<Actual>().toEqualTypeOf<Expected>();
});

test('Amount = Limit', () => {
type Actual = ArrayOfN<3, 3>;
type Expected = never;

expectTypeOf<Actual>().toEqualTypeOf<Expected>();
});

test('Amount > Limit', () => {
type Actual = ArrayOfN<4, 3>;
type Expected = never;

expectTypeOf<Actual>().toEqualTypeOf<Expected>();
});

test('Limit = -1 is the same as no Limit', () => {
type Actual = ArrayOfN<2, -1>;
type Same = ArrayOfN<2>;
type Expected = [0, 0];

expectTypeOf<Actual>().toEqualTypeOf<Same>();
expectTypeOf<Actual>().toEqualTypeOf<Expected>();
});

test('Acc has initial value is the same as without', () => {
type Actual = ArrayOfN<4, -1, [0, 0]>;
type Same = ArrayOfN<4>;

type Expected = [0, 0, 0, 0];

expectTypeOf<Actual>().toEqualTypeOf<Same>();
expectTypeOf<Actual>().toEqualTypeOf<Expected>();
});

test('Fill != 0', () => {
type Actual = ArrayOfN<4, -1, [], 2>;
type Expected = [2, 2, 2, 2];

expectTypeOf<Actual>().toEqualTypeOf<Expected>();
});

test('Acc can not contain numbers other than Fill value', () => {
// @ts-expect-error: testing
type Actual = ArrayOfN<4, -1, [1], 2>;
type Expected = [2, 2, 2, 2];

expectTypeOf<Actual>().not.toEqualTypeOf<Expected>();
});
});
63 changes: 63 additions & 0 deletions packages/types/tests/at-least-one.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { describe, expectTypeOf, it } from 'vitest';
import type { AtLeastOne } from '../src/at-least-one';

describe('requires to pass at least one of the values', () => {
it('should allow objects with at least one of the fields', () => {
type Obj = {
bob: string;
bert: number;
berta: {
check: boolean;
};
};

type Actual = AtLeastOne<Obj>;

const exactlyBob = { bob: '13' };
const exactlyBert = { bert: 13 };
const exactlyBerta = {
berta: {
check: true,
},
};

const bobAndBert = {
bob: '13',
bert: 13,
};

const bobAndBerta = {
bob: '13',
berta: {
check: true,
},
};

const bertAndBerta = {
bert: 13,
berta: {
check: true,
},
};

const all = {
bob: '13',
bert: 13,
berta: {
check: true,
},
};

const nothing = {};

expectTypeOf(exactlyBob).toMatchTypeOf<Actual>();
expectTypeOf(exactlyBert).toMatchTypeOf<Actual>();
expectTypeOf(exactlyBerta).toMatchTypeOf<Actual>();
expectTypeOf(bobAndBert).toMatchTypeOf<Actual>();
expectTypeOf(bobAndBerta).toMatchTypeOf<Actual>();
expectTypeOf(bertAndBerta).toMatchTypeOf<Actual>();
expectTypeOf(all).toMatchTypeOf<Actual>();

expectTypeOf(nothing).not.toMatchTypeOf<Actual>();
});
});
2 changes: 1 addition & 1 deletion packages/types/tests/empty-object.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expectTypeOf, it } from 'vitest';
import type { EmptyObject } from '../src/empty-object';

describe('Deep Dictionary', () => {
describe('Empty Object', () => {
it('should match an empty object', () => {
type Actual = EmptyObject;
const actual = {};
Expand Down
Loading

0 comments on commit ab9dbde

Please sign in to comment.