Skip to content

TypeScript guidelines: Document any requirement for callback parameters subject to bidirectional assignment#170

Open
MajorLift wants to merge 5 commits intomainfrom
jongsun/typescript/callback-any-contravariance
Open

TypeScript guidelines: Document any requirement for callback parameters subject to bidirectional assignment#170
MajorLift wants to merge 5 commits intomainfrom
jongsun/typescript/callback-any-contravariance

Conversation

@MajorLift
Copy link
Copy Markdown
Contributor

@MajorLift MajorLift commented Apr 14, 2026

View rendered version


Note

Low Risk
Low risk documentation-only change; no runtime code or APIs are modified.

Overview
Adds a new TypeScript guideline section explaining when callback parameter types must use any under --strictFunctionTypes due to bidirectional assignment constraints.

Includes a concrete Wide/Narrow example comparing unknown, never, and any, plus a note and real-world references clarifying that this any usage is intentionally scoped to the parameter position and not meant to be broadly propagated.

Reviewed by Cursor Bugbot for commit 5f5cc8f. Bugbot is set up for automated code reviews on this repo. Configure here.

@MajorLift MajorLift force-pushed the jongsun/typescript/callback-any-contravariance branch from f5a6e91 to 77548ad Compare April 14, 2026 21:22
Comment thread docs/typescript.md Outdated
@github-project-automation github-project-automation Bot moved this to Needs dev review in PR review queue Apr 20, 2026
@MajorLift MajorLift force-pushed the jongsun/typescript/callback-any-contravariance branch from 1d09fbf to 5f5cc8f Compare April 26, 2026 17:38
Comment thread docs/typescript.md
f3 = givesNarrow; // ✓
```

> **Note:** The `eslint-disable` is intentional. `any` here is _not_ infectious: it is scoped to a single callback's parameter position and does not propagate to callers. The enclosing external APIs re-impose their own parameter types at each use site, so type safety is preserved where values actually flow. Prefer `unknown` or `never` when only one direction applies; reach for `any` only when both apply to the same parameter position.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure about how this guideline is framed.

This note says that this is safe because "The enclosing external APIs re-impose their own parameter types at each use site", and indeed that is why this pattern is safe in the rare case where we've used it.

But this statement is not true of all situations where you might be tempted to use any to get around incompatible type expectations. "callback parameter types constrained by bidirectional assignment" may describe scenarios where there is no "enclosing external API" with genuinely safe types, where this error is a legitimate type error that indicates a broken design.

Copy link
Copy Markdown
Member

@Gudahtt Gudahtt Apr 27, 2026

Choose a reason for hiding this comment

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

I'm not totally sure what to suggest though. I am having trouble describing the safe scenarios here.

It's more than just a matter of encapsulation. We could encapsulate an instance of an invariant type causing a type error, and it still be a legitimate error. We need to be confident the error would never occur (e.g. we'll never attempt to pass in a parameter that doesn't match the callback parameter type, and we'll never return a value that doesn't match the callback return type)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes I'll clarify that sometimes we can't "re-impose types" and any won't be safe to use.

If it's possible to re-type either constraint to resolve an incompatible configuration without affecting downstream callers or introducing semantic inaccuracies, the type error does signal broken design and we shouldn't use any to suppress it.

Will push a new draft based on this.

Copy link
Copy Markdown

@lannathompson65-arch lannathompson65-arch left a comment

Choose a reason for hiding this comment

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

Done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Needs dev review

Development

Successfully merging this pull request may close these issues.

3 participants