typescript-is
TypeScript transformer that generates run-time type-checks.
💿 Installation
npm install --save typescript-is
npm install --save-dev typescript
npm install --save reflect-metadata
💼 Use cases
If you've worked with TypeScript for a while, you know that sometimes you obtain any
or unknown
data that is not type-safe.
You'd then have to write your own function with type predicates that checks the foreign object, and makes sure it is the type that you need.
This library automates writing the type predicate function for you.
At compile time, it inspects the type you want to have checked, and generates a function that can check the type of a wild object at run-time.
When the function is invoked, it checks in detail if the given wild object complies with your favorite type.
In particular, you may obtain wild, untyped object, in the following situations:
- You're doing a
fetch
call, which returns some JSON object.
You don't know if the JSON object is of the shape you expect. - Your users are uploading a file, which is then read by your application and converted to an object.
You don't know if this object is really the type you expect.
- You're reading a JSON string from
localStorage
that you've stored earlier.
Perhaps in the meantime the string has been manipulated and is no longer giving you the object you expect. - Any other case where you lose compile time type information...
In these situations typescript-is
can come to your rescue.
NOTE this package aims to generate type predicates for any serializable JavaScript object.
Please check What it won't do for details.
🎛️ Configuration
This package exposes a TypeScript transformer factory at typescript-is/lib/transformer-inline/transformer
As there currently is no way to configure the TypeScript compiler to use a transformer without using it programatically, the recommended way is to compile with ttypescript.
This is basically a wrapper around the TypeScript compiler that injects transformers configured in your tsconfig.json
.
(please vote here to support transformers out-of-the-box: https://github.com/Microsoft/TypeScript/issues/14419)
Using ttypescript
First install ttypescript
:
npm install --save-dev ttypescript
Then make sure your tsconfig.json
is configured to use the typescript-is
transformer:
{
"compilerOptions": {
"plugins": [
{ "transform": "typescript-is/lib/transform-inline/transformer" }
]
}
}
Now compile using ttypescript
:
npx ttsc
Using with ts-node
, webpack
, Rollup
Please check the README of ttypescript for information on how to use it in combination with ts-node
, webpack
, and Rollup
.
Options
There are some options to configure the transformer.
Property | Description |
---|
shortCircuit | Boolean (default false ). If true , all type guards will return true , i.e. no validation takes place. Can be used for example in production deployments where doing a lot of validation can cost too much CPU. |
ignoreClasses | Boolean (default: false ). If true , when the transformer encounters a class, it will ignore it and simply return true . If false , an error is generated at compile time. |
ignoreMethods | Boolean (default: false ). If true , when the transformer encounters a method, it will ignore it and simply return true . If false , an error is generated at compile time. |
If you are using ttypescript
, you can include the options in your tsconfig.json
:
{
"compilerOptions": {
"plugins": [
{
"transform": "typescript-is/lib/transform-inline/transformer",
"shortCircuit": true,
"ignoreClasses": true,
"ignoreMethods": true
}
]
}
}
⭐ How to use
Before using, please make sure you've completed configuring the transformer.
In your TypeScript code, you can now import and use the type-check function is
(or createIs
), or the type assertion function assertType
(or createAssertType
).
Validation (is
and createIs
)
For example, you can check if something is a string
or number
and use it as such, without the compiler complaining:
import { is } from 'typescript-is';
const wildString: any = 'a string, but nobody knows at compile time, because it is cast to `any`';
if (is<string>(wildString)) {
} else {
}
if (is<number>(wildString)) {
} else {
}
You can also check your own interfaces:
import { is } from 'typescript-is';
interface MyInterface {
someObject: string;
without: string;
}
const foreignObject: any = { someObject: 'obtained from the wild', without: 'type safety' };
if (is<MyInterface>(foreignObject)) {
const someObject = foreignObject.someObject;
const without = foreignObject.without;
}
Assertions (assertType
and createAssertType
)
Or use the assertType
function to directly use the object:
import { assertType } from 'typescript-is';
const object: any = 42;
assertType<number>(object).toFixed(2);
try {
const asString = assertType<string>(object);
asString.toUpperCasse();
} catch (error) {
}
Decorators (ValidateClass
and AssertType
)
You can also use the decorators to automate validation in class methods.
To enable this functionality, you should make sure that experimental decorators are enabled for your TypeScript project.
{
"compilerOptions": {
"experimentalDecorators": true
}
}
You should also make sure the peer dependency reflect-metadata is installed.
npm install --save reflect-metadata
You can then use the decorators:
import { ValidateClass, AssertType } from 'typescript-is';
@ValidateClass()
class A {
method(@AssertType() value: number) {
return value;
}
}
new A().method(42) === 42;
new A().method('42' as any);
To see the declarations of the functions and more examples, please check out index.d.ts.
For many more examples, please check out the files in the test/ folder.
There you can find all the different types that are tested for.
⛔ What it won't do
- This library aims to be able to check any serializable data.
- This library will not check functions. Function signatures are impossible to check at run-time.
- This library will not check classes. Instead, you are encouraged to use the native
instanceof
operator. For example:
import { is } from 'typescript-is';
class MyClass {
}
const instance: any = new MyClass();
is<MyClass>(instance);
if (instance instanceof MyClass) {
}
- This library will not magically check unbound type parameters. Instead, make sure all type parameters are bound to a well-defined type when invoking the
is
function. For example:
import { is } from 'typescript-is';
function magicalTypeChecker<T>(object: any): object is T {
return is<T>(object);
}
If you stumble upon anything else that is not yet supported, please open an issue or submit a PR. 😉
🗺️ Road map
Features that are planned:
More detailed error message when using assertType
and createAssertType
.
Give the reason why the assertion failed to the user as part of the error.
issue 2
Done as of version 0.10.0
.- Support detailed error message when using the decorators
@ValidateClass
and @AssertType
. - Detect additional keys. issue 11
- Promise support. Something like
assertOrReject<Type>(object)
will either resolve(object)
or reject(error)
. - Optimize the generated conditions. Things like
false || "key" === "key"
can be simplified. Might be more interesting to publish a different library that can transform a TypeScript AST, and then use it here, or use an existing one. Might be out of scope, as there are plenty of minifiers/uglifiers/manglers out there already.
🔨 Building and testing
git clone https://github.com/woutervh-/typescript-is.git
cd typescript-is/
npm install
npm run build
npm run test