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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

"print" types to compare them + better error messages #27

Closed
wants to merge 40 commits into from
Closed

Conversation

mmkal
Copy link
Owner

@mmkal mmkal commented Apr 11, 2023

This (draft, initially) pull request attempts to get the best of both worlds from #21 and #16

Fixes #17
Fixes #26
Fixes #5

What this does:

  1. Introduces toBeIdenticalTo to replace toEqualTypeOf, and toExtend to replace toMatchTypeOf. The differences:
  2. The names are hopefully clearer. "Equal" isn't a thing in typescript, and "identical" implies that it's some kind of special sauce from this library to say the two types are really, really identical. No exceptions for contra-/co-variance or anything. "Extend" is a thing in typescript and that's all toExtend checks for
  3. The new methods don't allow passing a value. You can just typeof in the generic typearg if you want it, but it was making it impossible (or at least very hard) to consistently have readable CLI error messages
  4. Adds an errors.test.ts (based on Improve CLI error messages聽#16) to explicitly track exactly what it looks like when assertions fail (since that's really the point of this library, more than the "happy path" that usage.test.ts covers.
  5. Improves the error messages by mapping the "expected" type to a similar-ish equivalent on failure. The similar-ish equivalent replaces "leaf" types with a string literal representation (e.g. string -> 'string', void -> 'void', 'foo' -> string: foo), and then puts them into a kind of assertion message like Expected: string, Actual: number. So the CLI error message (or the one in the IDE/on hover) will be something like:
test/test.ts:9:99 - error TS2344: Type '{ a: string; }' does not satisfy the constraint '{ a: \\"Expected: string, Actual: number\\"; }'.

Types of property 'a' are incompatible.
   Type 'string' is not assignable to type '\\"Expected: string, Actual: number\\"'.

9 expectTypeOf({a: 1}).toBeIdenticalTo<{a: string}>()
  1. Adds view and props helpers for debugging, to make it easy to get a copy-pasteable resolved type:

image

7. Adds support for "augmented" functions like `(() => number) & {foo: string}`

CC @trevorade @papb

In case either of you would be interested in reviewing, src/index.ts has the implementation, but print-type.test.ts is a useful way to see what the quite-complicated PrintProps actually does. errors.test.ts shows roughly what error messages would look like (at time of writing, some work still to do for the various toBeString() type methods still.

@papb this uses some of the naming you've been suggesting over the past couple of years (if I remember right!) #10. It also fixes the bug you found #5 caused by microsoft/TypeScript#50670

@trevorade I know some of the issues you've been seeking to fix are around perf. Do you have a good way to test if this is ok performance-wise? In terms of functionality it's where I'd like it to be - it's basically an alternative rewrite of Equals that doesn't require .simplified/works as expected for {a: 1} & {b: 1} vs {a: 1; b: 1}. As far as I've found, the only limitation it has is that it has to bail out of recursive types like inferface X { x: X } (no fancy loop checking - after it gets 20 props deep it just says 馃憢 ). The compiler hits "type instantiation is excessively deep and potentially infinite" if you set to 25 instead of 20 - but interestingly, without bailing out at all, VSCode just stalls.

Some potential follow-ons:

  • Make toBeIdenticalTo configurable, so you can do stuff like expectTypeOf<{a: readonly string}>().ignoringReadonly.toBeIdenticalTo<{a: string}>() or something
  • Add expectTypeOf<MyLogger>().toBeExtendedBy<typeof console>() to make sure your interfaces are compatible with existing types. You could always do expectTypeOf<typeof console>().toExtend<MyLogger>() but that failing implies there's something wrong with console rather than MyLogger. @papb you also requested something like this at some point, right?
  • Add expectTypeOf<MyThing>().notToHaveAnys() and expectTypeOf<MyThing>().notToHaveNevers() which would make sure that there are no any or never properties, even deeply nested ones - would be quite easy to implement now, and the error could probably tell you exactly where the offending any/never has crept in.

Problems/this might not work because:

  • PrintProps<MouseEvent> hits "type instantiation is excessively deep and possibly infinite".

@mmkal
Copy link
Owner Author

mmkal commented Apr 13, 2023

Ok, I think this makes performance significantly worse, to the extent that a bunch of existing tests will now fail because of type instantiation is excessively deep and possibly infinite - my IDE was just struggling to run the typescript server(!) so showed up no errors at first. So this almost certainly isn't the way to go.

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