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

Move to TypeScript #330

Merged
merged 2 commits into from
Apr 28, 2021
Merged

Move to TypeScript #330

merged 2 commits into from
Apr 28, 2021

Conversation

mgcrea
Copy link
Contributor

@mgcrea mgcrea commented Mar 5, 2021

Following #321, I took some time today to attempt a port to TypeScript with minimal changes.

Work is not fully done yet (did not update the CI pipeline, hence the failed tests), but it does pass typescript validation.

There is a two places where I had to @ts-expect-error, related to dynamically assigned props as I'm not sure yet how I can port it to Typescript (could be done easily by assigning each props without the loop, see reviews). Open to ideas on this.

Also there is some remaining typing issues (existing in the current code but invisible) regarding the actual options passed around (see InternalOptions vs NormalizedOptions) so I had to add a few manual as casting as well that should be fixed (but could be done after the migration to keep changes to a minimum).

Also I had to refactor the Ky class API a bit (added a static Ky.create) that did return a ResponsePromise from its constructor instead of the expected Ky instance as it looks like TypeScript forbids class constructor return type overloads.

I tried my best to follow what was made for got and to not change anything in the code yet (I have quite a few changes that I'd like to propose next but that were out of scope).

Before I spend too much more time on this I'd love to get some feedback on the direction of this merge request and the probability of it being merged once ready.

I'm willing to change anything that you would not like (naming, etc.).

Thanks for the review!

@mgcrea mgcrea force-pushed the refactor-ts branch 2 times, most recently from c85e8b5 to 2610fff Compare March 5, 2021 18:12
@sholladay
Copy link
Collaborator

Hi @mgcrea and thanks for your effort. I'll do a proper review once Sindre chimes in with whether he wants to move forward with this (given the comments on #321) and the tests are passing. I don't have strong feelings myself, except to say that - as you've encountered - some of the patterns we use in Ky don't seem to be fully supported by TypeScript and I dislike writing code differently than we would otherwise just to please the compiler. Especially when it comes to the public API, which seems to be the case here with the static Ky.create if I understand correctly.

That said, for Deno users like myself this would be awesome. The current approach to loading type definition files in Deno is kludgy and non-standard, so I'd love to remove the need for that.

@mgcrea
Copy link
Contributor Author

mgcrea commented Mar 15, 2021

@sholladay thanks! If I'm not mistaken, new Ky() was an internal API so I don't think there is any major issue here. I understand your point about having to please the compiler, but in most cases I find the advices pretty sound and that do lead to code both simpler understand and debug.

Waiting for @sindresorhus blessings to continue with unit tests.

@sholladay
Copy link
Collaborator

Correct, the Ky class is an internal concept. I thought that making create() a static method implied breaking the ability to call create() on an instance, but I can see in the code that you've kept that ability, so never mind on that.

@sindresorhus
Copy link
Owner

I'm ok with moving forward with this as the code is complicated and it's nice having some type safety when refactoring or adding features.

@sindresorhus
Copy link
Owner

sindresorhus commented Mar 16, 2021

What's the plan for testing?

We cannot move to ESM because AVA (test runner) doesn't yet support ESM with TS. - #321 (comment)

@mgcrea
Copy link
Contributor Author

mgcrea commented Mar 16, 2021

@sindresorhus Haven't really thought about it yet but I guess we could use a specific tsconfig build for tests (without ESM) along with @ava/typescript, and still output multiple independent builds depending on what we want to support (amd/cjs/esm)?

I'm usually using ts-jest in my projects where ts is a first-class citizen. So I could also easily port the testing setup to use jest but I guess you probably want to keep testing uniform among your projects and stick with ava.

Will try to have something working pretty soon.

@sindresorhus
Copy link
Owner

we could use a specific tsconfig build for tests (without ESM) along with @ava/typescript

I guess that might work.

and still output multiple independent builds depending on what we want to support (amd/cjs/esm)?

ESM is the only production target.

@mgcrea mgcrea force-pushed the refactor-ts branch 2 times, most recently from e4116ac to d3b96e8 Compare March 18, 2021 16:56
@mgcrea
Copy link
Contributor Author

mgcrea commented Mar 18, 2021

@sindresorhus I've just finished to fully migrate the tests to typescript. Was a bit painful to find the exact working setup to properly test the ESM build from non-ESM compiled unit-tests that nyc/ava can digest, but ended up finding something thanks to playwright debug mode.

I did bump all dev deps, so I had to fix a few linting-related issues in the code to have xo-related test pass, disabled a couple of eslint rules as well either related to typescript and to keep changes to a minimum.

Had to rewrite a bit the helpers part related to createHttpServer that was using an old non-ts module (took the helper directly from got) and had to add a couple of helpers to properly load the ESM build from chrome.

I went with named exports across the source code coupled with all-star-exports index.js files which is a setup I heavily use nowadays, however for an ESM-published lib, we might want to keep files to a minimum and explicitly import every dep. Can refactor that quickly if you want (or can be done later).

Also had to add .js extensions everywhere so that the esm build properly works (since typescript don't want to have anything with import-related transforms).

If you find any issue blocking the merge don't hesitate to ping me and I'll do my best to address it / fix it in a timely manner.

Another thing we should decide before publishing a release is the tsc compiler target, currently es2019 I guess would not work in some old browsers.

source/core/Ky.ts Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
.gitignore Outdated Show resolved Hide resolved
const isHeadersInstance = source2 instanceof globalThis.Headers;
const source = new globalThis.Headers(source2 as HeadersInit);

// eslint-disable-next-line unicorn/no-array-for-each
Copy link
Owner

Choose a reason for hiding this comment

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

Adhere to the linting.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately typescript won't let me use a for-of loop in that case, getting Type 'Headers' must have a '[Symbol.iterator]()' method that returns an iterator. and no entries() method declared.

edit: fixed by adding missing "DOM.Iterable" to tsconfig.json lib.

package.json Outdated
"unicorn/import-index": "off",
"import/extensions": "off"
"@typescript-eslint/dot-notation": "off",
"@typescript-eslint/quotes": "off",
Copy link
Owner

Choose a reason for hiding this comment

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

Don't disable this rule.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done for quotes, have to keep dot-notation off as typescript forbids dot notation in quite a few header-related cases (due to specific type) and disabling by line would be tedious/verbose.

package.json Outdated Show resolved Hide resolved
@sindresorhus
Copy link
Owner

Commented on some nitpick. Will do a more in-depth review tomorrow.

@sindresorhus
Copy link
Owner

Another thing we should decide before publishing a release is the tsc compiler target, currently es2019 I guess would not work in some old browsers.

es2019 is fine. It's what it currently is (Node.js 12). Ky only targets modern browsers.

@mgcrea mgcrea force-pushed the refactor-ts branch 2 times, most recently from 31982f2 to 47ac855 Compare March 19, 2021 10:26
@mgcrea
Copy link
Contributor Author

mgcrea commented Mar 19, 2021

Looking at the code today I realised that during my tests I had dropped the "type": "module" from the package.json, unfortunately adding it back does break testing (since test are built with require but require is forbidden when type is module). So I had to hack something around by monkey-patching a copied package.json with commonjs module inside the dist folder.

Hopefully nyc/ava adds support for ESM soon so we can simplify the build/test.

@sindresorhus
Copy link
Owner

Regarding testing, seems there's a solution with ts-node: https://github.com/avajs/ava/blob/main/docs/recipes/typescript.md#for-packages-with-type-module

@mgcrea
Copy link
Contributor Author

mgcrea commented Mar 25, 2021

Just finished implementing the solution you linked and it drastically simplified the testing process as it no longer requires building the tests (so no more dist/ folder) and no package.json monkey-patching needed!

One downside however is that we can no longer test against NodeJS v12, but since it's a browser lib I'm not sure there is much benefit testing against a matrix of NodeJS versions (ideally we should test against a matrix of browsers).

@mgcrea mgcrea force-pushed the refactor-ts branch 2 times, most recently from 562e941 to 5020183 Compare March 25, 2021 10:04
@sholladay
Copy link
Collaborator

One downside however is that we can no longer test against NodeJS v12

We do support Node when combined with polyfills, but I'm fine with dropping Node 12 from the test matrix.

ideally we should test against a matrix of browsers

That's what test/browser.js does. It uses Playwright to run some tests in a headless browser. It's currently set up to test against Chromium, but we could configure it to test Webkit and Firefox as well.

@sindresorhus
Copy link
Owner

but I'm fine with dropping Node 12 from the test matrix.

👍

package.json Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
source/index.ts Outdated Show resolved Hide resolved
tsconfig.json Outdated Show resolved Hide resolved
@mgcrea
Copy link
Contributor Author

mgcrea commented Apr 6, 2021

@sindresorhus I've dropped the node@12 from the GitHub action, is there anything else I can do to move this pr forward?

package.json Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
package.json Show resolved Hide resolved
tsconfig.json Show resolved Hide resolved
tsconfig.lib.json Outdated Show resolved Hide resolved
@mgcrea mgcrea changed the title WIP: refactor(project): migrate to typescript refactor(project): migrate to typescript Apr 9, 2021
.gitignore Outdated Show resolved Hide resolved
source/core/Ky.ts Show resolved Hide resolved
source/core/Ky.ts Outdated Show resolved Hide resolved
source/core/Ky.ts Outdated Show resolved Hide resolved
source/core/Ky.ts Outdated Show resolved Hide resolved
tsconfig.lib.json Outdated Show resolved Hide resolved
@sindresorhus
Copy link
Owner

@mgcrea Don't force push. It makes it impossible to review only what changed.

@sindresorhus
Copy link
Owner

@mgcrea Did you see @sholladay's feedback?

@mgcrea
Copy link
Contributor Author

mgcrea commented Apr 20, 2021

Yes and I think I've answered every comment, to me there is nothing really blocking left (but I could be wrong). Point taken regarding force-pushing, won't do it again.

@sholladay
Copy link
Collaborator

I don't see those responses to my comments, @mgcrea.

source/core/Ky.ts Show resolved Hide resolved
source/index.ts Show resolved Hide resolved
source/core/Ky.ts Outdated Show resolved Hide resolved
const isHeadersInstance = source2 instanceof globalThis.Headers;
const source = new globalThis.Headers(source2 as HeadersInit);

// eslint-disable-next-line unicorn/no-array-for-each
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately typescript won't let me use a for-of loop in that case, getting Type 'Headers' must have a '[Symbol.iterator]()' method that returns an iterator. and no entries() method declared.

edit: fixed by adding missing "DOM.Iterable" to tsconfig.json lib.

package.json Outdated Show resolved Hide resolved
package.json Show resolved Hide resolved
package.json Outdated Show resolved Hide resolved
tsconfig.lib.json Outdated Show resolved Hide resolved
tsconfig.json Show resolved Hide resolved
source/core/Ky.ts Show resolved Hide resolved
@mgcrea
Copy link
Contributor Author

mgcrea commented Apr 21, 2021

Indeed it looks like I did not properly understand how the reviewing process works, all my comments were flagged as pending and I thought you could see them. I had to « submit review » to properly publish them...! 😅 Good to know. I also marked every conversation as resolved as an earlier attempt to publish so feel free to unresolve the points you want to have addressed / discussed.

@sindresorhus
Copy link
Owner

For the remaining @ts-expect-error. I suggest we just leave it and open an issue about fixing @ts-expect-error. Maybe someone else knows how to correctly type those.

We can link to https://github.com/sindresorhus/ky/search?q=%22@ts-expect-error%22

@sindresorhus sindresorhus changed the title refactor(project): migrate to typescript Move to TypeScript Apr 28, 2021
@sindresorhus
Copy link
Owner

sindresorhus commented Apr 28, 2021

@mgcrea Would you be able to fix the failing tests?

@mgcrea
Copy link
Contributor Author

mgcrea commented Apr 28, 2021

@sindresorhus forgot to push a commit fixing the broken test due to the updated dist path, should be ok now. Looks like the workflow is stuck (new security feature of GitHub) pending your approval.

@sindresorhus sindresorhus merged commit 2d8d68a into sindresorhus:main Apr 28, 2021
@sindresorhus
Copy link
Owner

@mgcrea Thanks for working on this 👍

@mgcrea
Copy link
Contributor Author

mgcrea commented Apr 28, 2021

Thanks for merging such a big PR! Looking forward to contribute with smaller patches in the future!

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

3 participants