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
fix: improve TypeScript types #841
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall LGTM 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update: everything works correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check if isRedirect() is useable in this way.
The previous review contained some invalid code, I can confirm it works correctly 😆
@types/index.d.ts
Outdated
export default fetch; | ||
export function isRedirect(code: number): boolean; | ||
|
||
export default function fetch(url: RequestInfo, init?: RequestInit): Promise<Response>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the main
field will point to index.cjs
, and the types
field (which is the corresponding field for the typings) will point to this file, I think that the correct thing here would be to use a CommonJS export:
export default function fetch(url: RequestInfo, init?: RequestInit): Promise<Response>; | |
declare function fetch(url: RequestInfo, init?: RequestInit): Promise<Response>; | |
export = fetch; |
You can read some more info it here: https://github.com/DefinitelyTyped/DefinitelyTyped#a-package-uses-export--but-i-prefer-to-use-default-imports-can-i-change-export--to-export-default
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you look at the built index.cjs
, the resulting exports
is the following:
exports.default = fetch;
So the good way to type it is to use export default
.
export = fetch
is equivalent to module.exports = fetch
which is not the case here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ahh, cool, I was looking at the currently published package which uses module.exports = fetch
, my bad!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SomaticIT I just cloned the repo and made a clean build, and the output in the bundle is module.exports = fetch
, thus I think that we should type this as export = fetch
Thanks all reviewers for your time! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My major concern with current typings is the export description. I understand that export default fetch
matches with our code, etc.
However, it lacks tooling support (read VSCode IntelliSense) while the module is used as CommonJS (try with test/commonjs/test-artifact.js
:
export = fetch
while looks weird and requires synthetic default import solves this problem and so, results in better DX. Should we make that change?
I strongly disagree on this. A d.ts library should never require an option in tsc |
Important note: export type HeadersInit = Headers | Record<string, string> | Iterable<readonly [string, string]> | Iterable<string>[]; Indeed, the evaluation function looks at length in iterable items (line 79): if (pair.length !== 2) {
throw new TypeError('Each header pair must be a name/value tuple');
} It means that we have to replace export type HeadersInit = Headers | Record<string, string> | Iterable<readonly [string, string]> | Iterable<string[]>; It also performs better TypeScript check: // Works with Iterable<string>[] but report a type error with Iterable<string[]>
const headers = new Headers(['name', 'value]); |
Just double checked how the module is exported from the built file, and it is exported with the following code: exports = module.exports = fetch; Thus the correct typing for this module would be: export = fetch; ref: #841 (comment)
var node_fetch_1 = require("node-fetch");
node_fetch_1["default"]('test'); without
with
|
Please check the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still disagree that we need to export types for internal data structures of this library even due to reason that somebody else is building TypeScript library on top of node-fetch
and will need to expose original types:
- It makes any changes to these internal data structures a breaking change for the whole library
- These exports wouldn't be available if this library was written in TypeScript originally.
- There is a built-in ways in TypeScript to extract types of parameters and return type and constructor parameters
@tinovyatkin Slightly unrelated to this PR, but what would you rename the |
Argh, I have been looking way too long but cannot find it now. I know that I saw a comment from someone on the TypeScript team the other day explaining how to correctly type a library that exports |
I'm not a native English speaking person, so, probably not the best source of such type of suggestions, but what this test actually tests is that |
@LinusU Do you mean this comment? microsoft/TypeScript#2242 (comment) |
Hmmm, no unfortunately not, this was a more recent comment. And it had some text that was basically "If your library is really exporting both CommonJS and a default name, you can do this in your typings:"... |
@LinusU Maybe this one: |
@SomaticIT not that specific comment, but it seems to be the same trick 😍 awesome find!! 👏 With that I have managed to test this out locally and I think that this is the best approach: type Fetch = (url: RequestInfo, init?: RequestInit) => Promise<Response>
declare const fetch: Fetch & { default: Fetch }
export = fetch
This is amazing, I think that there is a lot of libraries that are missing this in their types, where the library is actually exporting both |
Dear reviewers, I think I found a very good solution that allows to do declare function fetch(url: RequestInfo, init?: RequestInit): Promise<Response>; // function is mergeable
declare class fetch {
static default: typeof fetch; // default needs to be a value to use import fetch from...
}
declare namespace fetch { // namespace allows import * and exporting types
// export types
}
export = fetch; This solutions allows any ways of importing in TypeScript and creates compatible transpilation. Eg: import fetch, {Request} from "node-fetch";
asserts.equals(fetch.Request, Request);
fetch.isRedirect = (code: number) => true;
import * as fetch from "node-fetch";
asserts.equals(fetch.default.Request, fetch.Request);
fetch.isRedirect = (code: number) => true;
import fetch from require("node-fetch");
asserts.equals(fetch.default.Request, fetch.Request);
fetch.isRedirect = (code: number) => true; With this model, I think that we match perfectly the What do you think? |
You're right, I fixed it in my last commit with But it's the real type for the implementation. |
What is the purpose of this pull request?
What changes did you make? (provide an overview)
class
instead ofinterface
+var
where it's possibleAbortSignal
to avoid the need to import 'abort-controller' moduleWhich issue (if any) does this pull request address?
RequestInit
orResponseInit
object correctly:abort-controller
dependencyv3.0.0-beta.5
typesIs there anything you'd like reviewers to know?
isRedirect()
is useable in this way.