Skip to content

Commit

Permalink
Loosen next/image TS types for src (#26996)
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 pass `src` without TS errors.

### Pros vs Cons
- **Pros**: better support for wrapping `next/image` so that TS won't report false errors
- **Cons**: using `src: string` without `blurDataURL` will no longer show TS errors and instead fail with a runtime error

### Issues
- Fixes #26892 
- Related to #26991
  • Loading branch information
styfle committed Jul 7, 2021
1 parent 12aa561 commit b6c590b
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 29 deletions.
30 changes: 7 additions & 23 deletions packages/next/client/image.tsx
Expand Up @@ -85,41 +85,25 @@ function isStaticImport(src: string | StaticImport): src is StaticImport {
)
}

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

type ObjectImageProps = {
src: StaticImport
width?: number | string
height?: number | string
layout?: LayoutValue
placeholder?: PlaceholderValue
blurDataURL?: never
}

export type ImageProps = Omit<
JSX.IntrinsicElements['img'],
'src' | 'srcSet' | 'ref' | 'width' | 'height' | 'loading' | 'style'
> & {
src: string | StaticImport
width?: number | string
height?: number | string
layout?: LayoutValue
loader?: ImageLoader
quality?: number | string
priority?: boolean
loading?: LoadingValue
placeholder?: PlaceholderValue
blurDataURL?: string
unoptimized?: boolean
objectFit?: ImgElementStyle['objectFit']
objectPosition?: ImgElementStyle['objectPosition']
onLoadingComplete?: () => void
} & (StringImageProps | ObjectImageProps)
}

const {
deviceSizes: configDeviceSizes,
Expand Down
@@ -0,0 +1,16 @@
import React from 'react'
import Image, { ImageProps } from 'next/image'

type DynamicSrcImageProps = ImageProps & {
id: string
srcString?: string
}

export function DynamicSrcImage({
srcString,
src,
...props
}: DynamicSrcImageProps) {
const newSrc = srcString || src
return <Image {...props} src={newSrc} />
}
20 changes: 17 additions & 3 deletions test/integration/image-component/typescript/pages/invalid.tsx
Expand Up @@ -5,12 +5,26 @@ const Invalid = () => {
return (
<div>
<h1>Invalid TS</h1>
<Image id="invalid-src" src={new Date()} width={500} height={500}></Image>
<Image
id="no-blur-data-url"
id="invalid-width"
src="https://via.placeholder.com/500"
width={500}
width={new Date()}
height={500}
placeholder="blur"
></Image>
<Image
id="invalid-layout"
src="https://via.placeholder.com/500"
width="500"
height="500"
layout="invalid"
></Image>
<Image
id="invalid-placeholder"
src="https://via.placeholder.com/500"
width="500"
height="500"
placeholder="invalid"
></Image>
<p id="stubtext">This is the invalid usage</p>
</div>
Expand Down
7 changes: 7 additions & 0 deletions test/integration/image-component/typescript/pages/valid.tsx
Expand Up @@ -3,6 +3,7 @@ import Image from 'next/image'
import testTall from '../public/tall.png'
import svg from '../public/test.svg'
import { ImageCard } from '../components/image-card'
import { DynamicSrcImage } from '../components/image-dynamic-src'

const Page = () => {
return (
Expand Down Expand Up @@ -84,6 +85,12 @@ const Page = () => {
height={100}
/>
<ImageCard id="image-card" src="https://via.placeholder.com/300" />
<DynamicSrcImage
id="dynamic-src"
src="https://via.placeholder.com/400"
width={400}
height={400}
/>
<p id="stubtext">This is valid usage of the Image component</p>
</div>
)
Expand Down
Expand Up @@ -74,9 +74,6 @@ 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(
/has "placeholder='blur'" property but is missing the "blurDataURL" property/
)
})
})

Expand Down

0 comments on commit b6c590b

Please sign in to comment.