Skip to content

Commit fe7d561

Browse files
authored
refactor: enable recommendedTypeChecked rules (#1107)
* refactor: enable `recommendedTypeChecked` rules * ci: add lint:ci script
1 parent 385d6c2 commit fe7d561

35 files changed

+222
-160
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
strategy:
2121
fail-fast: true
2222
matrix:
23-
script: ["format", "lint", "typecheck:ci"]
23+
script: ["format", "lint:ci", "typecheck:ci"]
2424

2525
steps:
2626
- name: Checkout repo

contentlayer.config.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import remarkGfm from "remark-gfm"
44
import rehypeMdxCodeProps from "rehype-mdx-code-props"
55
import emoji from "remark-emoji"
66
import * as sidebar from "./src/components/Menu/MenuLinks"
7+
import type { Pages } from "./src/types/types"
78

89
export const Doc = defineDocumentType(() => ({
910
name: "Doc",
@@ -33,13 +34,22 @@ export const Doc = defineDocumentType(() => ({
3334
type: "string",
3435
resolve: (doc) => doc._raw.flattenedPath.split("/").slice(1).join("/"),
3536
},
37+
38+
/**
39+
* Can't define value different from primitives, so hardcoding the correct type
40+
* @see https://github.com/contentlayerdev/contentlayer/issues/149
41+
*/
3642
segment: {
37-
type: "list",
43+
type: "string[]" as "list",
3844
resolve: (doc) => doc._raw.flattenedPath.split("/"),
3945
},
4046
pages: {
41-
type: "list",
42-
resolve: (doc) => sidebar[doc.sidebar] ?? [],
47+
type: "json",
48+
resolve: (doc) => {
49+
// added explicit type casting to keep track of this data structure
50+
// in case in the future contentlayer will support values different than primitives
51+
return sidebar[doc.sidebar] as unknown as Pages
52+
},
4353
},
4454
},
4555
}))

eslint.config.mjs

+23-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ const compat = new FlatCompat({
99
})
1010

1111
export default tseslint.config(
12-
tseslint.configs.recommended,
12+
tseslint.configs.recommendedTypeChecked,
13+
1314
...compat.config({
1415
extends: ["next"],
1516
rules: {
@@ -30,17 +31,37 @@ export default tseslint.config(
3031
"@next/next/no-img-element": "off",
3132
},
3233
}),
34+
3335
// @ts-expect-error eslintPluginReact.configs.flat, but runtime is always defined
3436
eslintPluginReact.configs.flat["jsx-runtime"],
37+
3538
{
3639
linterOptions: {
3740
reportUnusedDisableDirectives: "error",
3841
},
42+
languageOptions: {
43+
parserOptions: {
44+
projectService: true,
45+
tsconfigRootDir: import.meta.dirname,
46+
},
47+
},
3948
rules: {
4049
// typescript
4150
"@typescript-eslint/explicit-function-return-type": "off",
42-
"@typescript-eslint/interface-name-prefix": "off",
4351
"@typescript-eslint/explicit-module-boundary-types": "off",
52+
"@typescript-eslint/interface-name-prefix": "off",
53+
"@typescript-eslint/no-floating-promises": "off",
54+
"@typescript-eslint/restrict-template-expressions": [
55+
"error",
56+
{
57+
allowAny: false,
58+
allowBoolean: false,
59+
allowNever: false,
60+
allowNullish: false,
61+
allowNumber: true,
62+
allowRegExp: false,
63+
},
64+
],
4465
},
4566
}
4667
)

next.config.ts

+10
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,17 @@ import { withContentlayer } from "next-contentlayer"
33
import withBundleAnalyzer from "@next/bundle-analyzer"
44

55
const nextConfig: NextConfig = {
6+
eslint: {
7+
/**
8+
* Now eslint requires typecheck so we have to run build before executing lint
9+
* These check are performed via `.github/workflows/ci.yml` action
10+
*
11+
* @see https://github.com/react-hook-form/documentation/pull/1107
12+
*/
13+
ignoreDuringBuilds: true,
14+
},
615
typescript: {
16+
/** @see `eslint.ignoreDuringBuilds` comment */
717
ignoreBuildErrors: true,
818
},
919
reactStrictMode: true,

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"format:fix": "prettier . --write",
6464
"lint": "next lint",
6565
"lint:fix": "next lint --fix",
66+
"lint:ci": "next build --no-lint && next lint",
6667
"now-build": "pnpm run build",
6768
"start": "next start",
6869
"typecheck": "tsc --noEmit",

src/components/Admonition.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export const Admonition = ({
109109
children,
110110
}: {
111111
type: AdmonitionType
112-
title: string
112+
title?: string
113113
children: ReactNode
114114
}) => {
115115
return (

src/components/ApiGallery.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ export default function ApiGallery() {
1212
const router = useRouter()
1313

1414
const onChange: MouseEventHandler<HTMLButtonElement> = (e) => {
15-
const version = parseInt((e.target as HTMLElement).getAttribute("value")!)
15+
const version = parseInt(
16+
(e.target as HTMLElement).getAttribute("value") as string
17+
)
1618

1719
if (version !== 7) {
1820
router.push(`https://legacy.react-hook-form.com/v${version}/api`)

src/components/ApiRefTable.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,9 @@ export default function ApiRefTable({ api }: { api: typeof apiData }) {
8686
<legend>{api.register.options.title}</legend>
8787
<label>
8888
<input
89-
onChange={() => toggleOption(true)}
89+
onChange={() => {
90+
toggleOption(true)
91+
}}
9092
type="radio"
9193
name="errorMessage"
9294
defaultChecked
@@ -95,7 +97,9 @@ export default function ApiRefTable({ api }: { api: typeof apiData }) {
9597
</label>
9698
<label>
9799
<input
98-
onChange={() => toggleOption(false)}
100+
onChange={() => {
101+
toggleOption(false)
102+
}}
99103
type="radio"
100104
name="errorMessage"
101105
/>

src/components/BuilderPage.tsx

+11-8
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function BuilderPage({
5757
state: { formData = [] },
5858
actions: { updateFormData },
5959
} = useStateMachine({
60-
updateFormData: (state, payload) => {
60+
updateFormData: (state, payload: GlobalState["formData"]) => {
6161
return {
6262
...state,
6363
formData: [...payload],
@@ -81,7 +81,7 @@ function BuilderPage({
8181
setEditFormData(defaultValue)
8282
setEditIndex(-1)
8383
} else {
84-
updateFormData([...formData, ...[data]])
84+
updateFormData([...formData, data as FormDataItem])
8585
}
8686
reset()
8787
}
@@ -153,7 +153,7 @@ function BuilderPage({
153153
reset={reset}
154154
/>
155155
</section>
156-
156+
{/* eslint-disable-next-line @typescript-eslint/no-misused-promises */}
157157
<form className={styles.form} onSubmit={handleSubmit(onSubmit)}>
158158
<h2 className={typographyStyles.title} ref={form}>
159159
{builder.inputCreator.title}
@@ -255,7 +255,9 @@ function BuilderPage({
255255
<input
256256
type="checkbox"
257257
{...register("toggle", { required: false })}
258-
onClick={() => toggleValidation(!showValidation)}
258+
onClick={() => {
259+
toggleValidation(!showValidation)
260+
}}
259261
/>
260262
{builder.inputCreator.validation}
261263
</label>
@@ -325,7 +327,7 @@ function BuilderPage({
325327
<button
326328
className={buttonStyles.pinkButton}
327329
onClick={() => {
328-
form?.current?.scrollIntoView({ behavior: "smooth" })
330+
form.current?.scrollIntoView({ behavior: "smooth" })
329331
}}
330332
>
331333
{editIndex >= 0 ? generic.update : generic.create}
@@ -345,7 +347,7 @@ function BuilderPage({
345347
)}
346348

347349
<Animate
348-
play={(formData || []).length > 0}
350+
play={formData.length > 0}
349351
start={{
350352
opacity: 0,
351353
pointerEvents: "none",
@@ -374,7 +376,6 @@ function BuilderPage({
374376
)}
375377
/>
376378
</form>
377-
378379
<section
379380
style={{
380381
paddingRight: "20px",
@@ -395,7 +396,9 @@ function BuilderPage({
395396
>
396397
<div className={styles.buttonWrapper}>
397398
<ClipBoard
398-
onClick={() => copyClipBoard(generateCode(formData))}
399+
onClick={() => {
400+
copyClipBoard(generateCode(formData))
401+
}}
399402
className={`${styles.button} ${styles.copyButton}`}
400403
/>
401404
</div>

src/components/ClipBoard.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ const ClipBoard = ({
1919
setCopiedCode(false)
2020
}, 3000)
2121

22-
return () => clearTimeout(timerId)
22+
return () => {
23+
clearTimeout(timerId)
24+
}
2325
}, [copiedCode])
2426

2527
return (

src/components/CodeArea.tsx

+18-9
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ export default function CodeArea({
3030
}) {
3131
const [currentType, setType] = useState<
3232
(typeof ToggleTypes)[keyof typeof ToggleTypes]
33-
>(
34-
(rawData && ToggleTypes.js) ||
35-
(tsRawData && ToggleTypes.ts) ||
36-
ToggleTypes.types
37-
)
33+
>(() => {
34+
if (rawData) return ToggleTypes.js
35+
if (tsRawData) return ToggleTypes.ts
36+
return ToggleTypes.types
37+
})
38+
3839
const codeAreaRef = useRef<HTMLDivElement | null>(null)
3940

4041
return (
@@ -46,7 +47,9 @@ export default function CodeArea({
4647
<div className={styles.buttonWrapper}>
4748
{((rawData && tsRawData) || (rawData && rawTypes)) && (
4849
<button
49-
onClick={() => setType(ToggleTypes.js)}
50+
onClick={() => {
51+
setType(ToggleTypes.js)
52+
}}
5053
className={`${styles.button} ${styles.codeLink} ${
5154
currentType === ToggleTypes.js ? styles.active : ""
5255
}`}
@@ -56,7 +59,9 @@ export default function CodeArea({
5659
)}
5760
{((tsRawData && rawData) || (tsRawData && rawTypes)) && (
5861
<button
59-
onClick={() => setType(ToggleTypes.ts)}
62+
onClick={() => {
63+
setType(ToggleTypes.ts)
64+
}}
6065
className={`${styles.button} ${styles.codeLink} ${
6166
currentType === ToggleTypes.ts ? styles.active : ""
6267
}`}
@@ -66,7 +71,9 @@ export default function CodeArea({
6671
)}
6772
{((rawTypes && rawData) || (rawTypes && tsRawData)) && (
6873
<button
69-
onClick={() => setType(ToggleTypes.types)}
74+
onClick={() => {
75+
setType(ToggleTypes.types)
76+
}}
7077
className={`${styles.button} ${styles.codeLink} ${
7178
currentType === ToggleTypes.types ? styles.active : ""
7279
}`}
@@ -77,7 +84,9 @@ export default function CodeArea({
7784
{!withOutCopy && (
7885
<ClipBoard
7986
className={`${styles.button} ${styles.copyButton}`}
80-
onClick={() => copyClipBoard(codeAreaRef.current?.innerText || "")}
87+
onClick={() => {
88+
copyClipBoard(codeAreaRef.current?.innerText || "")
89+
}}
8190
/>
8291
)}
8392

src/components/CodeCompareSection.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,8 @@ function CodeCompareSection({
4747
borderRadius: 4,
4848
overflow: "hidden",
4949
transition: "0.3s all 0.5s",
50-
opacity: isPlayCodeCompare ? 1 : 0,
51-
transform: isPlayCodeCompare
52-
? "translateY(0)"
53-
: "translateY(100px)",
50+
opacity: 1,
51+
transform: "translateY(0)",
5452
}}
5553
title="React Hook Form codesandbox demo"
5654
allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"

src/components/DevTools.tsx

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useState } from "react"
2+
import type React from "react"
23
import { useForm } from "react-hook-form"
34
import { Animate } from "react-simple-animate"
45
import Form from "./Form"
@@ -22,7 +23,7 @@ const DevTool = dynamic<DevtoolUIProps>(
2223
() =>
2324
// @ts-expect-error no types are available
2425
import("@hookform/devtools/dist/index.cjs.development").then(
25-
(mod) => mod.DevTool
26+
(mod) => (mod as { DevTool: React.ElementType<DevtoolUIProps> }).DevTool
2627
),
2728
{
2829
ssr: false,
@@ -38,7 +39,9 @@ export default function DevTools() {
3839

3940
const { control } = methods
4041

41-
const onSubmit = (data: unknown) => console.log(data)
42+
const onSubmit = (data: unknown) => {
43+
console.log(data)
44+
}
4245

4346
return (
4447
<div className={containerStyles.container}>
@@ -81,7 +84,9 @@ export default function DevTools() {
8184
npm install -D @hookform/devtools
8285
<ClipBoard
8386
className={getStartedStyle.copyButton}
84-
onClick={() => copyClipBoard("npm install -D @hookform/devtools")}
87+
onClick={() => {
88+
copyClipBoard("npm install -D @hookform/devtools")
89+
}}
8590
/>
8691
</pre>
8792

src/components/Form.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ function Form({
3333
submitData: Record<string, unknown>
3434
toggleBuilder: (state: boolean) => void
3535
formUpdated: boolean
36-
methods: UseFormReturn<FieldValues, Record<string, unknown>, undefined>
36+
methods: UseFormReturn<FieldValues, Record<string, unknown>>
3737
devTool?: boolean
3838
}) {
3939
const { register, handleSubmit, watch, formState, reset } = methods
4040

41-
const touched = Object.keys(formState.touchedFields || {})
41+
const touched = Object.keys(formState.touchedFields)
4242
const {
4343
state: { formData },
4444
} = useStateMachine()
@@ -71,6 +71,7 @@ function Form({
7171
)}
7272

7373
<div className={styles.wrapper}>
74+
{/* eslint-disable-next-line @typescript-eslint/no-misused-promises */}
7475
<form className={styles.demoForm} onSubmit={handleSubmit(onSubmit)}>
7576
<h2 className={typographyStyles.title} style={{ marginTop: 40 }}>
7677
Example

0 commit comments

Comments
 (0)