fabiandev / ts-runtime Goto Github PK
View Code? Open in Web Editor NEWRuntime Type Checks for TypeScript
Home Page: https://fabiandev.github.io/ts-runtime/
License: MIT License
Runtime Type Checks for TypeScript
Home Page: https://fabiandev.github.io/ts-runtime/
License: MIT License
Hello, i would like to use your library with my React Native apps, but i don't understand how to change default typescript
runtime on your tsr
runtime. Maybe do you know how to do it?
Nice work @fabiandev; I'm really excited to test this out at some point!
Anyway, just throwing this idea out there in case you or anyone else have time for it at some point: a webpack plugin to automatically handle inserting the import into each source file and building with tsr
.
In addition to making it a lot simpler to get started by piggybacking on existing tooling, this would abstract away most of the work required for integrations like angular/angular-cli#6763 (at least for non-prod builds; some extra work might be needed to integrate tsr
into Angular's AOT compiler).
Currently there seems no sourceMap support. The line numbers in the runtime errors are actually those in the compiled .js
files.
Slick library! One issue with generic classes though.
class Foo {}
class GenericClass <FOO extends Foo> {}
class Implementation extends GenericClass <Foo> {}
is compiled to Javascript with the following line:
Implementation[t.TypeParametersSymbol] = _ImplementationTypeParametersSymbol;
which references the undefined _ImplementationTypeParametersSymbol
and causes a runtime error.
Full compiled output:
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import t from "ts-runtime/lib";
let Foo = class Foo {
};
Foo = __decorate([
t.annotate(t.class("Foo", t.object()))
], Foo);
const _GenericClassTypeParametersSymbol = Symbol("GenericClassTypeParameters");
let GenericClass = class GenericClass {
constructor() {
const _typeParameters = {
FOO: t.typeParameter("FOO", t.ref(Foo))
};
this[_GenericClassTypeParametersSymbol] = _typeParameters;
}
};
GenericClass[t.TypeParametersSymbol] = _GenericClassTypeParametersSymbol;
GenericClass = __decorate([
t.annotate(t.class("GenericClass", GenericClass => {
const FOO = GenericClass.typeParameter("FOO", t.ref(Foo));
return [];
}))
], GenericClass);
let Implementation = class Implementation extends GenericClass {
constructor(...args) {
super(...args);
t.bindTypeParameters(this, t.ref(Foo));
}
};
Implementation[t.TypeParametersSymbol] = _ImplementationTypeParametersSymbol;
Implementation = __decorate([
t.annotate(t.class("Implementation", t.extends(t.ref(GenericClass, t.ref(Foo)))))
], Implementation);
Currently flow-runtime is being used as a runtime type system. In a next step create an interface to exchange the underlying library, e.g., to io-ts, which was developed with TypeScript in mind and seems to be more lightweigh.
Following up the discussion of #5, I have looked over instructions on building a loader, and I should be able to build a simple webpack loader that transforms certain files into runtime check files. Basically, the webpack loader will compile certain ts files with ts-runtime
into javascript, then those files will be passed through ts-loader
or awesome-typescript-loader
as normal.
To make this work however, I need transform()
to be able to take in a string and output a string or buffer. Webpack expects each loader to act this way, it is how we are able to chain loaders together. Is a change like this possible?
Would there be a way to compile a TypeScript project that would only apply these transforms to files within a certain directory (or matching a glob pattern)?
It is possible to to extend each javascript object with a getType() method that them lets me reflect over the property names and methods.
If you give an idea where to start, I will create a branch and get to work
I'm curious; what do you think of a Babel plugin that would transpile this:
type Todo = {
userId: number;
id: number;
title: string;
completed: boolean;
}
const resp = await fetch("https://jsonplaceholder.typicode.com/todos/1");
const data = await resp.json();
assert(data typeof Todo);
to that:
const __TodoType = {
userId: Number;
id: Number;
title: String;
completed: Boolean;
};
const __isTodoType = obj => (
obj &&
obj.constructor === Object &&
Object.keys(obj).length === Object.keys(__TodoType).length &&
Object.entries(obj)
.every(([prop, val]) =>
__TodoType[prop] && val &&
__TodoType[prop] === val.constructor)
);
const resp = await fetch("https://jsonplaceholder.typicode.com/todos/1");
const data = await resp.json();
assert(__isTodoType(data));
https://fabiandev.github.io/ts-runtime/
Uncaught TypeError: Cannot read property 'config' of undefined
at app.js:1
at Object.<anonymous> (app.js:1)
at n (app.js:1)
at app.js:1
at app.js:1
This library seems very exciting! I just started using it and it seems to think that Promise
is a private name:
tsr -c tsconfig.json passit_sdk/*
ℹ ts-runtime v0.1.23
✔ Processing (1s)
✔ Scanning (750ms)
✔ Transforming (505ms)
✖ passit_sdk/api.ts(45,51): error TS4055: Return type of public method from exported class has or is using private name 'Promise'.
...
My tsconfig.js
that I am using is
{
"compilerOptions": {
"target": "es5",
"experimentalDecorators": true,
"sourceMap": true,
"module": "commonjs",
"declaration": true,
"removeComments": true,
"types" : [ "node", "jasmine", "core-js" ],
"outDir": "./js",
"lib": ["es2015", "dom"]
},
"exclude": [
"node_modules",
"js"
]
}
Any ideas?
If I give a rootDir, and ask tsr
to transpile a file that is not from the root directory, it assumes the tsr-declaration.js
path incorrectly:
e.g.
tsr --tsConfig tsconfig.json src/types/assert.ts
will give the error
$ node lib/types/assert.js
Error: Cannot find module './tsr-declarations'
I think the core of this error is ts-runtime assumes I will always be generating runtime check files at the root of my repo. However, I just want runtime checks on a few functions in my repo that deal with user input.
tsconfig.json:
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib/"
},
"include": ["src/**/*.ts"]
}
let me know if you want a reproducible repo, I can push one!
I'm having an issue that production js contains phisical folder names for runtime-interfaces output.
I'm on version 0.1.35
Once I switch on 0.2.0 it generates error. It will generate the error even if I comment out the content of the source ts file
node node_modules/ts-runtime/bin/index.js src/app/glb/ts/runtime-interfaces.ts
i ts-runtime v0.2.0
√ Processing (1s)
√ Scanning (22ms)
√ Transforming (28ms)
× No reflection for syntax kind 'UnknownKeyword' found.
× Emitting was interrupted.
Considering simple test.ts file and tsconfig.json like the following:
console.log('it works')
{
"compilerOptions": {
"noEmitOnError": true
}
}
I am able to compile using tsr src/test.ts
:
ℹ ts-runtime v0.1.35
✔ Processing (3s)
✔ Scanning (29ms)
✔ Transforming (18ms)
✔ Emitting (82ms)
✔ Done in 2s 819ms.
But not with compilerOptions.noEmitOnError=true using tsr -c tsconfig.json src/test.ts
ℹ ts-runtime v0.1.35
✔ Processing (3s)
✔ Scanning (27ms)
✔ Transforming (18ms)
✖ error TS6053: File '/home/thomas/.nvm/versions/node/v10.0.0/lib/node_modules/ts-runtime/node_modules/typescript/lib/lib.d.ts' not found.
✖ error TS2318: Cannot find global type 'Array'.
✖ error TS2318: Cannot find global type 'Boolean'.
✖ error TS2318: Cannot find global type 'Function'.
✖ error TS2318: Cannot find global type 'IArguments'.
✖ error TS2318: Cannot find global type 'Number'.
✖ error TS2318: Cannot find global type 'Object'.
✖ error TS2318: Cannot find global type 'RegExp'.
✖ error TS2318: Cannot find global type 'String'.
✖ src/test.ts(1,15): error TS2307: Cannot find module 'ts-runtime/lib'.
✖ src/test.ts(2,1): error TS2304: Cannot find name 'console'.
✖ Emitting was interrupted.
lib.d.ts exists at this location ([email protected])
If noEmitOnError is set to false, it works again.
How about an option to throw when NaN
is assigned to a number type?
Right now this library allows things like const x: number = parseFloat("hello");
...which makes sense as default behaviour because typeof NaN === "number"
in JavaScript, but it'd be useful to be able to use this library to do things like safely parse values in a querystring, coming from a redis cache, etc. etc.
So could it be another cli option or something?
I have a use case where I want to ensure my custom types are respected during run time as well. I would have written custom checks myself but Typescript's String Literal Types are not available during run time.
I am posting my code sample and generated code with ts-runtime library -
My Code -
type UploadMediaPayload = {
abc: string
}
type analyticsEvent = {
'Upload Media': UploadMediaPayload
}
function sampleFunction<Key extends keyof analyticsEvent>(evtName: Key, evtData: analyticsEvent[Key]){
console.log(evtName, evtData);
}
const getData = () =>
Promise.resolve({
key: "key",
value: "value"
});
// usage (1)
getData()
.then(data => {
const {key, value} = data;
sampleFunction(<keyof analyticsEvent>key, <UploadMediaPayload><unknown>value);
});
getData();
sampleFunction("Upload Media", {abc: "abc"});
let key, value;
sampleFunction(key, value);
Generated Code
import t from "ts-runtime/lib";
const UploadMediaPayload = t.type("UploadMediaPayload", t.object(t.property("abc", t.string())));
const analyticsEvent = t.type("analyticsEvent", t.object(t.property("pload Medi", t.ref(UploadMediaPayload))));
function sampleFunction(evtName, evtData) {
const Key = t.typeParameter("Key", t.any());
let _evtNameType = t.flowInto(Key);
let _evtDataType = t.any();
t.param("evtName", _evtNameType).assert(evtName);
t.param("evtData", _evtDataType).assert(evtData);
console.log(evtName, evtData);
}
t.annotate(sampleFunction, t.function(fn => {
const Key = fn.typeParameter("Key", t.any());
return [t.param("evtName", t.flowInto(Key)), t.param("evtData", t.any()), t.return(t.any())];
}));
const getData = t.annotate(() => {
return Promise.resolve({
key: "key",
value: "value"
});
}, t.function(t.return(t.any())));
// usage (1)
getData()
.then(t.annotate((data) => {
const { key, value } = data;
sampleFunction(key, value);
}, t.function(t.param("data", t.any()), t.return(t.any()))));
getData();
sampleFunction("Upload Media", { abc: "abc" });
let key, value;
sampleFunction(key, value);
analyticsEvent is not referred in the generated code. What do I have to make it work ?
Consider utilizing typescript-json-schema alongside ajv for runtime type reflections and validations to improve reliability, maintainability and performance.
This project seems pretty dead, but I don't see why there isn't any community will to work on it. Flow-runtime is the only reason my company is stuck on Flow instead of TypeScript; there's really just no TypeScript equivalent except this, and runtime checks can be essential for verifying API responses.
I'd probably be willing to help! I just don't know that I have time to do the whole thing myself.
Is it possible to use this library to do local type checks? I don't want to type check my whole app at runtime (I trust TypeScript to do that at compile time), but I'd like to type check individual variables at certain places (mainly API boundaries). So for example this:
verify<MyType>(obj); // throws if obj doesn't conform to interface MyType
This is obviously supported by this tool, because it does it behind the scenes, but I wonder if this API is exposed?
Demo project: https://github.com/cryptokat/ts-runtime-issue/
Hash.d.ts
file is missing due to the error of TS4070 and TS4052
yarn build-tsr --force
yarn run v1.5.1
$ yarn tsr Hash.ts -c tsconfig.json --force
ℹ ts-runtime v0.1.35
✔ Processing (184ms)
✔ Scanning (13ms)
✔ Transforming (14ms)
✖ Hash.ts(5,31): error TS4070: Parameter 'input' of public static method from exported class has or is using private name 'Uint8Array'.
✖ Hash.ts(5,44): error TS4052: Return type of public static method from exported class has or is using private name 'Uint8Array'.
✔ Emitting (287ms)
✔ Done in 498ms, but there were 2 compiler diagnostics.
Done in 1.29s.
export default class Hash {
public static h256(input : Uint8Array) : Uint8Array {
return new Uint8Array(0);
}
}
export default class Hash {
private h256(input : Uint8Array) : Uint8Array {
return new Uint8Array(0);
}
}
export default class Hash {
public static h256(input : string) : string {
return "";
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.