question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. ItĀ collects links to all the places you might be looking at while hunting down a tough bug.

And, if youā€™re still stuck at the end, weā€™re happy to hop on a call to see how we can help out.

Ensure forward-compatibility of callbacks

See original GitHub issue

Suggestion

šŸ” Search Terms

strict callback arguments, too many arguments warning, option

āœ… Viability Checklist

My suggestion meets these guidelines:

  • This wouldnā€™t be a breaking change in existing TypeScript/JavaScript code
  • This wouldnā€™t change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isnā€™t a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScriptā€™s Design Goals.

ā­ Suggestion

Edit: My original suggestion was unworkable. This is a second attempt.

const nextFrame = new Promise(requestAnimationFrame);

The above results in requestAnimationFrame being called with two arguments rather than the one it accepts. This is fragile, since, in future, a second argument may be added to requestAnimationFrame which makes the above code behave differently.

This problem is worse when it comes to browser-provided functions, as those change with new browser versions, and donā€™t rely on an app redeploy.

This can be worked around using never:

declare function requestAnimationFrame(callback: FrameRequestCallback, _: never): number;

ā€¦but it makes for a messy auto-complete, as the ā€˜neverā€™ arg shows up.

A solution would be some way to mark a function as ā€œcannot be assigned to a function type with more parametersā€. This would allow library authors to make this assertion if they want to reserve additional params for future use, and this assertion could be added to DOM functions like requestAnimationFrame.

šŸ’» Use Cases

I wrote a blog post about the risks with this pattern, including a section on how TypeScript doesnā€™t prevent it https://jakearchibald.com/2021/function-callback-risks/

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:48
  • Comments:15 (4 by maintainers)

github_iconTop GitHub Comments

3reactions
jakearchibaldcommented, Feb 1, 2021

I donā€™t think my proposed solution is right, as itā€™d cause TypeScript to reject things like this:

const doubler = (n: number) => n * 2;
const mappedArray = numberArray.map(doubler);
const nextFrame = new Promise(requestAnimationFrame);

Line 2 is fine, because doubler is designed to be a callback to array.map, whereas line 3 is bad because requestAnimationFrame isnā€™t designed to be a callback to new Promise. However, in both cases thereā€™s a function being called with an incorrect number of arguments.

Maybe this isnā€™t a typing problem, but Iā€™ll leave this open in case anyone thinks of a better solution.

Edit: Iā€™ve changed the solution in the OP.

2reactions
RyanCavanaughcommented, Feb 2, 2021

most folks consider this to be a backwards compatible change:

I donā€™t know what to say except that those people are, factually, wrong. If a library author wants to keep the door open for adding parameters in the future in a way that isnā€™t a breaking change, they need to throw today if the incoming arity is too high.

By way of analogy, most library authors donā€™t treat ā€œaddingā€ a return value (e.g. going from implicit undefined to number) to be a breaking change, but if tomorrow console.log starts returning the number of characters printed like Array#push does, then this will break code like foo(console.log(x) || x). TypeScript stops you from doing that because we think the console.log return value is incidentally undefined (i.e., void) rather than intentionally undefined, and we get bug reports about this because people think itā€™s safe to rely on that function always producing a falsy value, and I think on balance theyā€™re correct and we should change console.logā€™s return type to be undefined because thatā€™s whatā€™s in the spec today and, barring evidence to the contrary, the spec for any given function isnā€™t likely to change.

Ultimately the entire surface API of anything is subject to Hyrumā€™s law and Iā€™m struggling to draw a bright line where things ā€œcould plausibly changeā€ and ā€œcouldnā€™t plausibly changeā€ in a way that doesnā€™t require arguing about thousands of different functions in the DOM and core lib to represent this. Do we think Array#forEachā€™s callback could ever gain an additional arg? Why / why not? How do I apply that logic elsewhere?

Read more comments on GitHub >

github_iconTop Results From Across the Web

ray.tune.callback ā€” Ray 2.2.0 - the Ray documentation
Source code for ray.tune.callback ... ray.tune.stopper import Stopper class _CallbackMeta(ABCMeta): """A helper metaclass to ensure container classes (e.g.Ā ...
Read more >
How to ensure that a Node.js style callback it is called only once
I am looking for a utility that will ensure that only first call of the callback is executed. I know you can do...
Read more >
Ensure that callbacks registration and triggering don't cause ...
The solution, since I'm using the Qt event loop, was to schedule the callback to be triggered by a single-shot QTimer, rather than...
Read more >
Hls reference player. Getting Started. Segment. We know the ...
Ensure forward compatibility with potential beyond Rel-16 developments in 3GPP. This player downloads the video specified in the M3U8 The SPORTS REFERENCEĀ ...
Read more >
neo4j driver node js
For forward compatibility, I have also split out the auth details - this way we ... To use the CData ODBC Driver for...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found