Skip to content
This repository has been archived by the owner on Jun 21, 2023. It is now read-only.

Commit

Permalink
Loosen next/image TS types for width and height (vercel#26991)
Browse files Browse the repository at this point in the history
### Description
This changes the strict TS types to a looser implementation such that the user can always pass `width` and `height` (even when `layout=fill`) without TS errors.

### Pros vs Cons
- **Pros**: better support for wrapping `next/image` so that TS won't report false errors with `layout=fill`
- **Cons**: omitting width/height when using other `layout` will no longer show TS errors and instead fail with a runtime error

### Issues
- Fixes vercel#26531 
- Fixes vercel#25440
  • Loading branch information
styfle committed Jul 7, 2021
1 parent 0a71528 commit 8695cd5
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 39 deletions.
63 changes: 29 additions & 34 deletions packages/next/client/image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,21 +86,16 @@ function isStaticImport(src: string | StaticImport): src is StaticImport {

type StringImageProps = {
src: string
width?: number | string
height?: number | string
layout?: LayoutValue
} & (
| { width?: never; height?: never; layout: 'fill' }
| {
width: number | string
height: number | string
layout?: Exclude<LayoutValue, 'fill'>
placeholder?: Exclude<PlaceholderValue, 'blur'>
blurDataURL?: never
}
) &
(
| {
placeholder?: Exclude<PlaceholderValue, 'blur'>
blurDataURL?: never
}
| { placeholder: 'blur'; blurDataURL: string }
)
| { placeholder: 'blur'; blurDataURL: string }
)

type ObjectImageProps = {
src: StaticImport
Expand Down Expand Up @@ -381,6 +376,11 @@ export default function Image({
`Image with src "${src}" has invalid "width" or "height" property. These should be numeric values.`
)
}
if (layout === 'fill' && (width || height)) {
console.warn(
`Image with src "${src}" and "layout='fill'" has unused properties assigned. Please remove "width" and "height".`
)
}
if (!VALID_LOADING_VALUES.includes(loading)) {
throw new Error(
`Image with src "${src}" has invalid "loading" property. Provided "${loading}" should be one of ${VALID_LOADING_VALUES.map(
Expand Down Expand Up @@ -472,10 +472,24 @@ export default function Image({
}
: undefined),
}
if (
if (layout === 'fill') {
// <Image src="i.png" layout="fill" />
wrapperStyle = {
display: 'block',
overflow: 'hidden',

position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,

boxSizing: 'border-box',
margin: 0,
}
} else if (
typeof widthInt !== 'undefined' &&
typeof heightInt !== 'undefined' &&
layout !== 'fill'
typeof heightInt !== 'undefined'
) {
// <Image src="i.png" width="100" height="100" />
const quotient = heightInt / widthInt
Expand Down Expand Up @@ -518,25 +532,6 @@ export default function Image({
height: heightInt,
}
}
} else if (
typeof widthInt === 'undefined' &&
typeof heightInt === 'undefined' &&
layout === 'fill'
) {
// <Image src="i.png" layout="fill" />
wrapperStyle = {
display: 'block',
overflow: 'hidden',

position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,

boxSizing: 'border-box',
margin: 0,
}
} else {
// <Image src="i.png" />
if (process.env.NODE_ENV !== 'production') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react'
import Image, { ImageProps } from 'next/image'

type ImageCardProps = ImageProps & {
id: string
optional?: string
}

/**
* Example of using the `Image` component in a HOC.
*/
export function ImageCard(props: ImageCardProps) {
return <Image {...props} layout="fill" />
}
4 changes: 0 additions & 4 deletions test/integration/image-component/typescript/pages/invalid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ const Invalid = () => {
return (
<div>
<h1>Invalid TS</h1>
<Image
id="no-width-or-height"
src="https://via.placeholder.com/500"
></Image>
<Image
id="no-blur-data-url"
src="https://via.placeholder.com/500"
Expand Down
9 changes: 9 additions & 0 deletions test/integration/image-component/typescript/pages/valid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react'
import Image from 'next/image'
import testTall from '../public/tall.png'
import svg from '../public/test.svg'
import { ImageCard } from '../components/image-card'

const Page = () => {
return (
Expand Down Expand Up @@ -75,6 +76,14 @@ const Page = () => {
placeholder="blur"
/>
<Image id="object-src-with-svg" src={svg} />
<Image
id="fill-with-unused-width-height"
src="https://via.placeholder.com/200"
layout="fill"
width={100}
height={100}
/>
<ImageCard id="image-card" src="https://via.placeholder.com/300" />
<p id="stubtext">This is valid usage of the Image component</p>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ describe('TypeScript Image Component', () => {

it('should print error when invalid Image usage', async () => {
await renderViaHTTP(appPort, '/invalid', {})
expect(output).toMatch(/Error: Image/)
expect(output).toMatch(
/must use "width" and "height" properties or "layout='fill'" property/
/has "placeholder='blur'" property but is missing the "blurDataURL" property/
)
})
})
Expand Down

0 comments on commit 8695cd5

Please sign in to comment.