typescript 类型“never”上不存在属性“roleName”

ubof19bj  于 2022-12-30  发布在  TypeScript
关注(0)|答案(1)|浏览(618)
// ** React Imports
import { useEffect, useState } from 'react'

// ** MUI Imports
import Box, { BoxProps } from '@mui/material/Box'
import Button from '@mui/material/Button'
import Drawer from '@mui/material/Drawer'
import FormControl from '@mui/material/FormControl'
import FormHelperText from '@mui/material/FormHelperText'
import IconButton from '@mui/material/IconButton'
import InputLabel from '@mui/material/InputLabel'
import MenuItem from '@mui/material/MenuItem'
import Select from '@mui/material/Select'
import { styled } from '@mui/material/styles'
import TextField from '@mui/material/TextField'
import Typography from '@mui/material/Typography'

// ** Third Party Imports
import { yupResolver } from '@hookform/resolvers/yup'
import { Controller, useForm } from 'react-hook-form'
import * as yup from 'yup'

// ** Icon Imports
import Icon from 'src/@core/components/icon'

// ** Store Imports
import { useDispatch, useSelector } from 'react-redux'

// ** Actions Imports
import { addUser } from 'src/store/apps/user'

// ** Types Imports
import { AppDispatch, RootState } from 'src/store'
import { fetchDesignation } from 'src/store/apps/designation'
import { fetchRole } from 'src/store/apps/role'

interface SidebarAddUserType {
  open: boolean
  toggle: () => void
}

interface UserData {
  firstName: string
  middleName: string
  lastName: string
  loginId: string
  emailId: string
  mobileNumber: string
}

const showErrors = (field: string, valueLen: number, min: number) => {
  if (valueLen === 0) {
    return `${field} field is required`
  } else if (valueLen > 0 && valueLen < min) {
    return `${field} must be at least ${min} characters`
  } else {
    return ''
  }
}

const Header = styled(Box)<BoxProps>(({ theme }) => ({
  display: 'flex',
  alignItems: 'center',
  padding: theme.spacing(3, 4),
  justifyContent: 'space-between',
  backgroundColor: theme.palette.background.default
}))

const schema = yup.object().shape({
  emailId: yup.string().email().required(),
  mobileNumber: yup
    .string()
    .typeError('Mobile Number field is required')

    // .min(10, obj => showErrors('Contact Number', obj.value.length, obj.min))
    .required(),
  firstName: yup
    .string()

    // .min(3, obj => showErrors('First Name', obj.value.length, obj.min))
    .required(),
  middleName: yup.string(),

  // .min(3, obj => showErrors('First Name', obj.value.length, obj.min))
  lastName: yup
    .string()

    // .min(3, obj => showErrors('First Name', obj.value.length, obj.min))
    .required(),
  loginId: yup
    .string()

    // .min(3, obj => showErrors('Username', obj.value.length, obj.min))
    .required()
})

const defaultValues = {
  firstName: '',
  middleName: '',
  lastName: '',
  loginId: '',
  emailId: '',
  mobileNumber: ''
}

