@matt-usurp/grok
v1.1.0
Published
An expressive series of utilities for language like types
Downloads
9
Maintainers
Readme
Grok
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).
Example
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:
Logic & Expressions
Grok.If
Grok.And
Grok.Or
Grok.Not
Grok.Has
Type Helpers
Grok.Merge
Grok.Union
Grok.Union.FromArray
Grok.Union.RemoveValue
Grok.Union.Has
Grok.Record.RemoveValue
Grok.Record.ReplaceAny
Grok.Record.IsKeyOptional
Value Comparisons
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
Constraints
Grok.Constraint.ObjectLike
Grok.Constraint.FunctionLike
Grok.Constraint.ArrayWithOneOrMore<T>
Utility Helpers
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
Functional Helpers
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>
Testing Helpers
fn()
instance()
partial()
aPartial<T>
toT
disguise
Testing Helpers (types)
Grok.Assert
for asserting values.Grok.Assert.IsTrue
for asserting a value is true.Grok.Assert.IsFalse
for asserting a value is false.
Documentation
Functional Helpers
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.
noop
A function that can be used to represent a no-operation.
never
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.
okv
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
okvr
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
okey
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');
union
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']
provide
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
Testing Helpers
A series of helpers and utlities for enriching your testing experience.
fn
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);
instance
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);
partial
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