fix(next/image): annotate Image export so deprecated props show as deprecated - #98597
Open
franpeza wants to merge 1 commit into
Open
fix(next/image): annotate Image export so deprecated props show as deprecated#98597franpeza wants to merge 1 commit into
franpeza wants to merge 1 commit into
Conversation
…es call sites `Image` was declared as `forwardRef<HTMLImageElement | null, ImageProps>(...)` with the return type left to inference. Because `ImageProps` is declared in another module, declaration emit expands the alias inline and drops the JSDoc of every member, so the `@deprecated` tags never reach editors at the call site. Annotating the export explicitly keeps the alias by reference, which preserves the tags for `priority`, `onLoadingComplete`, `layout`, `objectFit`, `objectPosition`, `lazyBoundary` and `lazyRoot`. The emitted type is unchanged in substance: the previous expanded form and `ImageProps & React.RefAttributes<HTMLImageElement | null>` are mutually assignable, and prop completion is identical.
franpeza
marked this pull request as ready for review
September 12, 2026 07:37
SapanMozammel
left a comment
There was a problem hiding this comment.
Great TypeScript catch! Explicitly typing Image prevents declaration emit from dropping member JSDoc and @deprecated tags at consumer call sites. Unit tests look solid.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The
@deprecatedtags onnext/imageprops don't show up in the editor. If you write<Image priority />you get no strikethrough and no hint that you should usepreloadnow.The tags are there in
ImageProps, they just never reach the user.Cause
Imageis declared like this, with the return type inferred:ImagePropslives in another file (shared/lib/get-img-props). When TypeScript expands a type alias from a different module during declaration emit, it drops the JSDoc of all the members. If the alias is in the same file the comments survive, cross file they don't.This is what we ship today:
And this is what we get with the fix:
It recovers the tags for
priority,onLoadingComplete,layout,objectFit,objectPosition,lazyBoundaryandlazyRoot. So every deprecation added to the component since #83351 has been invisible.Is this breaking?
No. The old expanded type and the new one are assignable in both directions, and the props you get in autocomplete are the same (304 entries, same list). Only the way the type is printed changes. Now the deprecated ones are also marked as deprecated in the completion list.
The component is split in two (
ImageImpl+ the annotated export) only to keep the diff small. If I annotate the export directly, Prettier reformats the whole function body and the diff becomes 135 lines for a one line change.Test
test/unit/image-component-deprecated-props.test.tscompiles a small file that importsnext/image, and checks thatgetJsDocTags()returnsdeprecatedfor each of those props. With the old declaration it fails 7 of 8 (the 8th one only checks the props resolve, and passes either way, so it works as a control).I didn't use
expectTypeOflikeinfer-get-server-side-props-type.test.tsdoes, because both types are assignable to each other, so a type equality check passes before and after the fix. It can't catch this.Alternative I tried
Turning
ImagePropsinto aninterfacealso fixes it, and it needs no changes in the component, because interfaces are always referenced by name in the emitted types. I didn't go that way because interfaces don't get an implicit index signature, soconst x: Record<string, unknown> = imagePropsstops compiling.ImagePropsis public, so that would break users. Happy to switch if you prefer it.Also worth noting: with
--isolatedDeclarationsTypeScript already rejects the current code withTS9010: Variable must have an explicit type annotation. So this annotation is what TS wants anyway.