Skip to content

Commit cc6ed5a

Browse files
authored
refactor: enable typescript strict mode (#1104)
* refactor: enable typescript `strict` mode * refactor(tsconfig): rearrange options
1 parent 4b4b39d commit cc6ed5a

24 files changed

+217
-123
lines changed

.eslintrc.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ module.exports = {
3939
// react
4040
"react/prop-types": OFF,
4141
"react/no-unescaped-entities": OFF,
42-
"react/jsx-curly-brace-presence": "warn",
42+
"react/jsx-curly-brace-presence": WARN,
4343
// jsx-ally
4444
"jsx-a11y/no-onchange": WARN,
45+
// import
4546
"import/no-anonymous-default-export": OFF,
4647
// next
4748
"@next/next/no-img-element": OFF,

src/components/Admonition.tsx

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ReactNode } from "react"
12
import styles from "./Admonition.module.css"
23
import { cva } from "class-variance-authority"
34

@@ -100,7 +101,17 @@ const admonition = cva(styles.admonition, {
100101
},
101102
})
102103

103-
export const Admonition = ({ type, title, children }) => {
104+
type AdmonitionType = keyof typeof svgMap
105+
106+
export const Admonition = ({
107+
type,
108+
title,
109+
children,
110+
}: {
111+
type: AdmonitionType
112+
title: string
113+
children: ReactNode
114+
}) => {
104115
return (
105116
<div className={admonition({ type })}>
106117
<div className={styles.admonitionHeading}>

src/components/ApiGallery.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { useEffect } from "react"
2+
import type { MouseEventHandler } from "react"
23
import Link from "next/link"
34
import Footer from "./Footer"
45
import typographyStyles from "../styles/typography.module.css"
@@ -10,8 +11,8 @@ import { useRouter } from "next/router"
1011
export default function ApiGallery() {
1112
const router = useRouter()
1213

13-
const onChange = (e) => {
14-
const version = parseInt(e.target.value)
14+
const onChange: MouseEventHandler<HTMLButtonElement> = (e) => {
15+
const version = parseInt((e.target as HTMLElement).getAttribute("value")!)
1516

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

src/components/BuilderPage.tsx

+26-24
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { useState, useRef, useEffect, memo, RefObject } from "react"
22
import { useRouter } from "next/router"
33
import { Animate } from "react-simple-animate"
44
import { useForm } from "react-hook-form"
5+
import type { SubmitHandler } from "react-hook-form"
56
import SortableContainer from "./SortableContainer"
67
import { useStateMachine } from "little-state-machine"
7-
import type { GlobalState } from "little-state-machine"
8+
import type { GlobalState, FormDataItem } from "little-state-machine"
89
import colors from "../styles/colors"
910
import generateCode from "./logic/generateCode"
1011
import copyClipBoard from "./utils/copyClipBoard"
@@ -27,7 +28,9 @@ const errorStyle = {
2728
background: colors.errorPink,
2829
}
2930

30-
const defaultValue = {
31+
type FormFieldDefinitionItem = Partial<FormDataItem> & { toggle?: boolean }
32+
33+
const defaultValue: FormFieldDefinitionItem = {
3134
max: undefined,
3235
min: undefined,
3336
pattern: undefined,
@@ -36,7 +39,7 @@ const defaultValue = {
3639
required: undefined,
3740
name: "",
3841
type: "",
39-
options: [],
42+
options: "",
4043
}
4144

4245
function BuilderPage({
@@ -61,41 +64,43 @@ function BuilderPage({
6164
}
6265
},
6366
})
64-
const [editFormData, setFormData] = useState(defaultValue)
67+
const [editFormData, setEditFormData] = useState(defaultValue)
6568
const { register, handleSubmit, watch, setValue, reset, formState } =
66-
useForm()
69+
useForm<FormFieldDefinitionItem>()
6770
const errors = formState.errors
6871
const [editIndex, setEditIndex] = useState(-1)
6972
const copyFormData = useRef<GlobalState["formData"]>([])
7073
const closeButton = useRef<HTMLButtonElement>(null)
7174
const [showValidation, toggleValidation] = useState(false)
72-
const onSubmit = (data) => {
75+
76+
const onSubmit: SubmitHandler<FormFieldDefinitionItem> = (data) => {
7377
toggleValidation(false)
7478
if (editIndex >= 0) {
75-
formData[editIndex] = data
79+
formData[editIndex] = data as FormDataItem
7680
updateFormData([...formData.filter((formInput) => formInput)])
77-
setFormData(defaultValue)
81+
setEditFormData(defaultValue)
7882
setEditIndex(-1)
7983
} else {
8084
updateFormData([...formData, ...[data]])
8185
}
8286
reset()
8387
}
88+
8489
const form = useRef<HTMLHeadingElement>(null)
8590
const type = watch("type")
8691
const shouldToggleOn =
87-
editFormData.max ||
88-
editFormData.min ||
89-
editFormData.pattern ||
90-
editFormData.maxLength ||
91-
editFormData.minLength ||
92+
!!editFormData.max ||
93+
!!editFormData.min ||
94+
!!editFormData.pattern ||
95+
!!editFormData.maxLength ||
96+
!!editFormData.minLength ||
9297
editFormData.required
9398
copyFormData.current = formData
9499
const editIndexRef = useRef<number | null>(null)
95100
editIndexRef.current = editIndex
96101
const router = useRouter()
97102

98-
const validate = (value) => {
103+
const validate = (value: unknown) => {
99104
return (
100105
!Object.values(copyFormData.current).find(
101106
(data) => data.name === value
@@ -118,7 +123,7 @@ function BuilderPage({
118123
}, [editFormData.type, setValue])
119124

120125
useEffect(() => {
121-
setValue("required", editFormData.required)
126+
setValue("required", !!editFormData.required)
122127
}, [editFormData.required, editIndex, setValue])
123128

124129
const child = (
@@ -140,15 +145,12 @@ function BuilderPage({
140145
</p>
141146

142147
<SortableContainer
143-
{...{
144-
updateFormData,
145-
formData,
146-
editIndex,
147-
setEditIndex,
148-
setFormData,
149-
editFormData,
150-
reset,
151-
}}
148+
updateFormData={updateFormData}
149+
formData={formData}
150+
editIndex={editIndex}
151+
setEditIndex={setEditIndex}
152+
setFormData={setEditFormData}
153+
reset={reset}
152154
/>
153155
</section>
154156

src/components/DevTools.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import type { DevtoolUIProps } from "@hookform/devtools/dist/devToolUI"
2020

2121
const DevTool = dynamic<DevtoolUIProps>(
2222
() =>
23+
// @ts-expect-error no types are available
2324
import("@hookform/devtools/dist/index.cjs.development").then(
2425
(mod) => mod.DevTool
2526
),
@@ -37,7 +38,7 @@ export default function DevTools() {
3738

3839
const { control } = methods
3940

40-
const onSubmit = (data) => console.log(data)
41+
const onSubmit = (data: unknown) => console.log(data)
4142

4243
return (
4344
<div className={containerStyles.container}>

src/components/Form.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { memo } from "react"
22
import { Animate } from "react-simple-animate"
33
import { useStateMachine } from "little-state-machine"
4-
import FormFields from "./FormFields"
5-
import goToBuilder from "./utils/goToBuilder"
6-
import { FieldValues, UseFormReturn } from "react-hook-form"
4+
import type { FieldValues, UseFormReturn } from "react-hook-form"
75
import home from "../data/home"
86
import generic from "../data/generic"
97
import buttonStyles from "../styles/button.module.css"
108
import containerStyles from "../styles/container.module.css"
119
import typographyStyles from "../styles/typography.module.css"
10+
import FormFields from "./FormFields"
11+
import goToBuilder from "./utils/goToBuilder"
1212
import styles from "./Form.module.css"
1313

1414
const animationProps = {
@@ -76,7 +76,7 @@ function Form({
7676
Example
7777
</h2>
7878

79-
<FormFields {...{ formData, errors, register }} />
79+
<FormFields formData={formData} errors={errors} register={register} />
8080

8181
<button className={buttonStyles.pinkButton}>
8282
{home.liveDemo.submit}
@@ -136,7 +136,7 @@ function Form({
136136
<pre className={styles.code}>
137137
{Object.keys(errors).length > 0 &&
138138
JSON.stringify(
139-
Object.entries(errors).reduce(
139+
Object.entries(errors).reduce<Record<string, unknown>>(
140140
// @ts-expect-error needed for previous
141141
// eslint-disable-next-line @typescript-eslint/no-unused-vars
142142
(previous, [key, { ref, ...rest }]) => {

src/components/FormFields.tsx

+29-7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { GlobalState } from "little-state-machine"
2+
import type { FieldErrors, UseFormRegister } from "react-hook-form"
13
import colors from "../styles/colors"
24
import styles from "./FormFields.module.css"
35

@@ -7,7 +9,25 @@ const errorStyle = {
79
borderLeft: `10px solid ${colors.lightPink}`,
810
}
911

10-
const FormFields = ({ formData, errors, register }) => {
12+
function getNumericValidationFor<
13+
TKey extends "maxLength" | "minLength" | "min" | "max",
14+
>(name: TKey, value: string): Record<TKey, number> | null {
15+
const number = parseInt(value, 10)
16+
if (typeof number === "number" && !Number.isNaN(number)) {
17+
return { [name]: number } as Record<TKey, number>
18+
}
19+
return null
20+
}
21+
22+
const FormFields = ({
23+
formData,
24+
errors,
25+
register,
26+
}: {
27+
formData: GlobalState["formData"]
28+
errors: FieldErrors
29+
register: UseFormRegister<Record<string, unknown>>
30+
}) => {
1131
return (formData || []).map((field, i) => {
1232
switch (field.type) {
1333
case "select":
@@ -38,8 +58,8 @@ const FormFields = ({ formData, errors, register }) => {
3858
placeholder={field.name}
3959
{...register(field.name, {
4060
required: field.required,
41-
...(field.maxLength ? { maxLength: field.maxLength } : null),
42-
...(field.minLength ? { minLength: field.minLength } : null),
61+
...getNumericValidationFor("maxLength", field.maxLength),
62+
...getNumericValidationFor("minLength", field.minLength),
4363
})}
4464
key={field.name}
4565
style={{
@@ -96,13 +116,15 @@ const FormFields = ({ formData, errors, register }) => {
96116
placeholder={field.name}
97117
{...register(field.name, {
98118
required: field.required,
119+
99120
...(field.pattern
100121
? { pattern: new RegExp(field.pattern) }
101122
: null),
102-
...(field.max ? { max: field.max } : null),
103-
...(field.min ? { min: field.min } : null),
104-
...(field.maxLength ? { maxLength: field.maxLength } : null),
105-
...(field.minLength ? { minLength: field.minLength } : null),
123+
124+
...getNumericValidationFor("max", field.max),
125+
...getNumericValidationFor("min", field.min),
126+
...getNumericValidationFor("maxLength", field.maxLength),
127+
...getNumericValidationFor("minLength", field.minLength),
106128
})}
107129
/>
108130
)

src/components/HomePage.tsx

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { useState, useRef, useEffect, memo } from "react"
2+
import { useForm } from "react-hook-form"
3+
import type { SubmitHandler } from "react-hook-form"
4+
import Link from "next/link"
25
import Form from "./Form"
36
import Header from "./Header"
47
import Watcher from "./Watcher"
@@ -16,8 +19,6 @@ import styles from "./HomePage.module.css"
1619
import { SponsorsList } from "./sponsorsList"
1720
import { useRouter } from "next/router"
1821
import { GeneralObserver } from "./general-observer"
19-
import Link from "next/link"
20-
import { useForm } from "react-hook-form"
2122

2223
function HomePage() {
2324
const [submitData, updateSubmitData] = useState({})
@@ -33,7 +34,8 @@ function HomePage() {
3334
const [watchPlay, setWatchPlay] = useState(false)
3435
const { query } = useRouter()
3536
const methods = useForm()
36-
const onSubmit = (data) => {
37+
38+
const onSubmit: SubmitHandler<Record<string, unknown>> = (data) => {
3739
updateSubmitData(data)
3840
}
3941

@@ -273,13 +275,11 @@ function HomePage() {
273275
<div ref={HomeRef} />
274276

275277
<Form
276-
{...{
277-
methods,
278-
onSubmit,
279-
submitData,
280-
toggleBuilder,
281-
formUpdated,
282-
}}
278+
methods={methods}
279+
onSubmit={onSubmit}
280+
submitData={submitData}
281+
toggleBuilder={toggleBuilder}
282+
formUpdated={formUpdated}
283283
/>
284284

285285
<section className={containerStyles.centerContent}>

src/components/IsolateRender.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { memo, useState } from "react"
2+
import type React from "react"
23
import { AnimateKeyframes, Animate } from "react-simple-animate"
34
import colors from "../styles/colors"
45
import home from "../data/home"
@@ -39,7 +40,7 @@ const IsoLateInput = () => {
3940
)
4041
}
4142

42-
const ControlledInputs = ({ style }) => {
43+
const ControlledInputs = ({ style }: { style?: React.CSSProperties }) => {
4344
const [play, setPlay] = useState(false)
4445

4546
return (

src/components/Search.tsx

+8-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ import searchStyles from "./Search.module.css"
33
import useWindowSize from "./utils/useWindowSize"
44
import { LARGE_SCREEN } from "../styles/breakpoints"
55

6-
const Search = ({ focus, setFocus }: { focus: boolean; setFocus }) => {
6+
const Search = ({
7+
focus,
8+
setFocus,
9+
}: {
10+
focus: boolean
11+
setFocus: (value: boolean) => void
12+
}) => {
713
const { width } = useWindowSize()
814
const searchRef = useRef<HTMLInputElement>(null)
915

@@ -55,4 +61,5 @@ const Search = ({ focus, setFocus }: { focus: boolean; setFocus }) => {
5561
</>
5662
)
5763
}
64+
5865
export default Search

0 commit comments

Comments
 (0)