woutervh- / typescript-is Goto Github PK
View Code? Open in Web Editor NEWLicense: MIT License
License: MIT License
This may be getting into the territory of custom error reporting (io-ts#error-reporting for example). But it could also just be a special assertion generator. The idea is that I would like to validate a function's parameters. typescript-is
provides all the validation I need to make that happen, but the errors it gives back are not as helpful to an end user. This is easier shown than explained:
type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any ? A : never
type Fn = (a: number, b: string) => boolean
type Args = ArgumentTypes<Fn>
const validateFn = createAssertType<ArgumentTypes<Fn>>()
const fn: Fn = (...args) => {
validateFn(args)
const [a, b] = args
return true
}
fn() // TypeGuardError: validation failed at $: expected an array of length 2
// could be: TypeGuardError: validation failed at $: expected 2 arguments
fn(1, 2) // TypeGuardError: validation failed at $.[1]: expected a string
// could be TypeGuardError: validation failed at argument 2: expected a string
fn(1, 'b') // correct usage
I think a wrapAssertFunctionParams<T>
would be handy, as a higher order function. Usage would look like:
// generic type argument could be optional
const fnWithValidation = wrapAssertFunctionParams<Fn>(fn)
For the time being I will be writing something myself for my library which does just this. I can post the implementation here if it is interesting
Is there a way to manually run a transform and import the transformed code for this library? I'm using create-react-app and don't want to eject in order to change my webpack loaders
Hello!
I use react-scripts to set up my react project. It would be nice to use typescript-is with it without "ejecting". For the first trial I have added the plugins
section in my tsconfig.json
.
Unfortunately, I've got this error:
Error: This module should not be used in runtime. Instead, use a transformer during compilation.
Do you have any information what to do when I want to use it with react-scripts?
It would be nice if the error thrown from assertType and createAssertType used a custom error and exposed that error from the library.
Currently my code using assertions looks like this:
// assert.ts
const assertConfigType = (config: any) => {
try {
assertType<Config>(config)
} catch (e) {
throw new RuntimeTypeError(e.message)
}
}
// further up the stack
try {
...
} catch (e) {
if (e instanceof RuntimeTypeError) {
console.log('poorly formed config given')
console.log(e.message)
process.exit(1)
}
}
It would be nice if I could simplify the code to this:
// assert.ts
const assertConfigType = (config: any) => assertType<Config>(config)
// further up the stack
import { RuntimeTypeError } from 'typescript-is'
try {
...
} catch (e) {
if (e instanceof RuntimeTypeError) {
...
}
}
For reference, node-fetch
does this with the FetchError
class https://www.npmjs.com/package/node-fetch#class-fetcherror
Having the following code:
createAssertType<{
data: {
node: { a: string; closed: string; b: string };
};
}>()({ data: { node: { a: "a", closed: false, b: "b" } } });
When running yarn ttsc --watch
and making changes in this file, here is the compiled result:
But if I run yarn ttsc
, then the compiled result becomes correct:
I know you mentioned before that this strives to only work for serializable data.
However, 😏 would it be possible, happily hidden behind an options flag, to not simply ignore methods/functions but to allow at least a simple type check of sorts, to at least make the minimal guarantee that the provided value is actually a function and not e.g. a number?
Input:
import { createIs } from 'typescript-is';
export const enum MyEnum {
Alpha = 'alpha',
Bravo = 'bravo',
Charlie = 'charlie',
Delta = 'delta',
Echo = 'echo',
Foxtrot = 'foxtrot',
Golf = 'golf',
Hotel = 'hotel',
India = 'india',
Juliet = 'juliet',
}
export const isMyEnum = createIs<MyEnum>();
typescript-is 0.9 output:
const isMyEnum = object => { return object === "alpha" || object === "bravo" || object === "charlie" || object === "delta" || object === "echo" || object === "foxtrot" || object === "golf" || object === "hotel" || object === "india" || object === "juliet"; };
export { isMyEnum };
typescript-is 0.10 output:
import { createIs } from 'typescript-is';
const isMyEnum = createIs(object => { var path = ["$"]; function _74(object) { if (object !== "alpha")
return "validation failed at " + path.join(".") + ": expected string 'alpha'";
else
return null; } function _76(object) { if (object !== "bravo")
return "validation failed at " + path.join(".") + ": expected string 'bravo'";
else
return null; } function _78(object) { if (object !== "charlie")
return "validation failed at " + path.join(".") + ": expected string 'charlie'";
else
return null; } function _80(object) { if (object !== "delta")
return "validation failed at " + path.join(".") + ": expected string 'delta'";
else
return null; } function _82(object) { if (object !== "echo")
return "validation failed at " + path.join(".") + ": expected string 'echo'";
else
return null; } function _84(object) { if (object !== "foxtrot")
return "validation failed at " + path.join(".") + ": expected string 'foxtrot'";
else
return null; } function _86(object) { if (object !== "golf")
return "validation failed at " + path.join(".") + ": expected string 'golf'";
else
return null; } function _88(object) { if (object !== "hotel")
return "validation failed at " + path.join(".") + ": expected string 'hotel'";
else
return null; } function _90(object) { if (object !== "india")
return "validation failed at " + path.join(".") + ": expected string 'india'";
else
return null; } function _92(object) { if (object !== "juliet")
return "validation failed at " + path.join(".") + ": expected string 'juliet'";
else
return null; } function _94(object) { var conditions = [_74, _76, _78, _80, _82, _84, _86, _88, _90, _92]; for (const condition of conditions) {
var error = condition(object);
if (!error)
return null;
} return "validation failed at " + path.join(".") + ": there are no valid alternatives"; } var error = _94(object); return error; });
export { isMyEnum };
typescript-is 0.11 seems to have the same output as typescript-is 0.10
Especially given that the output of the isMyEnum
function is just a boolean, this seems extremely wasteful in the 0.10+ versions.
I just tried to validate that one of my optional config variables is a RegExp
. typescript-is
complained and told me:
Error: Encountered a method declaration, but methods are not supported. Please check the README
Would it be possible to either support RegExp
, or to opt-out of this error explicitly in the is<...>(...)
?
Hi.
Currently, to wrap some async request call with the library, one has to write:
const res = assertType<{
name: string;
age?: number;
}>(await client.request(
“https://example.com”,
{ some_post_data: 123, other: “abc” }
));
It doesn’t look nice:
A way better syntax would be:
const res = await client
.request(
“https://example.com”,
{ some_post_data: 123, other: “abc” }
)
.enforce<{
name: string;
age?: number;
}>();
(This is how Prettier would format it.)
Is it possible to implement this .postfix-like syntax in the transformer?
Notice 3 things here:
Promise<T> & EnforcePromiseMixin<T>
)), it’s easily expressible in TS I guess.If it’s possible to implement such a thing (is it possible in theory at all?), it would be a huge relief IMHO. There is no other library which supports such things.
TypeScript 3.7 introduces the assert keyword microsoft/TypeScript#32695, it allows code to be written this way:
import { assertType } from 'typescript-is';
try {
assertType<string>(object); // No need to reassign
object.toUpperCasse();
} catch (error) {
// ...
}
instead of this:
import { assertType } from 'typescript-is';
try {
const asString = assertType<string>(object);
asString.toUpperCasse();
} catch (error) {
// ...
}
It'd be good if the library provided support for it, maybe using a new function if backwards compatibility is required.
I just wanted to drop this in here for your information. I don't know the root cause, or whether this issue even belongs in this repo but I thought it might be helpful for you to know about this.
When running with ts-node-dev --compiler ttypescript --respawn --transpileOnly src/index.ts
, I get the error This module should not be used in runtime. Instead, use a transformer during compilation.
when the validator runs. I don't get the error in parts of the program where the validator is not running.
Removing the --transpileOnly
fixes the problem.
Sometimes I want to verify if an object has a key without rejecting everything else. Sometimes I want to verify an object has a key and nothing else. Configuring this at the transformer level can be too broad.
I'm trying to use typescript-is
to provide runtime validation for a library that manipulates a custom AST that is serialized/deserialized from well structured JSON. I've written out the type definition using only interfaces and a few basic types. When trying to compile it throws the following error
NestedError: Failed to transform node at: /Users/tristan/code/mobiledoc-core/lib/index.ts:53:32
at transformNodeAndChildren (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/transformer.js:33:15)
at ts.visitEachChild (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/transformer.js:35:62)
at visitNode (/Users/tristan/code/mobiledoc-core/node_modules/typescript/lib/typescript.js:60504:23)
at Object.visitEachChild (/Users/tristan/code/mobiledoc-core/node_modules/typescript/lib/typescript.js:60778:59)
at transformNodeAndChildren (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/transformer.js:35:15)
at ts.visitEachChild (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/transformer.js:35:62)
at visitNodes (/Users/tristan/code/mobiledoc-core/node_modules/typescript/lib/typescript.js:60555:48)
at Object.visitEachChild (/Users/tristan/code/mobiledoc-core/node_modules/typescript/lib/typescript.js:60774:45)
at transformNodeAndChildren (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/transformer.js:35:15)
at ts.visitEachChild (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/transformer.js:35:62)
Caused By: Error: Classes cannot be validated. https://github.com/woutervh-/typescript-is/issues/3
at Object.checkIsClass (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/visitor-utils.js:23:19)
at visitObjectType (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:155:22)
at visitType (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:346:16)
at visitTypeReference (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:142:20)
at visitType (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:338:16)
at visitTypeReference (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:142:20)
at visitType (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:338:16)
at ts.createFunctionDeclaration.ts.createBlock.propertyInfos.map (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:100:23)
at Array.map (<anonymous>)
at VisitorUtils.setFunctionIfNotExists (/Users/tristan/code/mobiledoc-core/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:94:30)
Here are the type definitions
// Cards
type CardPayload = Record<string, any>
type Card = [string, CardPayload]
// Atoms
type Atom = [string, string, unknown]
// Markers
// [typeIdentifier, openMarkupsIndexes, numberOfClosedMarkups, text]
type TextMarker = [0, number[], number, string]
// [typeIdentifier, openMarkupsIndexes, numberOfClosedMarkups, atomIndex]
type AtomMarker = [1, number[], number, number]
type Marker = TextMarker | AtomMarker
// Sections Tags enums / unions
type MarkupSectionTag = 'aside' | 'blockquote' | 'pull-quote' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'p'
type ListSectionTag = 'ul' | 'ol'
// Section Identifiers
type MarkupIdentifer = 1
type ImageIdentifer = 2
type ListIdentifer = 3
type CardIdentifer = 10
// Sections
type MarkupSection = [MarkupIdentifer, MarkupSectionTag, Marker[]]
type ImageSection = [ImageIdentifer, string]
type ListSection = [ListIdentifer, ListSectionTag, Marker[][]]
type CardSection = [CardIdentifer, number]
type Section = MarkupSection | ImageSection | ListSection | CardSection
// Markups
type MarkupTag = 'a' | 'b' | 'code' | 'em' | 'i' | 's' | 'strong' | 'sub' | 'sup' | 'u'
type Markup = [MarkupTag, string[] | undefined]
// Mobiledoc Interface
export interface Mobiledoc_0_3_2 {
atoms: Atom[]
cards: Card[]
markups: Markup[]
sections: Section[]
readonly version: '0.3.2'
}
is<Mobiledoc_0_3_2>(data) //=> throws compilation error
is<Section>(data) //=> throws compilation error
is<Markup>(data) //=> throws compilation error
is<Card>(data) //=> ok
is<Atom>(data) //=> ok
The interesting thing is that if I make the Mobiledoc_0_3_2 only have sections
and cards
properties it still throws an error, however if I make them not an array of those types it seems to work fine. Perhaps Array is being seen as a class?
Since at the moment typescript-is does not support using is<T>()
when T is a class, I have used a mapped type as this:
class MyClass {
method() : string {
return "";
};
}
type MappedType = {
[P in keyof MyClass]: MyClass[P];
}
but typescript-is does not check properly MappedType and throws a error, though the flag "ignoreMethods" is true: it not ignore the method, but expect the property contains an object:
let mappedType : MappedType = {
method() {
return "!!!";
}
};
assertType<MappedType>(mappedType);
error log:
TypeGuardError: validation failed at $.method: expected an object
at Object.assertType (...\node_modules\typescript-is\index.js:62:15)
at Object. (...\my_test.js:10:21)
at Module._compile (internal/modules/cjs/loader.js:736:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:747:10)
at Module.load (internal/modules/cjs/loader.js:628:32)
at tryModuleLoad (internal/modules/cjs/loader.js:568:12)
at Function.Module._load (internal/modules/cjs/loader.js:560:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:801:12)
at executeUserCode (internal/bootstrap/node.js:526:15)
at startMainThreadExecution (internal/bootstrap/node.js:439:3)
I was just trying out your transformer as I planned to write something similar myself until I found yours, but on a medium sized library just enabling the transformer I see a 21% increase in compile time (from 3.82 seconds to 4.63 on a 16 core machine with all NVMe storage).
Is that expected?
Using ttypescript (ttsc)
interface IDevice {
createdAt: Date;
}
const foo = { createdAt: new Date() } as any;
assertType<IDevice>(foo);
This causes:
Error: Encountered a method declaration, but methods are not supported. Issue: https://github.com/woutervh-/typescript-is/issues/5
I have an interface which has a date field and when I try to assert an object against this interface the library throws the following error
Failed to transform node at: at transformNodeAndChildren (/xxx/node_modules/typescript-is/lib/transform-inline/transformer.js:27
interface MyInterface {
id: string;
createdAt: Date;
modifiedAt: Date;
}
Does the library doesnt support interfaces with Date
fields?
"This library will not check classes. Instead, you are encouraged to use the native instanceof operator."
Wouldn't it be easy to have this library generate the instanceof code? What's the reason not to?
Hey @woutervh-. Looks like we're both working on similar tools. I would have emailed you privately but I can't find your contact details.
Maybe it would make sense to work together instead of building the two tools in parallel?
I've got a debug mode that will explain why the check failed. You've got some of my planned features (assertions etc). Generally speaking I think I prefer your API and your project seems more mature than ts-auto-guard. Without having looked I assume you've got a nicer code base too. ;)
Let me know what you think.
It might be easier to read from the bottom as I simplified and narrowed down the source of the error over time
I get the following exception having updated a type. The update is non-trivial, and I've been unable (so far) to pin down the exact cause, but here's the jist of what the type is.
It represents a set of jobs, each of which is an async function with either parameters in an array via a params
member, or a single parameter via a param
member, like this:
interface ValidJobs {
testJob: typeof testJob,
rolloverJob: typeof rolloverJob,
replicationTask: typeof replicationTask,
processOutstanding: typeof processOutstanding,
}
type GenericJobFunction = (...args: any[]) => Promise<any>;
type JobParams<J extends GenericJobFunction> = Parameters<J> extends { length: 1 }
? { params: Parameters<J> } | { param: Parameters<J>[0] }
: Parameters<J> extends { length: 0 }
? undefined
: { params: Parameters<J> }
type JobSpec<J extends GenericJobFunction> = {
runsOn?: Roles[] | undefined | null | false,
interval: number | 'startup' | 'never';
timeout?: number;
} & JobParams<J>
export type Jobs = {
[jobName in keyof ValidJobs]?: JobSpec<ValidJobs[jobName]> | false;
};
A sample of the object passed to validate is something like:
{
"rolloverJob": {
"interval":3,
"param":{"key":"837hdy487srt56x" }
},
"replicationTask": {
"interval":10,
"params":[12,"hourly check"]
},
"processOutstanding": {
"interval":5,
"runsOn": ["masternodes"]
},
}
The previous version of the type was much less strict about defining the relationship between the keys of Jobs
and the parameters (ie it was possible to pass parameters for one job to the definition of another).
Obviously, if I do find what causes/suppresses the error I'll let you know, but I'm hoping the stack dump & defns will point you (and therefore me!) in the right direction.
Version: "typescript-is": "^0.13.0"
NestedError: Failed to transform node at: /Users/matthew.woolf/git/news-search/f-extract/config/index.ts:175:13
at transformNodeAndChildren (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/transformer.js:33:15)
at ts.visitEachChild (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/transformer.js:35:62)
at visitNode (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript/lib/typescript.js:67586:23)
at Object.visitEachChild (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript/lib/typescript.js:67814:45)
at transformNodeAndChildren (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/transformer.js:35:15)
at ts.visitEachChild (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/transformer.js:35:62)
at visitNode (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript/lib/typescript.js:67586:23)
at Object.visitEachChild (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript/lib/typescript.js:67846:52)
at transformNodeAndChildren (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/transformer.js:35:15)
at ts.visitEachChild (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/transformer.js:35:62)
Caused By: Error: Could not generate type-check; unsupported type with flags: 16777216
at visitType (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:369:15)
at ts.createFunctionDeclaration.ts.createBlock.propertyInfos.map (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:100:23)
at Array.map (<anonymous>)
at VisitorUtils.setFunctionIfNotExists (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:94:30)
at Object.setFunctionIfNotExists (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-utils.js:34:46)
at visitRegularObjectType (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:76:25)
at visitObjectType (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:168:16)
at visitType (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:346:16)
at intersectionType.types.map (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:202:72)
at Array.map (<anonymous>)
Not sure if this is intended behavior, please let me know if so!
ttypescript version: 3.2.4
typescript-is version: 0.7.17
node version: 11.6.0
Steps to reproduce:
Try compiling a file containing the following:
import { is } from 'typescript-is';
interface Wrapped<T> {
wrap: T;
}
interface X<T> {
a: Wrapped<T>;
}
is<X<number>>({ a: { wrap: 2 } });
Actual results:
The compilation fails with an error like this:
typescript-is: transforming program with 30 source files; using TypeScript 3.2.4.
undefined:86995
throw e;
^
NestedError: Failed to transform node at: /home/geomaster/Projects/tests/typescript-is/index.ts:9:2
at transformNodeAndChildren (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transformer.js:27:15)
at ts.visitEachChild (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transformer.js:29:62)
at visitNode (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript/lib/typescript.js:63390:23)
at Object.visitEachChild (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript/lib/typescript.js:63664:59)
at transformNodeAndChildren (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transformer.js:29:15)
at ts.visitEachChild (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transformer.js:29:62)
at visitNodes (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript/lib/typescript.js:63441:48)
at visitLexicalEnvironment (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript/lib/typescript.js:63474:22)
at Object.visitEachChild (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript/lib/typescript.js:63778:54)
at transformNodeAndChildren (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transformer.js:29:15)
Caused By: Error: Unbound type parameter, missing type node.
at visitTypeParameter (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:255:15)
at visitType (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:362:16)
at visitTypeParameter (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:257:12)
at visitType (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:362:16)
at createPropertyCheck (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:8:28)
at visitPropertySignature (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:41:16)
at visitDeclaration (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:49:16)
at visitPropertySymbol (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:77:25)
at visitRegularObjectType (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:131:29)
at visitObjectType (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:172:16)
Expected results:
Since the T
type parameter is bound to number
in X<number>
, I'd expect wrap
to also be of known type, precisely, Wrapped<number>
. However, it looks like typescript-is isn't able to deduce this, and so thinks that T
in a: Wrapped<T>
is an unbound type parameter.
Thanks and let me know if I can help with more info!
When developing a module we often npm link
it to a parent application for testing. This way we temporarily rely on the parent app's compiler - which has neither ttypescript
, nor it checks the linked module's tsconfig
file -> meaning it will not run the transformer and is<...>
will not be generated. This way it's pretty much impossible to develop using npm link
unless I'm missing something.
Do you have an idea for this situation?
In addition to the current functionality, it would be very nice to have JSON schema generation as part of the transform. This would provide a means of communicating types to other non-typescript systems. For example, the ability to perform client side validation of data by pushing out JSON schema to the clients so they could do their own local validation prior to submitting data to an HTTP API, for example.
In my project I use webpack and I am able to use typescript-is without the ttypescript dependency. ts-loader
has the ability to pass transformers directly into it. The following is enough to get typescript-is validators up and running.
const typescriptIsTransformer = require('typescript-is/lib/transform-inline/transformer').default
module.exports = {
// I am hiding the rest of the webpack config
module: {
rules: [
{
test: /\.ts$/,
exclude: /node_modules/,
loader: 'ts-loader',
options: {
getCustomTransformers: program => ({
before: [typescriptIsTransformer(program)]
})
}
},
}
Just a thought if you want to drop it into your readme
interface Test {
a?: string
}
assertType<Test>({ a: undefined })
// TypeGuardError: validation failed at $.a: expected a string
Is this expected behavior? I thought a?: string
and a: string | undefined
were equivalent in TypeScript.
kai@desktop:/bigdata/KAI/projects/Mechanic$ ./prepare-checkin.sh
yarn run v1.13.0
$ ttsc
typescript-is: transforming program with 98 source files; using TypeScript 3.2.4.
/bigdata/KAI/projects/Mechanic/node_modules/typescript/lib/typescript.js:86995
throw e;
^
NestedError: Failed to transform node at: /bigdata/KAI/projects/Mechanic/src/io/readRuleFile.ts:28:19
at transformNodeAndChildren (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/transformer.js:27:15)
at ts.visitEachChild (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/transformer.js:29:62)
at visitNode (/bigdata/KAI/projects/Mechanic/node_modules/typescript/lib/typescript.js:63390:23)
at Object.visitEachChild (/bigdata/KAI/projects/Mechanic/node_modules/typescript/lib/typescript.js:63694:154)
at transformNodeAndChildren (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/transformer.js:29:15)
at ts.visitEachChild (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/transformer.js:29:62)
at visitNodes (/bigdata/KAI/projects/Mechanic/node_modules/typescript/lib/typescript.js:63441:48)
at Object.visitEachChild (/bigdata/KAI/projects/Mechanic/node_modules/typescript/lib/typescript.js:63696:63)
at transformNodeAndChildren (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/transformer.js:29:15)
at ts.visitEachChild (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/transformer.js:29:62)
Caused By: Error: Unsupported declaration kind: 155
at visitDeclaration (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/visitor.js:52:15)
at visitPropertySymbol (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/visitor.js:77:25)
at visitRegularObjectType (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/visitor.js:131:29)
at visitObjectType (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/visitor.js:172:16)
at visitType (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/visitor.js:371:20)
at type.types.map (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/visitor.js:213:24)
at Array.map ()
at visitUnionOrIntersectionType (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/visitor.js:213:10)
at visitType (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/visitor.js:380:16)
at visitArrayObjectType (/bigdata/KAI/projects/Mechanic/node_modules/typescript-is/lib/transform-inline/visitor.js:66:33)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Using ignoreMethods has no effect for the following data
const test = {
list: {
listItem: "aaa",
data: {
id: {
some: "span.to-details",
convert: x => x
}
}
}
};
validate against the following interface
interface IElement {
some?: string;
convert?: (value: string) => string;
}
interface IMap {
id: string|IElement;
}
interface ITest {
list: {
listItem: string;
data: IMap;
convert?: (value: string) => string;
};
}
by using assertEquals<ITest>(test)
. This results in TypeGuardError: validation failed at $.list.data.id: there are no valid alternatives
. The following data results in TypeGuardError: validation failed at $.list.convert: expected an object
const test = {
list: {
listItem: "aaa",
data: {
id: {
some: "span.to-details"
}
},
convert: x => x
},
};
Using
const test = {
list: {
listItem: "aaa",
data: {
id: {
some: "span.to-details"
}
}
},
};
works as expected.
Hello!
I noticed some peculiarity when working with optional properties.
For example:
interface SomeInterface {
someOptionalProperty?: string;
}
const value: SomeInterface = { someOptionalProperty: undefined };
This code is quite valid and can be compiled without any problems.
Example with assertType:
For example:
interface SomeInterface {
someOptionalProperty?: string;
}
const value: SomeInterface = assertType<SomeInterface>({ someOptionalProperty: undefined });
In this case, I will get the error:
TypeGuardError: validation failed at $.someOptionalProperty: expected a string
Hi! I saw that a new version was published just a few days ago, though its hard to say that that release actually changed (though I did my best to dig through the commits). It would be awesome if you started keeping a CHANGELOG.txt or used github releases for future versions so we can see what new features/fixes/breaking changes are part of that release.
Of course, its more work to maintain, but it helps you (the maintainer) look back on what you have done over time too.
This may help, this may not, but on one of my personal projects (of which I am using typescript-is
🎈!) I automate the process of publishing so that travis ci runs npm publish
for me upon creating a github release https://github.com/andykais/scrape-pages/blob/319dd19b0c952bd9b72ba4f23b3995c25fc6bd81/.travis.yml
interface HasPassword {
password: string
}
interface HasUsername {
username: string
}
type UsernamePassword = HasPassword & HasUsername
const object = {username: 'test', password: 'test'}
try {
assertType<UsernamePassword>(object)
} catch (e) {
console.log(e)
}
Result:
TypeGuardError: validation failed at $: validation failed at $: superfluous property 'username' in object
at Object.assertType (/Users/LoganDark/nitrogen/backend/node_modules/typescript-is/index.js:58:15)
at Object.<anonymous> (/Users/LoganDark/nitrogen/backend/index.ts:239:5)
at step (/Users/LoganDark/nitrogen/backend/index.ts:32:23)
at Object.next (/Users/LoganDark/nitrogen/backend/index.ts:13:53)
at /Users/LoganDark/nitrogen/backend/index.ts:7:71
at new Promise (<anonymous>)
at __awaiter (/Users/LoganDark/nitrogen/backend/index.ts:3:12)
at /Users/LoganDark/nitrogen/backend/index.ts:224:16
at Layer.handle [as handle_request] (/Users/LoganDark/nitrogen/backend/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/LoganDark/nitrogen/backend/node_modules/express/lib/router/route.js:137:13) +1ms
Ignore the line numbers, I simplified the code for the issue.
Configuration:
{
"transform": "typescript-is/lib/transform-inline/transformer",
"ignoreClasses": true,
"ignoreMethods": true,
"disallowSuperfluousObjectProperties": true
}
The object does have username
and password
keys and they are both strings.
Adding | null
alternative significantly degrades verboseness of the error message.
Example:
createAssertType<{
data: {
node: { a: string; closed: string; b: string } | null;
};
}>()({ data: { node: { a: "a", closed: false, b: "b" } } });
// Result:
// TypeGuardError: validation failed at $.data.node: there are no valid alternatives
Notice that it doesn't mention the name of the mis-typed field ("closed"), so it's pretty hard to debug, what's the problem (especially when there is a lot of fields).
If I remove | null
clause, it starts showing the field name:
createAssertType<{
data: {
node: { a: string; closed: string; b: string };
};
}>()({ data: { node: { a: "a", closed: false, b: "b" } } });
// Result:
// TypeGuardError: validation failed at $.data.node.closed: expected a string
Is it (at least theoretically) possible to improve error messages here? The case when | null
or | undefined
or field?: type
are used is pretty common (e.g. in GraphQL responses, the field is often times nullable).
I've made the changes to jest.config.js
to use ttypescript
and I've modified my tsconfig.json
to use typescript-is/lib/transform-inline/transformer
as a transform plugin. I can compile everything fine. But when I try to use Jest, I get:
This module should not be used in runtime. Instead, use a transformer during compilation.
I've created a repo that reproduces the issue here:
https://github.com/xogeny/siren-types
Be sure to use the ttypescript
branch.
Sample :
import { assertType } from 'typescript-is';
type Works = [number];
assertType<Works>([0]);
type Fails = [];
assertType<Fails>([]);
If you comment out the final line, all is good, if you don't you get (at compile time):
NestedError: Failed to transform node at: _test/ex.ts:6:17
at transformNodeAndChildren (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/transformer.js:33:15)
at ts.visitEachChild (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/transformer.js:35:62)
at visitNode (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript/lib/typescript.js:67586:23)
at Object.visitEachChild (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript/lib/typescript.js:67860:59)
at transformNodeAndChildren (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/transformer.js:35:15)
at ts.visitEachChild (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/transformer.js:35:62)
at visitNodes (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript/lib/typescript.js:67637:48)
at visitLexicalEnvironment (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript/lib/typescript.js:67670:22)
at Object.visitEachChild (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript/lib/typescript.js:67974:54)
at transformNodeAndChildren (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/transformer.js:35:15)
Caused By: Error: Expected tuple type to have type arguments.
at visitTupleObjectType (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-name.js:10:15)
at visitObjectType (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-name.js:46:16)
at Object.visitType (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-name.js:114:16)
at visitTupleObjectType (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:12:34)
at visitObjectType (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:161:16)
at visitType (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:347:16)
at visitTypeReference (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:143:20)
at Object.visitType (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/visitor-type-check.js:339:16)
at createArrowFunction (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/transform-node.js:16:36)
at Object.transformNode (/Users/matthew.woolf/git/news-search/f-extract/node_modules/typescript-is/lib/transform-inline/transform-node.js:60:35)
Awesome project BTW.
This is a similar issue to #4. In my case I am getting the error when trying to use assertType<T>(data)
async function fetchContent<T>(filename: string): Promise<boolean> {
const url = `/${filename}`;
const res = await fetch(url);
const data = await res.json();
try {
assertType<T>(data);
return true;
} catch (error) {
console.log(error.message);
return false;
}
}
fetchContent<CMS.Homepage>('homepage.json');
Hi there, been playing about with this library and I think it looks really good - especially as a way of defining contracts for messages between client / server.
This is probably out of scope but I thought I'd ask - it might be useful to have a way of specifying regexes on strings.
I'm not sure how that would look or work though, some custom comment syntax or decorator?
Is it possible to determine which property is valid on an object from the AssertType
function? Currently I only see Type assertion failed
Something like the following would be helpful:
interface MyRequest {
name: string,
age: number
}
try {
const asString = assertType<MyRequest>(unknownObj);
} catch (e) {
console.log('error: ', e); // "error: Object does not have property: 'name' "
}
First: Thanks for this fantastic work! Finally bringing runtime sanity to objects, APIs, files, etc.
So I'm in the process of adding validators across my code; something like:
interface Person {
name: string;
age: number;
}
const isPerson = createIs<Person>();
which means that I'm repeating that isInterface = createIs<Interface>()
line for all my interfaces and types.
Would you consider automatically generating is[Interface]
, assert[Interface]
and other checker and assertion functions and emitting them in an is.ts
(or is.js
) file, usable for runtime?
Same idea as how TypeScript can produce declarations
, your library can produce a validator/checker/assertion bundle.
This isn't a huge concern, but when running the transformer with webpack, my console gets spammed with line after line of typescript-is: transforming program with 893 source files; using TypeScript 3.3.1.
https://github.com/woutervh-/typescript-is/blob/master/src/transform-inline/transformer.ts#L7
Either I’m doing something wrong, or this is not yet supported:
interface Example {
exists: true
}
const breaks = { exists: true, doesNotExist: true }
is<Example>(breaks)
// expected: false, received: true
While using this library I have run into an issue where any interface I would like to be validated cannot hold any properties that hold a Date
type. I have both "ignoreClasses": true
and "ignoreMethods": true
in my tsconfig.json
. This problem occurs at run time, as validations with Date
types fail every time, though it compiles just fine.
Glancing through the generated code, it seems a large list of checks is created for each Date
typed property, that checks that it has a number of properties, including properties that aren't always supported. I found the first place that fails is a check that a Date
has the getVarDate()
method, which is only supported in Internet Explorer and obsolete according to MDN. I have not checked the rest. I am using this on server application for cursory incoming type checking.
Right now my workaround is to have a separate interface without any Date
properties simply for use with validation and then checking for those properties manually. This would not work with the setting "disallowSuperfluousObjectProperties": true
in the tsconfig.json
.
When strictNullChecks
is set to false
, nullable properties are not allowed to be null. I suspect this is related to the TypeScript compiler transforming string | null
to string
in this mode. A potential fix could be allowing null
/undefined
for all types within typescript-is
if it detects strictNullChecks
are disabled?
import { createIs } from 'typescript-is'
interface Foo {
nullableString: string | null
}
const isFoo = createIs<Foo>()
const validFoo: Foo = {
nullableString: null,
}
// should be true, but returns false!
console.log(isFoo(validFoo))
Hi! This one is a big ask, but I want to know if it is in the works at all. Currently, this is what my webpack loaders setup looks like:
module: {
rules: [
{
test: /\.(ts|js)$/,
exclude: /node_modules/,
loader: 'babel-loader'
},
{
test: /\.runtime\.ts/,
exclude: /node_modules/,
use: [{ loader: 'ts-loader', options: { compiler: 'ttypescript' } }]
},
]
},
and my .babelrc
{
"presets": [
[
"@babel/env",
{
"targets": {
"node": "8.0"
}
}
],
"@babel/typescript"
],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
Before introducing typescript-is, I used the single babel-loader to transpile both typescript and javascript. Now, the code has to go through two different loaders, which slows the process significantly, since typechecking the entire project is essential for the loader to properly transform the code. It also adds more dependencies specifically for this library, and makes tsconfig.json non-standard.
A similar framework to the typescript transformer api in babel land is the babel-plugin-macros project. Importing a typescript-is.macro
would operate identically to the current import/transform.
Of course, this hinges on the hope that a babel macro and a typescript transformer could share some core code. If not, then maintaining two alongside each other is likely too much of an undertaking.
Recursive types do not compile, and throw a RangeError: Maximum call stack size exceeded
. Let me know if this is a problem with ttypescript
and I can report this bug upstream. Thank you for working on this library! 😄
stack trace:
$ npm run compile:runtime:config
/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript/lib/typescript.js:87527
throw e;
^
NestedError: Failed to transform node at: /Users/andrew/Code/scratchwork/scrape-pages/src/settings/config/assert.ts:9:55
at transformNodeAndChildren (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript-is/lib/transform-inline/transformer.js:30:15)
at ts.visitEachChild (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript-is/lib/transform-inline/transformer.js:32:62)
at visitNode (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript/lib/typescript.js:63887:23)
at Object.visitEachChild (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript/lib/typescript.js:64161:59)
at transformNodeAndChildren (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript-is/lib/transform-inline/transformer.js:32:15)
at ts.visitEachChild (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript-is/lib/transform-inline/transformer.js:32:62)
at visitNodes (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript/lib/typescript.js:63938:48)
at Object.visitEachChild (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript/lib/typescript.js:64157:45)
at transformNodeAndChildren (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript-is/lib/transform-inline/transformer.js:32:15)
at ts.visitEachChild (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript-is/lib/transform-inline/transformer.js:32:62)
Caused By: RangeError: Maximum call stack size exceeded
at Object.isNodeKind (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript/lib/typescript.js:13880:24)
at Object.createNode (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript/lib/typescript.js:16206:22)
at createSynthesizedNode (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript/lib/typescript.js:59514:23)
at Object.createTypeOf (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript/lib/typescript.js:60561:20)
at visitString (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript-is/lib/transform-inline/visitor.js:470:131)
at visitType (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript-is/lib/transform-inline/visitor.js:514:16)
at createPropertyCheck (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript-is/lib/transform-inline/visitor.js:12:24)
at visitPropertySignature (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript-is/lib/transform-inline/visitor.js:51:12)
at visitDeclaration (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript-is/lib/transform-inline/visitor.js:55:16)
at visitPropertySymbol (/Users/andrew/Code/scratchwork/scrape-pages/node_modules/typescript-is/lib/transform-inline/visitor.js:111:16)
simplified failing snippet:
import { assertType } from 'typescript-is'
type ConfigInit = {
folder: string
children: ConfigInit
}
export const assertConfigType = (configInit: any) => {
assertType<ConfigInit>(configInit)
}
tsconfig.json:
{
"compilerOptions": {
"rootDir": "../../../../src",
"outDir": "./",
"sourceMap": true,
"noImplicitAny": true,
"strictNullChecks": true,
"lib": ["es2015", "es2016", "es2017"],
"module": "commonjs",
"moduleResolution": "node",
"target": "es2017",
"allowJs": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"plugins": [
{
"transform": "typescript-is/lib/transform-inline/transformer"
}
]
},
"include": ["src/**/*.ts", "custom.d.ts"],
"files": ["../assert.ts", "../../../../custom.d.ts"]
}
Is there a way to is
or createIs
that errors on excess properties?
interface IPerson {
name: string;
}
const isPerson = createIs<IPerson>();
isPerson({name: "Arash", role: "Admin" }); // this should return false, but it returns true!
Use case: API server receives POST message with payload in the body, performs check to make sure payload conforms to expected schema, writes object to Cosmos DB (or other no-sql Document DB). Goal would be to 1. make sure payload has the properties that the interface expects, AND 2. payload does not have any other/extra properties not defined in the interface.
TypeScript itself errors on unexpected properties:
const person: IPerson = {name: "Arash", role: "Admin"}
// Type '{ name: string; role: string; }' is not assignable to type 'IPerson'.
// Object literal may only specify known properties, and 'role' does not exist in type 'IPerson'.ts(2322)
P.S. Big fan of typescript-is
:)
ttypescript version: 3.2.4
typescript-is version: 0.7.16
node version: 11.6.0
Steps to reproduce:
Try compiling a file containing the following:
import { is } from 'typescript-is';
class X {
method() {}
}
console.log(is<X>(new X()));
Actual results:
The compilation fails with an error like this:
typescript-is: transforming program with 23 source files; using TypeScript 3.2.4.
undefined:86995
throw e;
^
NestedError: Failed to transform node at: /home/geomaster/Projects/tests/typescript-is/index.ts:6:13
at transformNodeAndChildren (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transformer.js:27:15)
at ts.visitEachChild (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transformer.js:29:62)
at visitNodes (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript/lib/typescript.js:63441:48)
at Object.visitEachChild (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript/lib/typescript.js:63610:156)
at transformNodeAndChildren (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transformer.js:29:15)
at ts.visitEachChild (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transformer.js:29:62)
at visitNode (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript/lib/typescript.js:63390:23)
at Object.visitEachChild (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript/lib/typescript.js:63664:59)
at transformNodeAndChildren (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transformer.js:29:15)
at ts.visitEachChild (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transformer.js:29:62)
Caused By: Error: Unsupported declaration kind: 156
at visitDeclaration (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:52:15)
at visitPropertySymbol (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:77:25)
at visitRegularObjectType (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:131:29)
at visitObjectType (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:172:16)
at Object.visitType (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/visitor.js:366:16)
at createArrowFunction (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transform-node.js:9:21)
at Object.transformNode (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transform-node.js:58:35)
at transformNodeAndChildren (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transformer.js:22:44)
at ts.visitEachChild (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript-is/lib/transform-inline/transformer.js:29:62)
at visitNodes (/home/geomaster/Projects/tests/typescript-is/node_modules/typescript/lib/typescript.js:63441:48)
Expected results:
I know that class validation is explicitly not supported, which is completely fine. But this only breaks when the class has a method, which is a bit hard to track down and confusing. It might be more intuitive to display a normal compile error as soon as a class is tested.
This is not a high-priority issue, but might be good to put it on the backlog - I had quite some trouble trying to figure out what exactly is happening here :)
TypeGuardError forms a nice error message, but the data which failed validation is not part of this error. I.e. I can catch & print "TypeGuardError: validation failed at ...", but I can't print "TypeGuardError: validation failed at ..., input data was: ..." down the stack.
Is it possible to add a field to TypeGuardError which holds the actual data failed validation? This would be super useful for debugging.
For much faster development we use transpileOnly
option and do typechecking in different background thread. Unfortunately this library fails with this option (see #14).
It would be great to add an option to skip type checking in such a case. Then assertType
would never throw, is
would always return true
, etc.
We could achieve this by hacking typescript-is/index.js
:
function assertType(obj, getErrorMessage = () => null) {
...
function is(obj, getErrorMessage = () => null) {
...
function createIs(getErrorMessage = () => null) {
...
function createAssertType(getErrorMessage = () => null) {
...
I hope you understand what do I mean :-)
The length check for a Parameter list tuple is too strict in the case of optional parameters (or in fact any array where the final element(s) can validly be undefined).
Here's a case to reproduce the issue:
import * as assert from 'assert';
import { createIs } from '../index';
describe('is', () => {
function foo(_a: number, _b?: string) { return; }
type FooParams = Parameters<typeof foo>;
const isFooParams = createIs<FooParams>();
describe('isFooParams', () => {
it('should return true for valid parameters, optional or not', () => {
assert.deepStrictEqual(isFooParams([1, 'a']), true);
assert.deepStrictEqual(isFooParams([1]), true);
assert.deepStrictEqual(isFooParams([1, undefined]), true);
assert.deepStrictEqual(isFooParams([1, null]), false);
});
});
});
The second assert (incorrectly) fails: element [1] of the array is indeed undefined
.
In the generated output (below) there is an inappropriate test for Array.length===2.
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const assert = require("assert");
const index_1 = require("../index");
describe('is', () => {
function foo(_a, _b) { return; }
const isFooParams = index_1.createIs(object => {
var path = ["$"];
function _number(object) {
if (typeof object !== "number")
return "validation failed at " + path.join(".") + ": expected a number";
else
return null;
}
function _undefined(object) {
if (object !== undefined)
return "validation failed at " + path.join(".") + ": expected undefined";
else
return null;
}
function _string(object) {
if (typeof object !== "string")
return "validation failed at " + path.join(".") + ": expected a string";
else
return null;
}
function su__undefined__string_eu(object) {
var conditions = [_undefined, _string]; for (const condition of conditions) {
var error = condition(object);
if (!error)
return null;
} return "validation failed at " + path.join(".") + ": there are no valid alternatives";
}
function st__number_su__undefined__string_eu_et_9_396(object) {
/* HERE: array.length might be <= 2 since all the final elements after length=1 can validly be undefined */
if (!Array.isArray(object) || object.length !== 2)
return "validation failed at " + path.join(".") + ": expected an array of length 2"; {
path.push("[0]");
var error = _number(object[0]);
path.pop();
if (error)
return error;
} {
path.push("[1]");
var error = su__undefined__string_eu(object[1]);
path.pop();
if (error)
return error;
} return null;
} var error = st__number_su__undefined__string_eu_et_9_396(object); return error;
});
describe('isFooParams', () => {
it('should return true for valid parameters, optional or not', () => {
assert.deepStrictEqual(isFooParams([1, 'a']), true);
assert.deepStrictEqual(isFooParams([1]), true);
assert.deepStrictEqual(isFooParams([1, undefined]), true);
assert.deepStrictEqual(isFooParams([1, null]), false);
});
});
});
Output
Hi,
I have the following use case, and it seems your project does something pretty close, so was wondering if you might want to add it.
I've been working with the helpful CLI argument processing utility command-line-args
, but found it redundant to have to define what is basically a schema of definitions and then represent that same schema all over again in my jsdoc (or TypeScript) annotations.
I'd like to be able to define interfaces in TS or jsdoc, and then at runtime, pass a JSON form of these interfaces onto a utility function.
So in place of this (I don't know if your code works with jsdoc typedef's, so if not, just imagine the typedefs are interfaces or types instead):
/**
* @typedef {Object} MyCLIOptions
* @property {boolean} v verbose
* @property {string[]} src [default]
* @property {number} t timeout
*/
const optionDefinitions = [
{ name: 'verbose', alias: 'v', type: Boolean },
{ name: 'src', type: String, multiple: true, defaultOption: true },
{ name: 'timeout', alias: 't', type: Number }
]
I'd like to be able to do something like this (I know it might not build the exact same JSON as the above array (and as a generic tool it might not parse the [default]
text but could hopefully at least pass on the jsdoc names/descriptions), but if it could be parsed by a utility, that'd be enough):
import {typeAsJSON} from 'typescript-is';
/**
* @typedef {Object} MyCLIOptions
* @property {boolean} v verbose
* @property {string[]} src [default]
* @property {number} t timeout
*/
const optionDefinitions = typeAsJSON('MyCLIOptions');
// I think I prefer the line above for being valid JS (not even needing TS), but
// possibly these if necessary?
// const optionDefinitions = typeAsJSON(MyCLIOptions);
// Or this?
// const optionDefinitions : MyCLIOptions = typeAsJSON();
If this is beyond your project's scope, do you have any suggestions for a novice to TypeScript?
ttypescript version: 3.2.4
typescript-is version: 0.7.18
node version: 11.6.0
Steps to reproduce:
Try compiling a file containing the following:
import { is } from 'typescript-is';
type NumTuple = [number, number];
console.log(is<NumTuple>([0]));
console.log(is<NumTuple>([]));
Actual results:
Both statements print true
.
Expected results:
Both statements should print false
, as neither [0]
nor []
satisfy the NumTuple
type.
I feel like I'm submitting too many bug reports :) Let me know if I can help any further. Also, if there's a way to donate to the project, I'd be happy to - this is one of the most useful libraries I've used in a long time!
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.