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

Commit 1c63b2d

Browse files
authored
Fix types for static image (vercel#25808)
If you give a Static Image to the Image component, TypeScript will throw a type error. This Pull Request fixes it. ## Bug - ~~Related issues linked using `fixes #number`~~ - [x] Integration tests added ## ~~Feature~~ - ~~Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.~~ - ~~Related issues linked using `fixes #number`~~ - ~~Integration tests added~~ - ~~Documentation added~~ - ~~Telemetry added. In case of a feature if it's used or not.~~ ## ~~Documentation / Examples~~ - ~~Make sure the linting passes~~ --- follow-up vercel#24993 cc @atcastle
1 parent 80d023c commit 1c63b2d

File tree

6 files changed

+108
-17
lines changed

6 files changed

+108
-17
lines changed

packages/next/client/image.tsx

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -83,33 +83,45 @@ function isStaticImport(src: string | StaticImport): src is StaticImport {
8383
)
8484
}
8585

86+
type StringImageProps = {
87+
src: string
88+
} & (
89+
| { width?: never; height?: never; layout: 'fill' }
90+
| {
91+
width: number | string
92+
height: number | string
93+
layout?: Exclude<LayoutValue, 'fill'>
94+
}
95+
) &
96+
(
97+
| {
98+
placeholder?: Exclude<PlaceholderValue, 'blur'>
99+
blurDataURL?: never
100+
}
101+
| { placeholder: 'blur'; blurDataURL: string }
102+
)
103+
104+
type ObjectImageProps = {
105+
src: StaticImport
106+
width?: number | string
107+
height?: number | string
108+
layout?: LayoutValue
109+
placeholder?: PlaceholderValue
110+
blurDataURL?: never
111+
}
112+
86113
export type ImageProps = Omit<
87114
JSX.IntrinsicElements['img'],
88115
'src' | 'srcSet' | 'ref' | 'width' | 'height' | 'loading' | 'style'
89116
> & {
90-
src: string | StaticImport
91117
loader?: ImageLoader
92118
quality?: number | string
93119
priority?: boolean
94120
loading?: LoadingValue
95121
unoptimized?: boolean
96122
objectFit?: ImgElementStyle['objectFit']
97123
objectPosition?: ImgElementStyle['objectPosition']
98-
} & (
99-
| { width?: never; height?: never; layout: 'fill' }
100-
| {
101-
width: number | string
102-
height: number | string
103-
layout?: Exclude<LayoutValue, 'fill'>
104-
}
105-
) &
106-
(
107-
| {
108-
placeholder?: Exclude<PlaceholderValue, 'blur'>
109-
blurDataURL?: never
110-
}
111-
| { placeholder: 'blur'; blurDataURL: string }
112-
)
124+
} & (StringImageProps | ObjectImageProps)
113125

114126
const {
115127
deviceSizes: configDeviceSizes,

packages/next/types/global.d.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,58 @@ declare module '*.module.scss' {
2525
const classes: { readonly [key: string]: string }
2626
export default classes
2727
}
28+
29+
interface StaticImageData {
30+
src: string
31+
height: number
32+
width: number
33+
placeholder?: string
34+
}
35+
36+
declare module '*.png' {
37+
const content: StaticImageData
38+
39+
export default content
40+
}
41+
42+
declare module '*.svg' {
43+
const content: StaticImageData
44+
45+
export default content
46+
}
47+
48+
declare module '*.jpg' {
49+
const content: StaticImageData
50+
51+
export default content
52+
}
53+
54+
declare module '*.jpeg' {
55+
const content: StaticImageData
56+
57+
export default content
58+
}
59+
60+
declare module '*.gif' {
61+
const content: StaticImageData
62+
63+
export default content
64+
}
65+
66+
declare module '*.webp' {
67+
const content: StaticImageData
68+
69+
export default content
70+
}
71+
72+
declare module '*.ico' {
73+
const content: StaticImageData
74+
75+
export default content
76+
}
77+
78+
declare module '*.bmp' {
79+
const content: StaticImageData
80+
81+
export default content
82+
}

test/integration/image-component/typescript/pages/invalid.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ const Invalid = () => {
99
id="no-width-or-height"
1010
src="https://via.placeholder.com/500"
1111
></Image>
12+
<Image
13+
id="no-blur-data-url"
14+
src="https://via.placeholder.com/500"
15+
width={500}
16+
height={500}
17+
placeholder="blur"
18+
></Image>
1219
<p id="stubtext">This is the invalid usage</p>
1320
</div>
1421
)

test/integration/image-component/typescript/pages/valid.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react'
22
import Image from 'next/image'
3+
import testTall from '../public/tall.png'
34

45
const Page = () => {
56
return (
@@ -58,6 +59,20 @@ const Page = () => {
5859
objectFit="scale-down"
5960
objectPosition="50px"
6061
/>
62+
<Image
63+
id="placeholder-and-blur-data-url"
64+
src="https://via.placeholder.com/500"
65+
width={500}
66+
height={500}
67+
placeholder="blur"
68+
blurDataURL="data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="
69+
/>
70+
<Image id="no-width-and-height" src={testTall} />
71+
<Image
72+
id="object-src-with-placeholder"
73+
src={testTall}
74+
placeholder="blur"
75+
/>
6176
<p id="stubtext">This is valid usage of the Image component</p>
6277
</div>
6378
)
Loading

test/integration/rewrite-with-browser-history/test/index.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ const runTests = () => {
3232

3333
await browser.back().waitForElementByCss('#another')
3434

35-
expect(await browser.elementByCss('#another').text()).toBe('another page')
35+
expect(await browser.waitForElementByCss('#another').text()).toBe(
36+
'another page'
37+
)
3638

3739
expect(await browser.eval('window.beforeNav')).toBe(1)
3840
})

0 commit comments

Comments
 (0)