Skip to content

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

Open
MajorLift wants to merge 6 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 6 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 that clarifies when any is acceptable for callback parameters under bidirectional function-type constraints; no runtime or type-checking behavior is modified.

Overview
Adds a new TypeScript guideline section that carves out a narrow exception for using parameter any in callbacks that sit in a bivariant position between two fixed, irresolvable function-type constraints.

Includes examples distinguishing redesignable vs fixed constraints, guidance to annotate eslint-disable comments with the acceptance criteria, and an appendix-style derivation explaining why unknown/never fail under --strictFunctionTypes.

Reviewed by Cursor Bugbot for commit 7c49096. 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 Outdated
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.

lannathompson65-arch

This comment was marked as spam.

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.

Amazing

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