繁体   English   中英

在使用默认值对空对象进行解构时,打字稿类型错误?

[英]Typescript type error while destructuring on empty object with default values?

我正在用ReactJS + Typescript编写一个Web应用程序。 我有一个如下定义的功能组件。

我的问题是:在道具中,属性exercise ,父组件传递对象,无论是初始化空的,或者我指定,某种类型的Exercise 然后Typescript引发以下错误:

[ts] Property 'description' does not exist on type '{} | Exercise'
[ts] Property 'title' does not exist on type '{} | Exercise'

我如何重构它,以便如果对象确实为空,它将使用默认值,否则使用传递的值?

编辑:添加了我使用的其他道具

type Exercise = {
  description: string
  id: string
  muscles: string
  title: string
}

type Props = {
  category: string
  children?: never
  exercise: {} | Exercise
  exercises: Array<[string, Exercise[]]>
  onSelect: (id: string) => void
}

const Exercises = ({
  exercises,
  category,
  onSelect,
  exercise: {
    description = 'Please select an exercise',
    title = 'Welcome!'
  }
}: Props) => (
   <Grid container>
     <Grid item sm>
       {/* Some stuff going on with exercises, category and onSelect */ }
     </Grid>
     <Grid item sm>
       <Paper>
         <Typography variant="h4">{title}</Typography>
         <Typography variant="subtitle1">{description}</Typography>
       </Paper>
     </Grid>
   </Grid>
)

我认为类似的东西应该起作用

type Exercise = {
  description: string
  id: string
  muscles: string
  title: string
}

type Props = {
  exercise: Partial<Exercise>
}

const Exercises = (props: Props) => {
    const exercice = {
      description:'Please select an exercise',
      title: 'Welcome!', 
      ...props.exercise
    }

    return (
        <Grid container>
          <Grid item sm>
            <Paper>
              <Typography variant="h4">{exercice.title}</Typography>
              <Typography variant="subtitle1">{exercice.description}</Typography>
            </Paper>
          </Grid>
        </Grid>
    )
}

编辑:对齐代码

因此,总的来说,我认为您的API设计不适用于此组件。 您基本上是在将练习实体误用为某些默认的“欢迎消息”,这很容易导致该组件的使用者使用。

我要做的是在没有练习时提供这些介绍性默认值,但绝对不会使用练习道具来分配这些默认值。

接下来,不要使用{} ,它不是空对象(您可以像下面的https://github.com/Hotell/rex-tils/blob/master/src/guards/types.ts#L39那样定义空对象) 。 在TS 3.0之前,它曾经是底部类型(现在unknown是底部类型)。 这是什么意思? {}可以是null / undefined以外的任何值:

// all of this is valid !
let foo: {} = 1231
foo = true
foo = { baz: 'bar' }
foo = [1,2,3]

另外,如果您真的想支持将“空”非原始数据类型传递给组件,则最好使用null

type Props = {
  category: string
  children?: never
  // Maybe type
  exercise: null | Exercise
  exercises: [string, Exercise[]][]
  onSelect: (id: string) => void
}

无论如何,如果您确实想保持API原样。 您有以下选择:

  1. 提取默认为常量,需要将其强制转换为练习
const defaultExercise = {
  description: 'Please select an exercise',
  title: 'Welcome!',
} as Exercise
  1. 您需要在函数默认参数之外键入狭窄的运动道具,因为在函数参数中这是不可能的
const Exercises = ({ exercises, category, onSelect, exercise }: Props) => {
  // $ExpectType Exercise
  const { title, description } = exercise ? exercise : defaultExercise

  return <>your markup</>
}

现在,尽管这样做有效,但给您错误的假设。 因为您的exercise可能只是部分exercise (如果使用默认设置),则可能会导致运行时错误。 您将需要通过警卫(如果是三元)来进一步缩小类型。

您可以通过一些类型映射在类型级别上改善这种情况:

// $ExpectType  { description: string, title: string, id?: string, muscles?: string }
const defaultExercise = {
  description: 'Please select an exercise',
  title: 'Welcome!',
} as Partial<Exercise> & Required<Pick<Exercise, 'description' | 'title'>>

如果您要在组件中使用idmuscles ,则使用该类型,您将获得正确的类型,因为它们可能是未定义的,从而正确地反映了我们的三元

const { 
  title, //$ExpectType string 
  description, //$ExpectType string
  id, //$ExpectType string | undefined  
} = exercise ? exercise : defaultExercise

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM