An expressive series of utilities for language like types.
The meta programming within TypeScript
using types can be very powerful and extremely helpful for enforcing strong types and safer builds.
The goal of Grok
is to remove some of the cognitive load that comes with trying to express these types by introducing a more verbose language syntax (as helpers).
At core of Grok
is an abstraction around the extends
expression to introduce the concept of an if-statement
.
With only this Grok
has grown to be extremely powerful and diverse in its offering.
In simple cases Grok
might be considered over complicated but it really shines in complex type logic that changes depending on given inputs.
type MyValue<T> = (
Grok.If<
Grok.Value.IsExtending<T, 'foo'>,
'its foo',
Grok.If<
Grok.Value.IsExtending<T, 'bar'>,
'its bar',
'its something'
>,
>
);
)
More logical helpers and value comparitors are available:
Grok.If
Grok.And
Grok.Or
Grok.Not
Grok.Has
Grok.Merge
Grok.Union
Grok.Union.FromArray
Grok.Union.RemoveValue
Grok.Union.Has
Grok.Record.RemoveValue
Grok.Record.ReplaceAny
Grok.Record.IsKeyOptional
Grok.Value.IsExtending
Grok.Value.IsExactly
Grok.Value.IsAny
Grok.Value.IsNever
Grok.Value.IsUnknown
Grok.Value.IsUndefined
Grok.Value.IsBoolean
Grok.Value.IsTrue
Grok.Value.IsFalse
Grok.Constraint.ObjectLike
Grok.Constraint.FunctionLike
Grok.Constraint.ArrayWithOneOrMore<T>
Maybe
to addundefined
(opposite ofNonNullable
intypescript
core)Nullable
to addnull
(opposite ofNonNullable
intypescript
core)Mutable
to removereadonly
(opposite ofReadonly
intypescript
core)Provider<T>
to represent a function that returns its given value, alias() => T
noop()
a function that has no-operationnever()
anever
assertionokv()
a key accessorokvr()
a key accessor with validation and requirementsokey()
a typekeyof
assistanceunion()
a union value enforcementprovide()
a helper for creatingProvider<T>
fn()
instance()
partial()
aPartial<T>
toT
disguise
Grok.Assert
for asserting values.Grok.Assert.IsTrue
for asserting a value is true.Grok.Assert.IsFalse
for asserting a value is false.
A series of modular functional helpers and utilities are available also. These are all exported from the main entrypoint and as a modular path export that will allow for better tree shaking.
A function that can be used to represent a no-operation.
For use with union exhaustiveness checking which will cause TypeScript to error when the given value is not of type never
.
This function simply throws an error of NeverReachAssertionError
when compiled which should be caught by your error reporting tool of choice.
import { never } from '@matt-usurp/grok';
// or
import { never } from '@matt-usurp/grok/core/assert-never';
declare const union: 1 | 2
if (union === 1 || union === 2) {
// do something
}
never(union);
Introducing a new value to union
will cause never
to raise an error at build time.
An accessor utility for retreiving values from object sources by key. This is a factory that returns a function that can be called multiple times to retrieve values from the source by key.
There is a runtime safe version of this called
okvr
!
import { okv } from '@matt-usurp/grok';
// or
import { okv } from '@matt-usurp/grok/core/object';
declare const mapping: {
readonly name: string;
readonly age?: number;
}
okv(mapping)('name') // mapping.name [string]
okv(mapping)('age', 26) // mapping.age or 26 if undefined
A runtime safe version of okv()
that will throw errors when keys are not available in the given source object.
import { okvr } from '@matt-usurp/grok';
// or
import { okvr } from '@matt-usurp/grok/core/object';
declare const mapping: {
readonly name: string;
readonly age?: number;
}
// mapping.name must not be undefined
okvr(mapping)('name')
// mapping.name will be replaced with fallback if undefined
// an error is thrown if fallback was undefined also.
okvr(mapping)('name', fallback)
// custom validator function is provided checking values are not null also
// mapping.age is verified, an error is thrown if its undefined or null (validator function)
okvr(mapping, ['age'], (v) => v !== null)
// mapping.age is verified, an error is thrown if its undefined
// mapping.name can be undefined, and fallback can be used.
okvr(mapping, ['age'])('name', fallback)
The okvr
function is perfect for accessing keys against process.env
when working in the context of Node.
const env = okvr(process.env);
env('SOME_AVAILABLE_VAR');
env('SOME_UNDEFINED_VAR', fallback);
env('ANOTHER_UNDEFINED_VAR'); // throws
Allows for type-safe strings that are keys of an object.
A functional variant of the <keyof SomeObject>'name' which cannot be used with
JSX`.
import { okey } from '@matt-usurp/grok';
// or
import { okey } from '@matt-usurp/grok/core/object';
declare const mapping: {
readonly name: string;
readonly age?: number;
}
// the same
<keyof typeof mapping>'name';
okey<typeof mapping>('name');
A unique utility for times where you want to convert a union of strings to a string[]
at runtime.
This is done by forcing the values to be defined through a mapping that can be enforced at compile time.
Unlike using MyUnion[]
which does not enforce all values are present within the array.
import { union } from '@matt-usurp/grok';
// or
import { union } from '@matt-usurp/grok/core/value';
type MyUnion = 'name' | 'age';
const values = union<MyUnion>({
name: undefined,
age: undefined,
});
values; // ['name', 'age']
A utility that creates functions that are compatible with a Provider<T>
.
A Provider<T>
is a essentially a value wrapped in a function with no parameters.
When called it will return the given value.
import { provide } from '@matt-usurp/grok'
// or
import { provide } from '@matt-usurp/grok/core/value';
const age = provide(30)
age(); // 30
A series of helpers and utlities for enriching your testing experience.
An extension on vi.fn()
that allows you to pass your function type as the generic and it will resolve the arguments and result for you.
import { fn } from '@matt-usurp/grok/testing';
type MyFunction = (age: number) => boolean;
const mock = fn<MyFunction>();
mock; // Mock<boolean, [age: number]>
expect(mock).toBeCalledTimes(0);
A utiltity that disguises an object as the given type T
. Optionally you can provide a string[]
of functions and they will be automatically set using fn()
above.
Note, this is a disguise and all properties are
undefined
unless set manually after calling. This function is inteded to minimise the amount of type casting and boiler plate needed for creating simplistic mocks of complex classes or types.
import { instance } from '@matt-usurp/grok/testing';
class Person {
public readonly name: string;
public readonly age: number;
public speak(): void { .. };
public walk(steps: number): boolean { .. };
};
const value = instance<Person>([
'speak',
]);
value; // { speak: Mock }
value.speak(); // returns undefined
expect(value.speak).toBeCalledTimes(1);
This is a utility that works similar to instance()
but in a more simplified way, it can be better expressed through the type <T>(in: Partial<T>) => T
.
Again, this is intended for use with testing where a full T
is needed but your test is asserting just a Partial<T>
.
This is intended more of a utility around types and less about functionality.
import { partial } from '@matt-usurp/grok/testing';
type Person = {
readonly name: string;
readonly age: number;
readonly gender: unknown;
}
const value = partial<Person>({
name: 'Tony Stark',
});
value; // Person
value.name; // 'Tony Stark'
value.age; // undefined