Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add validate-value #508

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

RA80533
Copy link
Contributor

@RA80533 RA80533 commented Dec 28, 2020

@RA80533 RA80533 changed the title Add validate-value feat: add validate-value Dec 28, 2020
@moltar
Copy link
Owner

moltar commented Dec 28, 2020

@RA80533 wanna rebase that with all of the recent changes and try again?

@moltar
Copy link
Owner

moltar commented Dec 28, 2020

Seems to be the build step is failing. Is it working on your local?

npm run test:build

Does this pass?

@RA80533
Copy link
Contributor Author

RA80533 commented Dec 28, 2020

The import for expect-type should be removed.

import { expectTypeOf } from 'expect-type';

@moltar
Copy link
Owner

moltar commented Dec 28, 2020

The import for expect-type should be removed.

import { expectTypeOf } from 'expect-type';

Why? It's being used for a type test.

@RA80533
Copy link
Contributor Author

RA80533 commented Dec 28, 2020

Woah, I'm nuts. I thought I saw a commit that removed it as a dependency.

@RA80533
Copy link
Contributor Author

RA80533 commented Dec 28, 2020

The actual type test fails. It seems the test was written expecting it to evaluate at run-time but it actually evaluates the test at compile-time.

// %inferred-type: any
res;

res does not conform to the type of DATA at compile-time because it is of type any.

expectTypeOf(res).toMatchTypeOf(DATA);

@moltar
Copy link
Owner

moltar commented Dec 28, 2020

Yeah the type tests are compile time only, as there is no way to test types at runtime.

@moltar
Copy link
Owner

moltar commented Dec 28, 2020

Before your issue, I didn't have the build tests at all. It was just running jest. But I added the build step to GH Actions to catch type errors too. But this test was there before and it did actually fire via jest when there were issues.

@RA80533
Copy link
Contributor Author

RA80533 commented Dec 28, 2020

The error we're actually seeing is a result of the expectation failing due to the type mismatch. It's not very clear on its own but, when we inspect the function signature for the failed expectation itself, the argument MISMATCH_0: never provides that bit of information.

Screen Shot 2020-12-28 at 1 25 45 AM

@RA80533
Copy link
Contributor Author

RA80533 commented Dec 28, 2020

When I did this PR originally, I removed line 31 in order to get the tests to pass because of the failed expectation.

@RA80533
Copy link
Contributor Author

RA80533 commented Dec 28, 2020

protected readonly data: any;

Annotating data as type Data rather than any allows the expectation to pass.

@RA80533
Copy link
Contributor Author

RA80533 commented Dec 28, 2020

Jest already runs the TypeScript compiler through ts-jest so the extra workflow step of calling tsc --noEmit via test:build is redundant.

@moltar
Copy link
Owner

moltar commented Dec 28, 2020

Jest already runs the TypeScript compiler through ts-jest so the extra workflow step of calling tsc --noEmit via test:build is redundant.

Not redundant, because not all code is tested. As evident from your original issue - the benchmarking code was failing after an automatic upgrade.

protected readonly data: any;

Annotating data as type Data rather than any allows the expectation to pass.

But the point of having it as any is so that we can test if the type casting works correctly after validation.

input: any
output: Data


I'll take a look at your PR code shortly.

@moltar
Copy link
Owner

moltar commented Dec 28, 2020

Ok, so the problem is that isValid method is not type guarding at all. It just returns a boolean. The data is still any.

Per the docs of the lib, you want to use the isTypeOf fn.

When using TypeScript, you may even specify a generic type parameter, and use the function as a type guard. Also it is recommended to use the constant values exported by validate-value instead of the string literals as above, as they are the type-safe versions of these literals:

@RA80533
Copy link
Contributor Author

RA80533 commented Dec 28, 2020

Not redundant, because not all code is tested. As evident from your original issue - the benchmarking code was failing after an automatic upgrade.

After a bit of digging, I found out why it was passing prior to my original issue. The expected behavior of ts-jest is to both type-check files and run the tests (see here), I was perplexed as to why those specific errors weren't showing up in the logs.

Jest wasn't type-checking index.ts because it was not picked up by Jest (it is not in the Jest glob nor is it included by anything in the glob). Furthermore, it wasn't counted towards any coverage metrics because it had no exports.

Per the docs of the lib, you want to use the isTypeOf fn.

Good catch—using the function with an explicit type parameter settles things down. I didn't realize types were being narrowed for expectTypeOf().

validate() {
const { data } = this;

if (isOfType<Data>(data, dataType.schema)) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also don't want to type hint with Data, because that defeats the purpose of DRY. The idea is to define the validation once, and from that TS can infer the type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeScript cannot infer the type isOfType() should narrow data to for the same reason it must be narrowed: data is of type any. Unfortunately, validate-value provides no type relation between dataType.schema and any would-be valid objects conforming to the schema.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeScript cannot infer the type

Really? But the docs say that it is a type guard... The only purpose of the type guard is to infer type. So are the docs wrong then?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, yeah, ok, I verified it with their example and type guarding indeed does not work. In which case then this library does not fit the parameters of this repository.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See criteria: https://github.com/moltar/typescript-runtime-type-benchmarks#criteria

The idea is that we don't want to define the validation AND the type in two places. Types should be inferred from the schema. See all of the other test cases. In no other test case we use the Data type to hint what the type is. The validation or type guarding function infers the type automatically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really? But the docs say that it is a type guard... The only purpose of the type guard is to infer type. So are the docs wrong then?

It is a generic type guard where you must write the type you wish dataType.schema to represent. The library does not evaluate what the expected type is on its own because it does not compute what objects conforming to dataType.schema would look like. As a result, the only thing TypeScript understands when data passes through the type guard is that the type of data conforms to the schema dataType.schema.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a generic type guard

Fair.


But in that case it does not fit the purpose of this repository. So we should close this PR, or fix the library to do type guarding without generics hints.

@moltar
Copy link
Owner

moltar commented Dec 28, 2020

Jest wasn't type-checking index.ts because it was not picked up by Jest (it is not in the Jest glob nor is it included by anything in the glob). Furthermore, it wasn't counted towards any coverage metrics because it had no exports.

Yup, so that's why I added the build step as well. The index benchmark code isn't really testable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants