cast-as-trophy
v0.0.8
Published
Build the type guards you want for the supported types you can
Downloads
7
Maintainers
Readme
Cast as Trophy
Build the type guards you want for the supported types you can! And enjoy your compile time errors when you forget to keeping something up to date! ;)
The rules of the supported types:
| No. | Rule | Example |
| --- | -------------------------------------------------------------------------------- | -------------------------------------------- |
| 1. | Objects with string
, number
or boolean
properties | { s: string }
|
| 2. | Supported objects can be embedded | { i: { s: string } }
|
| 3. | Arrays can be used for the supported types and objects | { ia: Array<{ s: string }> }
|
| 4. | The supported types and objects can be optional with null
and / or undefined
| { iau: Array<{ s: string }> \| undefined }
|
The library is not optimized for performance. And it is built for TypeScript (with strictNullChecks
option turned on).
How to start?
You can install the library with NPM:
npm i --save cast-as-trophy
And create a caster for a not too complex object type:
import { createCaster } from "cast-as-trophy";
interface Example {
s: string; // You need to call `addString("s")`
n: number; // You need to call `addNumber("n")`
b: boolean; // You need to call `addBoolean("b")`
}
const exampleCaster = createCaster<Example>()
.addString("s") // Needed because of `s: string`
.addNumber("n") // Needed because of `n: number`
.addBoolean("b") // Needed because of `b: boolean`
.build();
And check some values:
const test: unknown = {
s: "test",
n: 42,
b: true,
};
if (exampleCaster.isType(test)) {
// The `test` is type guarded! It has the proper type here.
console.log(`Value of s: ${test.s}`);
}
How to use a caster?
1. isType<TYPE>(value: unknown) => value is TYPE
A simple old fashion type guard.
| | | | ------------- | ----------------------------------------------------------------------- | | Input | Value to check | | Result | Whether the value has the proper type (boolean, but the type guard way) | | Exception | No |
Example:
import { createCaster } from "cast-as-trophy";
interface Example {
s: string;
}
const caster = createCaster<Example>().addString("s").build();
const myLittleObject: unknown = { s: "test" };
if (caster.isType(myLittleObject)) {
// The type of the `myLittleObject` is now known.
console.log(myLittleObject.s);
}
2. castTo<TYPE>(value: unknown) => TYPE
Returns the same object but with the narrowed type. (Throws error when the type of the input is not okay.)
| | | | ------------- | ------------------------------------- | | Input | Value to convert | | Result | Same value but with the narrowed type | | Exception | When the type of the value is wrong |
Example:
import { createCaster } from "cast-as-trophy";
interface Example {
s: string;
}
const caster = createCaster<Example>().addString("s").build();
try {
const value: Example = caster.castTo({ s: "test", n: 42 });
// You can use `s` in the result, but there is no `n`
console.log(`Value of s: ${value.s}`);
} catch {
// Things can go wrong.
}
Warning:
- The returned object can contain additional values
3. copyTo<TYPE>(value: unknown) => TYPE
Returns a copy of checked part of the input with the narrowed type. (Throws error when the type of the input is not okay.)
| | | | ------------- | ----------------------------------- | | Input | Value to convert | | Result | Copy of input | | Exception | When the type of the value is wrong |
Example:
import { createCaster } from "cast-as-trophy";
interface Example {
s: string;
}
const caster = createCaster<Example>().addString("s").build();
try {
const value: Example = caster.copyTo({ s: "test", n: 42 });
// You can use `s` in the result, but there is no `n`
console.log(`Value of s: ${value.s}`);
} catch {
// Things can go wrong.
}
Warning:
- The returned copy only contains the checked props (and nothing else)
- Creating a copy costs extra memory usage and CPU time (can cause possible performance issues)
4. asyncCastTo<TYPE>(value: unknown) => Promise<TYPE>
The asynchronous version of the castTo(...)
method. It does the same but with promises. Can be useful if you work with large object and don't want to block your app.
| | | | ------------- | ----------------------------------------------------------- | | Input | Value to convert | | Result | Copy of the value with the checked type (a promise) | | Exception | When the type of the value is wrong (in a promise like way) |
Example:
import { createCaster } from "cast-as-trophy";
interface Example {
s: string;
}
const caster = createCaster<Example>().addString("s").build();
function doAsyncStuffs(): Promise<void> {
try {
const value = await caster.asyncCastTo({ s: "test" });
console.log(`Value of s: ${value.s}`);
} catch {
// Don't forget: error can be thrown...
}
}
Warning:
- This method returns the same value but with the narrowed type, just like the original
castTo(...)
- There can be a lot of async calls in the background which can cause performance issues too
5. asyncCopyTo<TYPE>(value: unknown) => Promise<TYPE>
The asynchronous version of the copyTo(...)
method. It does the same but with promises. Can be useful if you work with large object and don't want to block your app.
| | | | ------------- | ----------------------------------------------------------------------------- | | Input | Value to convert | | Result | Copy of the checked part of the input value with the checked type (a promise) | | Exception | When the type of the value is wrong (in a promise like way) |
Example:
import { createCaster } from "cast-as-trophy";
interface Example {
s: string;
}
const caster = createCaster<Example>().addString("s").build();
function doAsyncStuffs(): Promise<void> {
try {
const value = await caster.asyncCopyTo({ s: "test" });
console.log(`Value of s: ${value.s}`);
} catch {
// Don't forget: error can be thrown...
}
}
Warning:
- This method creates a copy of the checked part of the input, just like the original
copyTo(...)
- There can be a lot of async calls in the background which can cause performance issues too
Missing features on the casters
Async type guards and async assertions are not supported in TypeScript. (At least not yet. As far as I know.) So implementing a features like asyncIsType
is unfortunatelly not an option.
How to build a caster?
You can do this with 3 easy steps:
- Call the
createCaster<...>(...)
function with the proper generic type - Call the
addXYZ(...)
methods on the returned value. - Call the
build()
at the end of the process.
Example:
import { createCaster } from "cast-as-trophy";
interface Example {
s: string;
n: number;
b: boolean;
}
const exampleCaster = createCaster<Example>().addString("s").addNumber("n").addBoolean("b").build();
Notes:
- The best way to build a caster is this kind of call chain you can see in the example
- The order of the
addXYZ(...)
methods is not important. Just be sure you called all of them! - You can only call the
build()
method after you did all the necessary calls to add your props to the caster builder. Before that thebuild()
will simply not be available. (If there is a propertys: string
and you haven't added yet then you need to add anaddString("s")
before you can call thebuild()
.) - You will get compile errors related to
never
for the keys when you try to use the wrongaddXYZ(...)
method for a property.
Basic types
Methods to add a basic types (the input parameter is the key of the given property):
| Method | Supported type |
| ----------------- | -------------- |
| addString(KEY)
| string
|
| addNumber(KEY)
| number
|
| addBoolean(KEY)
| boolean
|
Example:
import { createCaster } from "cast-as-trophy";
interface Example {
s: string;
n: number;
b: boolean;
}
const result = createCaster<Example>().addString("s").addNumber("n").addBoolean("b").build();
Embedded
You can use the addCustom(..., ...)
method to add a previously created caster for a given property. This means that you can embed the objects and the casters) into each other. The second parameter of the function is the caster you already have. (The first one is the key, as usual.)
| Method | Supported type |
| ------------------------ | ---------------------------------------------- |
| addCustom(KEY, CASTER)
| The embedded object type with a builded caster |
Example:
import { createCaster } from "cast-as-trophy";
interface Inner {
s: string;
}
interface Outer {
i: Inner;
}
const innerCaster = createCaster<Inner>().addString("s").build();
const outerCaster = createCaster<Outer>().addCustom("i", innerCaster).build();
Arrays
You can use arrays of string
, number
, boolean
and embedded objects of these. (Or object of object of these. And so on.)
| Method | Supported type |
| ----------------------------- | ------------------------------------------------------- |
| addStringArray(KEY)
| Array<string>
|
| addNumberArray(KEY)
| Array<number>
|
| addBooleanArray(KEY)
| Array<boolean>
|
| addCustomArray(KEY, CASTER)
| Array of the embedded object type with a builded caster |
Example:
import { createCaster } from "cast-as-trophy";
interface Inner {
sa: string[];
na: number[];
ba: boolean[];
}
interface Outer {
ia: Inner[];
}
const innerCaster = createCaster<Inner>().addStringArray("sa").addNumberArray("na").addBooleanArray("ba").build();
const outerCaster = createCaster<Outer>().addCustomArray("ia", innerCaster).build();
Optional (null and undefined)
At last but not least you can make the previous types optional with null
and / or undefined
| Method | Supported type |
| ---------------------------------------------- | ------------------------------------------ |
| addStringOrUndefined(KEY)
| string \| undefined
|
| addNumberOrUndefined(KEY)
| number \| undefined
|
| addBooleanOrUndefined(KEY)
| boolean \| undefined
|
| addCustomOrUndefined(KEY)
| EmbeddedType \| undefined
|
| addStringArrayOrUndefined(KEY)
| Array<string> \| undefined
|
| addNumberArrayOrUndefined(KEY)
| Array<number> \| undefined
|
| addBooleanArrayOrUndefined(KEY)
| Array<boolean> \| undefined
|
| addCustomArrayOrUndefined(KEY, CASTER)
| Array<EmbeddedType> \| undefined
|
| addStringOrNull(KEY)
| string \| null
|
| addNumberOrNull(KEY)
| number \| null
|
| addBooleanOrNull(KEY)
| boolean \| null
|
| addCustomOrNull(KEY)
| EmbeddedType \| null
|
| addStringArrayOrNull(KEY)
| Array<string> \| null
|
| addNumberArrayOrNull(KEY)
| Array<number> \| null
|
| addBooleanArrayOrNull(KEY)
| Array<boolean> \| null
|
| addCustomArrayOrNull(KEY, CASTER)
| Array<EmbeddedType> \| null
|
| addStringOrNullOrUndefined(KEY)
| string \| null \| undefined
|
| addNumberOrNullOrUndefined(KEY)
| number \| null \| undefined
|
| addBooleanOrNullOrUndefined(KEY)
| boolean \| null \| undefined
|
| addCustomOrNullOrUndefined(KEY)
| EmbeddedType \| null \| undefined
|
| addStringArrayOrNullOrUndefined(KEY)
| Array<string> \| null \| undefined
|
| addNumberArrayOrNullOrUndefined(KEY)
| Array<number> \| null \| undefined
|
| addBooleanArrayOrNullOrUndefined(KEY)
| Array<boolean> \| null \| undefined
|
| addCustomArrayOrNullOrUndefined(KEY, CASTER)
| Array<EmbeddedType> \| null \| undefined
|
Example:
import { createCaster } from "cast-as-trophy";
interface Example {
su: string | undefined;
nn: number | null;
bnu: boolean | null | undefined;
}
const result = createCaster<Example>()
.addStringOrUndefined("su")
.addNumberOrNull("nn")
.addBooleanOrNullOrUndefined("bnu")
.build();
Not supported features for the caster builders
Union types are not supported
Except null
and undefined
, you can make other types optional with them.
You can not use something like this: string | number | boolean
.
Array of arrays is not supported
Only the first level of arrays is available.
Array of unions is not supported
You can not use unions of types in the arrays. So Array<string | number>
is not supported. (The Array<string | undefined>
is not supported too.)
Tuples are not supported
You can't use tuples like [string]
or [number, number]
. Only arrays are supported, like this: string[]
.
Pure undefined
and null
are not supported
You can not use pure undefined
or null
for a property. And you can not use undefined[]
and null[]
array types either.
Index types are not supported
You can not use index types.
Using other exotic types like functions, symbols, etc. are not supported
You can not build caster for object with exotic types in it. (Including unknown
and never
.)
Using custom type guards are not supported
You can not embed your custom type guards.
Alternatives
If this library is not flexible enough for you then a good alternative can be the https://www.npmjs.com/package/ts-looks-like package.