Here is a reducer used in React's useReducer
export const formReducer = (state: FormState, action: Action) => {
const { data, type } = action
switch (type) {
case 'updated_input': {
const { value, hasError, error, name, isFormValid } = data
return {
...state,
[name]: {
...(state[name as keyof FormState] as {}),
value,
hasError,
error,
},
isFormValid,
}
}
case 'updated_nested_input': {
const { value, hasError, error, isFormValid, nestedKey, name } = data
return {
...state,
[nestedKey]: {
...state[nestedKey],
[name]: {
...(state[nestedKey][name as keyof (Address | AdminUser)] as {}),
value,
hasError,
error,
nestedKey,
},
},
isFormValid,
}
}
case 'focus_out_input': {
const { name, isFormValid, touched, hasError, error } = data
return {
...state,
[name]: {
...(state[name as keyof FormState] as {}),
touched,
hasError,
error,
},
isFormValid,
}
}
case 'focus_out_nested_input': {
const { touched, isFormValid, nestedKey, name, hasError, error } = data
return {
...state,
[nestedKey]: {
...state[nestedKey],
[name]: {
...(state[nestedKey][name as keyof (Address | AdminUser)] as {}),
touched,
nestedKey,
hasError,
error,
},
},
isFormValid,
}
}
case 'submit_error': {
const { submitErrors } = data
return {
...state,
submitErrors,
}
}
default:
throw new Error(`Unknown action type: ${type}`)
}
}
Here is my Action
Type. I am using unions since there can be multiple shapes for my Action Type.
import { AxiosError } from 'axios'
export type NestedKey = 'address' | 'adminUser'
export interface StatePropObj {
value: string
error: string
nestedKey: NestedKey | boolean
touched: boolean
hasError: boolean
}
export type UpdatedInputActionData = StatePropObj & {
submitErrors?: AxiosError | boolean
isFormValid: boolean
name: string
}
export interface UpdatedNestedInputActionData extends UpdatedInputActionData {
nestedKey: NestedKey
}
export type Action =
| { type: 'updated_nested_input'; data: UpdatedInputActionData }
| { type: 'submit_error'; data: { submitErrors: AxiosError | boolean } }
| { type: 'updated_input'; data: UpdatedInputActionData }
| {
type: 'focus_out_input'
data: {
touched: boolean
name: string
isFormValid: boolean
error: string
hasError: boolean
}
}
| {
type: 'focus_out_nested_input'
data: {
touched: boolean
name: string
isFormValid: boolean
nestedKey: NestedKey
error: string
hasError: boolean
}
}
I am receiving a lot of tsc errors, specifically TS2339
errors:
Property 'value' does not exist on type 'UpdatedInputActionData | { submitErrors: boolean | AxiosError; } | { touched: boolean; name: string; isFormValid: boolean; error: string; hasError: boolean; } | { ...; }'.
49 const { value, hasError, error, name, isFormValid } = data
The error doesn't find the values destructured in the updated_input
and updated_nested_input
case.
I thought that UpdatedInputActionData
would have all props from StatePropObj
and then the additional ones I defined. I am essentially "extending" the type here.
I tried this with an interface and the extension keyword and received the same results. Why am I receiving this typescript error when the value is defined on the type?
Note: I am using TS version: "typescript": "^3.8.3"