unexpectedjs / unexpected

Unexpected - the extensible BDD assertion toolkit
http://unexpected.js.org/
MIT License
370 stars 29 forks source link

Typescript typings #525

Open studds opened 6 years ago

studds commented 6 years ago

Would you be open to a PR with some basic typescript typings, so that this can be used in typescript projects? They might not cover the full breadth and depth of the API, but would cover the most frequent use cases.

kasperisager commented 6 years ago

I'm currently working on TypeScript typings for Unexpected with a goal to cover as much of the API as possible. Since I need the typings for plugin development I expect to use the majority of the APIs available.

On a related note, might @papandreou or @sunesimonsen have an opinion on where these typings should live? They're currently internal to the project I'm working on but I'd love to make them more easily available. I know some people swear to DefinitelyTyped, which is a great resource for typings and all, though I do personally prefer as a consumer of a library not to have to install additional packages for things to Just Work™.

papandreou commented 6 years ago

@kasperisager, that sounds really cool! If we can find a way to automatically generate the typings from the assertions that are defined and their types, I would be open to have the typings live in Unexpected itself. I remember @bruderstein talked about that idea earlier and seemed quite confident that it could be done.

If they have to be manually maintained, I think it would be better to keep them separate for the time being, as none of the maintainers use TypeScript, AFAIK.

kasperisager commented 6 years ago

That can absolutely be done, though the core typings (expect(), use(), clone(), addAssertion(), etc.) would still have to be maintained somewhere rather than generated. Currently, expect() is only minimally typed:

function expect<A extends Array<unknown> = []>(subject: unknown, assertionName: string, ...args: A): expect.Unexpected;

What could be done then is generate overloads based on assertion signatures, e.g. something like this...

expect.addAssertion<Person, [number]>(
  "<Person> [not] to be older than <number>", 
  (expect, subject, age) {
    // ...
  }
);

...would generate the following overload for expect():

function expect(subject: Person, assertionName: "to be older than" | "not to be older than", age: number): expect.Unexpected;
sunesimonsen commented 6 years ago

I agree with @papandreou if it is not almost hands off then if should probably live somewhere else. The very dynamic nature of unexpected means that it will never be fully covered. But you might create something useful anyway. I don't use TypeScript, so I'm personally not super interested in maintaining it. But I can imagine an external project that takes the newest version of unexpected installs the most common plugins inspects the types and handlers on the unexpected instance and generates the type definitions.

studds commented 6 years ago

fwiw, the official recommendation from the typescript team in this case would be to publish the types to the @types organisation on npm, which is done by submitting a PR to https://github.com/DefinitelyTyped/DefinitelyTyped.

The types could still be generated, of course. Has anyone made a start on this? @kasperisager are your types currently available anywhere publicly?

kasperisager commented 5 years ago

The typings I have so far can be found here: https://github.com/Siteimprove/alfa/blob/master/packages/alfa-unexpected/types/unexpected.d.ts

alexjeffburke commented 5 years ago

This is really interesting - as it happens I recently did an experiment for the other part of this, which is to actually get auto-completion when writing assertions in test call. I'll see if I can get them cleaned up and arrive at a definition for all the assertions in core.

studds commented 5 years ago

I've fleshed out the types we're using internally in preparation for opening a PR on DefinitelyTyped. I've also tried to combine with what you've got, @kasperisager. We're not doing any plugin development, though, so I'm not sure if this will work for that - would you mind having a look and letting me know?

https://github.com/studds/DefinitelyTyped/blob/master/types/unexpected/index.d.ts

kasperisager commented 5 years ago

I've pulled in your typings to see if our packages still type, and it looks like the only thing missing is either a general overload for the expect() function (similar to https://github.com/unexpectedjs/unexpected/issues/525#issuecomment-436966420) or the addition of optional conditions such as "[not] to be" which are used when defining new assertions: http://unexpected.js.org/api/addAssertion/

kasperisager commented 5 years ago

The general overload would actually be required regardless as there would otherwise be no way to invoke custom assertions. One of the issues I ran in to when implementing the typings used in our project was also that I couldn't find a way to allow plugins to define additional overloads for the expect() function, effectively making custom assertions only minimally typed. Unexpected itself will catch issues arising from that at runtime though so it was an acceptable trade-off.

studds commented 5 years ago

Ahhh, yes, that is a problem. I can't figure out how to get around that with plain types.

Can I propose a different approach, in that case: wrapping unexpected and re-exporting it in a way that makes it more typescript friendly: https://github.com/studds/unexpected-ts#readme publish as https://www.npmjs.com/package/unexpected-ts

kasperisager commented 5 years ago

Your approach made me realise how it can be done with just a type declaration 👌 Voilá: https://github.com/Siteimprove/alfa/commit/f0fa281f3993706bbf67daedb147632594f09313

studds commented 5 years ago

That's awesome, @kasperisager!

msiebuhr commented 5 years ago

I ended up going @kasperisager 's typings and works for me (with a few tweaks).

IMHO, I don't think typing up all the different assertions is a terribly good idea. We add quite a few assertions of our own, and AFAIK, we wouldn't be able to add those to TSC run-time, making strict checking a no-go. The only benefit left is editor-typeahead (which I don't use much).

How about we get a MVP typings thing shipped and take it from there with adding more fancy stuff?

kasperisager commented 5 years ago

IMHO, I don't think typing up all the different assertions is a terribly good idea. We add quite a few assertions of our own, and AFAIK, we wouldn't be able to add those to TSC run-time, making strict checking a no-go. The only benefit left is editor-typeahead (which I don't use much).

On the contrary, the typings I've written allow both for strongly typed core assertions in addition to weakly typed custom assertions that can then be made strongly typed by extending the Expect interface from outside as seen here: https://github.com/Siteimprove/alfa/blob/f0fa281f3993706bbf67daedb147632594f09313/packages/alfa-unexpected/src/create-unexpected-plugin.ts#L10-L17

shicks commented 2 years ago

Presumably one could take advantage of the (relatively) new template literal types to generate many possible assertions with only a few lines of declarations.

I'd love to be able to use this library, but without IDE support/completion of the assertion strings, it's a lot less appealing.