Add types for gensync#60125
Conversation
|
@jakebailey Thank you for submitting this PR! This is a live comment which I will keep updated. 1 package in this PR
Code ReviewsThis PR adds a new definition, so it needs to be reviewed by a DT maintainer before it can be merged. You can test the changes of this PR in the Playground. Status
Once every item on this list is checked, I'll ask you for permission to merge and publish the changes. Diagnostic Information: What the bot saw about this PR{
"type": "info",
"now": "-",
"pr_number": 60125,
"author": "jakebailey",
"headCommitOid": "cbd3f0c15f53a12ce0728e39898668c6024642e5",
"mergeBaseOid": "922d794d523ab3926fa6a65466e73bbf8b3b8598",
"lastPushDate": "2022-07-06T17:14:23.000Z",
"lastActivityDate": "2022-07-08T17:20:43.000Z",
"hasMergeConflict": false,
"isFirstContribution": false,
"tooManyFiles": false,
"hugeChange": false,
"popularityLevel": "Well-liked by everyone",
"pkgInfo": [
{
"name": "gensync",
"kind": "add",
"files": [
{
"path": "types/gensync/gensync-tests.ts",
"kind": "test"
},
{
"path": "types/gensync/index.d.ts",
"kind": "definition"
},
{
"path": "types/gensync/tsconfig.json",
"kind": "package-meta-ok"
},
{
"path": "types/gensync/tslint.json",
"kind": "package-meta",
"suspect": "not [the expected form](https://github.com/DefinitelyTyped/DefinitelyTyped#user-content-linter-tslintjson) (check: `rules`)"
}
],
"owners": [],
"addedOwners": [
"jakebailey",
"nicolo-ribaudo"
],
"deletedOwners": [],
"popularityLevel": "Well-liked by everyone"
}
],
"reviews": [
{
"type": "stale",
"reviewer": "peterblazejewicz",
"date": "2022-05-01T19:55:23.000Z",
"abbrOid": "230423e"
}
],
"mainBotCommentID": 1113604040,
"ciResult": "pass"
} |
|
🔔 @jakebailey — you're the only owner, but it would still be good if you find someone to review this PR in the next few days, otherwise a maintainer will look at it. (And if you do find someone, maybe even recruit them to be a second owner to make future changes easier...) |
|
@zxbodya @loganfsmyth @nicolo-ribaudo maybe have opinions (sorry for the ping); I did this out of interest for the library after I saw it mentioned on one of prettier's GitHub issues, so I have no major attachment other than thinking this library was compelling and was surprised to find it not on DT. |
| { | ||
| "extends": "@definitelytyped/dtslint/dt.json", | ||
| "rules": { | ||
| "space-before-function-paren": false |
There was a problem hiding this comment.
is this one required? It can be a prettier nitpit setting, that can be disabled via prettier inline comment
There was a problem hiding this comment.
Required, no, but every single use of a generator (so, every single test) would need to have a prettier comment.
There was a problem hiding this comment.
I'm somewhat inclined to go figure out if there's something in dtslint that can be changed to not complain about this particular code. But, then again, I would really like DT to enforce prettier-d code in the first place, so what can I do? 😄
There was a problem hiding this comment.
oh, really sorry to miss a point here. I"m pretty sure Prettier always space this:
prettier/prettier#3845 (comment)
There is no way to add a configuration and I'd avoid always adding custom exclusions to keep things simple. I'd modify default global settings used here via inclusion:
https://github.com/microsoft/DefinitelyTyped-tools/blob/master/packages/dtslint/dt.json
and add this exclusion, as it cannot be (and won't be) configured via Prettier config
There was a problem hiding this comment.
All good. Prettier wants function* () {}, dtslint (apparently) wants function*(). Will look to see what I can do about that.
There was a problem hiding this comment.
I'd really opt to add this change upstream, I had the same problem couple of times here. This will eventually incrementally increase the toil on developers, if not corrected.
| * - `.errback(...args, (err, result) => {})` - Calls the callback with the computed value, or error. | ||
| * @param generatorFnOrOptions A generator function, or options for an existing sync/async function | ||
| */ | ||
| declare function gensync<A extends unknown[], R, E = unknown>( |
There was a problem hiding this comment.
I defaulted E to unknown everywhere; I'm not sure if this is good or if any would be a better choice. Hard to claw back from any once it's in the types, though, so I'm being strict.
|
@peterblazejewicz Thank you for reviewing this PR! The author has pushed new commits since your last review. Could you take another look and submit a fresh review? |
|
Thanks for the ping! I'll test these types in our codebase, and check if there is any problem.
Almost 100% of those weekly downloads come from |
I was mainly worried about people installing Let me know how they go; the type variables are different, so it won't be a simple swap; I was going to test this myself, but I had already been nerd sniped by typing this library enough and needed to go back to work. 😅 |
|
I'm testing these definitions in Babel's source. Our approach of passing a Also, could you verify if this code type-checks properly with your type definitions? I'm getting an error about const isAsync = gensync<[], boolean>({
sync: () => false,
errback: cb => cb(null, true),
});
gensync_dt are your types: |
|
I tried debugging the type Callback<R, E = unknown> = R extends void ? (err: E) => void : (err: E, result: R) => void;
declare var a: Callback<boolean>;
a(null, true);However, I don't know how to fix it. EDIT: Rewriting type Callback<R, E = unknown> = (
...args: R extends void ? [err: E] : [err: E, result: R]
) => void;EDIT 2: Probably it should use type Callback<R, E = unknown> = (
...args: R extends void ? [err: E | null] : [err: E | null, result: R]
) => void; |
The main reason I wrote it this way is that it's generally preferred to not put all of the constraints into type variables and then have to pick the info back out again with utility types. I would expect that nearly all of the time, these functions are correctly typed via inference (so the type var detail doesn't matter so much); do you see it another way?
That's weird; swapping it out for that tuple-based definition of
RE: Confusingly, I'll have a chance to look at this tomorrow. You (I think) also had a code snippet like this in one of your comments: const runGenerator = gensync<[item: Generator], any>(function* <Return>(item: Generator<unknown, Return>) {
return yield* item;
});What happened to that? Did you find a workaround? The other goal I had with the types was for the generators to be "closed", in that you shouldn't yield from generators not produced by gensync, under the assumption that doing so would be unsafe because the Symbols gensync uses as sentinel values are not exposed and therefore not possible to emulate. Did you have a working code sample that yielded an arbitrary generator and it worked? |
|
Actually, while scrolling through the tweets to find the explanation for the type var thing, I found the answer for the conditional type thing (probably; combined with the whole |
|
Fixed easily enough via the fix on microsoft/TypeScript#37279 (comment). |
|
The
Ok, that makes sense. For some reason we explicitly pass all the type parameters, I'll check. |
|
I can absolutely see you writing the types explicitly to encode the API, though I think babel's current types don't allow for encoding of the errback error type (very possible nobody actually consumes those when there are sync and async versions available). I think I could equivalently make the first type parameter be the sync function type, and keep a defaulted I don't have massively strong feelings either besides my general feeling towards keeping type params simple. I'm already sort of drive-by causing breaks for you as it is! |
|
What do folks want to do with this PR? |
|
I'd still like feedback from @nicolo-ribaudo whenever he gets a chance. I could just convert this into a draft to get this out of the maintainer queue for now. I can imagine switching to something like this, if my type var differences are too problematic: declare function gensync<Fn extends (...: any[]) => any, E = unknown>(...)Which will be compatible with babel's existing types (and then also provide an error type), but |
|
@nicolo-ribaudo I've added you as an owner; I can't seem to get the inference to work, so if you're happy with these written less-functionally, then I think they are where they need to be. Were you able to see how this works in babel, if inference of functions works rather than explicitly specifying the signatures everywhere? |
|
Re-ping @«anyone?»: This PR has been out for over a week, yet I haven't seen any reviews. Could someone please give it some attention? Thanks! |
|
@jakebailey 🤷♂️merge? |
|
Whops sorry, I'm testing these right now in the Babel repo |
|
@jakebailey It looks like inference never works for the return type: import gensync, { type Handler } from "gensync";
const transform = gensync(function*(param: string): Handler<number> {
return 2;
})
// Error: Type 'unknown' is not assignable to type 'number'
const num: number = transform.sync("foo"); |
|
Oh well, I don't know why but it's working now for me. |
|
It's working well! babel/babel#14710 |
|
I'm out of office, but I'll come back to this when I am back next week. |
|
It has been more than two weeks and this PR still has no reviews. I'll bump it to the DT maintainer queue. Thank you for your patience, @jakebailey. (Ping @«anyone?».) |
|
I'll export the type, but can you provide an example of what you were trying to do? I'm curious if there's a better way for these types to work if the conditional is causing problems. |
|
Ah, I saw those, I was moreso trying to understand
|
|
I think I was trying to define my But the reason it is good to expose the |
|
The PR has If everything looks solid with this PR, I can click the button. |
|
Looks good to me! |
tslint.jsonwas modified to addspace-before-function-parenbecause the linter is not particularly happy withfunction*syntax, or at least, is incompatible with our prettier config's choice of spacing. AFAIK it's supposed to not complain and instead rely ongenerator-star-spacing, but maybe that's eslint not tslint.It turns out that
@babel/corewrote ad.tsfile for this library (or at least, copied the flow types ingensyncand modified them), independently of me (which I did not know until I had finished), but didn't send it here; I personally think that the types in this PR are clearer, catch more errors, and do a better job withallandrace, but they're not compatible with babel's declaration because the type variables are different. Maybe this PR is a bad idea and we should just adopt theirs (or at least one with compatible type vars + better typing for the differences).I would treat this as a popular/critical package (even though the labels don't indicate it),
gensyncgets some 21 million weekly downloads.npm test <package to test>.If adding a new definition:
.d.tsfiles generated via--declarationdts-gen --dt, not by basing it on an existing project.tslint.jsonshould contain{ "extends": "@definitelytyped/dtslint/dt.json" }, and no additional rules.tsconfig.jsonshould havenoImplicitAny,noImplicitThis,strictNullChecks, andstrictFunctionTypesset totrue.