const SidebarAddUser = (props: SidebarAddUserType) => {
  // ** Props
  const { open, toggle } = props

  // ** State
  const [plan, setPlan] = useState<string>('basic')
  const [roleId, setRoleId] = useState<string>('')
  const [reportsTo, setReporterTo] = useState<string>('')
  const [departmentId, setDepartmentId] = useState<string>('')
  const [designationId, setDesignationId] = useState<string>('')

  // ** Hooks
  const dispatch = useDispatch<AppDispatch>()
  const store = useSelector((state: RootState) => state.role)
  const designations = useSelector((state: RootState) => state.designation)

  useEffect(() => {
    dispatch(fetchRole({}))
    dispatch(fetchDesignation({}))
  }, [])

  const {
    reset,
    control,
    setValue,
    handleSubmit,
    formState: { errors }
  } = useForm({
    defaultValues,
    mode: 'onChange',
    resolver: yupResolver(schema)
  })

  const onSubmit = (data: UserData) => {
    console.log({ ...data, roleId, reportsTo, departmentId, designationId })
    dispatch(addUser({ ...data, roleId, reportsTo, departmentId, designationId }))
    toggle()
    reset()
  }

  const handleClose = () => {
    setPlan('basic')

    // setRole('subscriber')
    // setValue('contact', Number(''))
    toggle()
    reset()
  }

  return (
    <Drawer
      open={open}
      anchor='right'
      variant='temporary'
      onClose={handleClose}
      ModalProps={{ keepMounted: true }}
      sx={{ '& .MuiDrawer-paper': { width: { xs: 600, sm: 450 } } }}
    >
      <Header>
        <Typography variant='h6'>Add User</Typography>
        <IconButton size='small' onClick={handleClose} sx={{ color: 'text.primary' }}>
          <Icon icon='mdi:close' fontSize={20} />
        </IconButton>
      </Header>
      <Box sx={{ p: 5 }}>
        <form onSubmit={handleSubmit(onSubmit)}>
          <FormControl fullWidth sx={{ mb: 6 }}>
            <Controller
              name='firstName'
              control={control}
              rules={{ required: true }}
              render={({ field: { value, onChange } }) => (
                <TextField
                  value={value}
                  label='First Name'
                  onChange={onChange}
                  placeholder='John Doe'
                  error={Boolean(errors.firstName)}
                />
              )}
            />
            {errors.firstName && (
              <FormHelperText sx={{ color: 'error.main' }}>{errors.firstName.message}</FormHelperText>
            )}
          </FormControl>
          <FormControl fullWidth sx={{ mb: 6 }}>
            <Controller
              name='middleName'
              control={control}
              rules={{ required: true }}
              render={({ field: { value, onChange } }) => (
                <TextField
                  value={value}
                  label='Middle Name'
                  onChange={onChange}
                  placeholder='johndoe'
                  error={Boolean(errors.middleName)}
                />
              )}
            />
            {errors.middleName && (
              <FormHelperText sx={{ color: 'error.main' }}>{errors.middleName.message}</FormHelperText>
            )}
          </FormControl>
          <FormControl fullWidth sx={{ mb: 6 }}>
            <Controller
              name='lastName'
              control={control}
              rules={{ required: true }}
              render={({ field: { value, onChange } }) => (
                <TextField
                  value={value}
                  label='Last Name'
                  onChange={onChange}
                  placeholder=''
                  error={Boolean(errors.lastName)}
                />
              )}
            />
            {errors.lastName && <FormHelperText sx={{ color: 'error.main' }}>{errors.lastName.message}</FormHelperText>}
          </FormControl>
          <FormControl fullWidth sx={{ mb: 6 }}>
            <Controller
              name='loginId'
              control={control}
              rules={{ required: true }}
              render={({ field: { value, onChange } }) => (
                <TextField
                  type='text'
                  value={value}
                  label='Login ID'
                  onChange={onChange}
                  placeholder='123456'
                  error={Boolean(errors.loginId)}
                />
              )}
            />
            {errors.loginId && <FormHelperText sx={{ color: 'error.main' }}>{errors.loginId.message}</FormHelperText>}
          </FormControl>
          <FormControl fullWidth sx={{ mb: 6 }}>
            <Controller
              name='emailId'
              control={control}
              rules={{ required: true }}
              render={({ field: { value, onChange } }) => (
                <TextField
                  value={value}
                  label='Email Id'
                  onChange={onChange}
                  placeholder='abc@gmail.com'
                  error={Boolean(errors.emailId)}
                />
              )}
            />
            {errors.emailId && <FormHelperText sx={{ color: 'error.main' }}>{errors.emailId.message}</FormHelperText>}
          </FormControl>
          <FormControl fullWidth sx={{ mb: 6 }}>
            <Controller
              name='mobileNumber'
              control={control}
              rules={{ required: true }}
              render={({ field: { value, onChange } }) => (
                <TextField
                  value={value}
                  label='Mobile Number'
                  onChange={onChange}
                  placeholder='9874561230'
                  error={Boolean(errors.mobileNumber)}
                />
              )}
            />
            {errors.mobileNumber && (
              <FormHelperText sx={{ color: 'error.main' }}>{errors.mobileNumber.message}</FormHelperText>
            )}
          </FormControl>

          <FormControl fullWidth sx={{ mb: 6 }}>
            <InputLabel id='department-select'>Department</InputLabel>
            <Select
              fullWidth
              value={departmentId}
              id='select-department'
              label='Select Department'
              labelId='department-select'
              onChange={e => setDepartmentId(e.target.value)}
              inputProps={{ placeholder: 'Select Department' }}
            >
              <MenuItem value=''>
                <em>Choose a Department</em>
              </MenuItem>
              {store.role.map(p => (
                <MenuItem key={p.id} value={p.roleName}>
                  {p.roleName}
                </MenuItem>
              ))}
            </Select>
            {/* {errors.departmentId && (
              <FormHelperText sx={{ color: 'error.main' }}>{errors.departmentId.message}</FormHelperText>
            )} */}
          </FormControl>
          <FormControl fullWidth sx={{ mb: 6 }}>
            <InputLabel id='designation-select'>Designation</InputLabel>
            <Select
              fullWidth
              value={designationId}
              id='select-designation'
              label='Select Designation'
              labelId='designation-select'
              onChange={e => setDesignationId(e.target.value)}
              inputProps={{ placeholder: 'Select Designation' }}
            >
              <MenuItem value=''>
                <em>Choose a Designation</em>
              </MenuItem>
              {designations.allDesignations.map(p => (
                <MenuItem key={p.key} value={p.key}>
                  {p.value}
                </MenuItem>
              ))}
            </Select>
            {/* {errors.designationId && (
              <FormHelperText sx={{ color: 'error.main' }}>{errors.designationId.message}</FormHelperText>
            )} */}
          </FormControl>
          <FormControl fullWidth sx={{ mb: 6 }}>
            <InputLabel id='reportsTo-select'>Reporter</InputLabel>
            <Select
              fullWidth
              value={reportsTo}
              id='select-reporter'
              label='Select Reporter'
              labelId='reportsTo-select'
              onChange={e => setReporterTo(e.target.value)}
              inputProps={{ placeholder: 'Select Reporter' }}
            >
              <MenuItem value={1}>Reporter1</MenuItem>
              <MenuItem value={2}>Reporter2</MenuItem>
              <MenuItem value={3}>Reporter3</MenuItem>
              <MenuItem value={4}>Reporter4</MenuItem>
            </Select>
          </FormControl>
          <FormControl fullWidth sx={{ mb: 6 }}>
            <InputLabel id='role-select'>Role</InputLabel>
            <Select
              fullWidth
              value={roleId}
              labelId='role-select'
              inputProps={{ placeholder: 'Select Reporter' }}
              id='select-role'
              label='Select Role'
              onChange={e => setRoleId(e.target.value)}
            >
              <MenuItem value=''>
                <em>Choose a Role</em>
              </MenuItem>
              {store.role.map(p => (
                <MenuItem key={p.id} value={p.roleName}>
                  {p.roleName}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
          <Box sx={{ display: 'flex', alignItems: 'center' }}>
            <Button size='large' type='submit' variant='contained' sx={{ mr: 3 }}>
              Submit
            </Button>
            <Button size='large' variant='outlined' color='secondary' onClick={handleClose}>
              Cancel
            </Button>
          </Box>
        </form>
      </Box>
    </Drawer>
  )
}

export default SidebarAddUser

你看到的红线中的id和rolename来自API数据。我没有得到我需要指定id和rolename来自api的东西。同样的事情发生在所有的select元素上。所有的下拉列表都是动态的,其中id和rolename来自api。我将为动态的rolename,id,value,key指定接口
我需要知道我需要在哪里为API结果数据指定接口。
定义RootState的文件。

// ** Toolkit imports
import { configureStore } from '@reduxjs/toolkit'

// ** Reducers
import calendar from 'src/store/apps/calendar'
import chat from 'src/store/apps/chat'
import designation from 'src/store/apps/designation'
import email from 'src/store/apps/email'
import invoice from 'src/store/apps/invoice'
import permissions from 'src/store/apps/permissions'
import role from 'src/store/apps/role'
import user from 'src/store/apps/user'

export const store = configureStore({
  reducer: {
    user,
    role,
    designation,
    chat,
    email,
    invoice,
    calendar,
    permissions
  },
  middleware: getDefaultMiddleware =>
    getDefaultMiddleware({
      serializableCheck: false
    })
})

export type AppDispatch = typeof store.dispatch
export type RootState = ReturnType<typeof store.getState>

src/store/apps/role文件。写入操作的位置

// ** Redux Imports
import {
  createAsyncThunk,
  createSlice
} from '@reduxjs/toolkit'
import {
  Dispatch
} from 'redux'

// ** Axios Imports
import axios from 'axios'

interface RoleParams {
  //   id: number
  //   roleName: string
  //   description: string
  //   ipAddress: string
  //   createdOn: string
  //   updatedOn: string
  //   createdBy: string
  //   updatedBy: string
}

interface Redux {
  getState: any
  dispatch: Dispatch < any >
}

const demo_token =
  'xzy'

// ** Fetch Users
export const fetchRole = createAsyncThunk('appRoles/fetchRole', async(params: RoleParams) => {
  try {
    const response = await axios.get('http://consoleapi.xzy.com/api/Roles', {
      headers: {
        Authorization: `Bearer ${demo_token}`
      }
    })
    console.log(response.data)
    return response.data
  } catch (error) {
    console.error(error)
  }
})

// ** Add User
// export const addUser = createAsyncThunk(
//   'appUsers/addUser',
//   async (data: { [key: string]: number | string }, { getState, dispatch }: Redux) => {
//     const response = await axios.post(
//       'http://consoleapi.xzy.com/api/Account/AddUser',
//       {
//         data
//       },
//       {
//         headers: {
//           Authorization: `Bearer ${demo_token}`
//         }
//       }
//     )
//     dispatch(fetchData(getState().user.params))

//     return response.data
//   }
// )

// ** Delete User
// export const deleteUser = createAsyncThunk(
//   'appUsers/deleteUser',
//   async (id: number | string, { getState, dispatch }: Redux) => {
//     const response = await axios.delete('/apps/users/delete', {
//       data: id
//     })
//     dispatch(fetchData(getState().user.params))

//     return response.data
//   }
// )

export const appRolesSlice = createSlice({
  name: 'appRoles',
  initialState: {
    role: [],
    allRole: []
  },
  reducers: {},
  extraReducers: builder => {
    builder.addCase(fetchRole.fulfilled, (state, action) => {
      state.role = action.payload.data
      // state.total = action.payload.total
      // state.params = action.payload.params
      state.allRole = action.payload.data
    })
  }
})

export default appRolesSlice.reducer
uqzxnwby

uqzxnwby1#

当你在typescript中定义一个数组,没有任何类型或推断类型,它假设数组项的类型是never,所以你必须给你的数组给予一些类型。

interface Role {
  id: string;
  roleName: string;
}

export const appRolesSlice = createSlice({
  ...
  initialState: {
    role: [] as Array<Role>,
    allRole: []
  },
  ...
})

您需要对allRole属性执行相同的操作。

相关问题