【问题标题】:Typescript type error while destructuring on empty object with default values?使用默认值解构空对象时打字稿类型错误?
【发布时间】:2019-02-06 17:02:49
【问题描述】:

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

我的问题如下:在 props 中,对于属性 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>
)

【问题讨论】:

标签: javascript reactjs typescript ecmascript-6


【解决方案1】:

我认为类似的东西应该可以工作

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>
    )
}

编辑:对齐代码

【讨论】:

  • 您可以在练习中明确添加类型。
  • const exercice: Exercice = { description:'请选择一个练习', title: 'Welcome!', ...props }
  • 我修复了它以支持其他道具
  • 我仍然很想知道是否可以将我的 exercise 属性输入为严格的空对象,或者具有我根据需要定义的所有类型的对象。具有可选属性的对象更精确。
  • 到目前为止,我的解决方案是在 props 的类型定义上保留 Partial&lt;Exercise&gt;。然后,在我的组件主体中,我像 const { exercises, category, onSelect } = props 那样解构我的其他道具。要访问和输入 exercise 属性,我使用您的解决方案,在对象上添加 Partial&lt;Exercise&gt; 类型。
【解决方案2】:

所以总的来说,我认为您的 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. Extract 默认为常量,需要转换为练习
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 可能是部分的(如果使用默认值),这可能会导致运行时错误。您需要通过警卫(如果,三元)进行额外的类型缩小。

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

// $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

【讨论】:

  • 非常感谢,我重构了这个组件以及父级传递的内容! Partial&lt;Exercise&gt; &amp; Required&lt;Pick&lt;Exercise, 'description' | 'title'&gt;&gt; 绝对是我的想法,但作为 TS 初学者,我找不到合适的语法。使用三元值进行解构以分配默认值是一个非常巧妙的解决方案。竖起大拇指!
  • 你不认为在 JavaScript 中同时使用 nullundefined 会导致混乱吗?我从 TSLint 文档中读到了它:palantir.github.io/tslint/rules/no-null-keyword我想听到更多关于这个主题的意见,以确定好的做法......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-29
  • 2017-08-04
  • 1970-01-01
相关资源
最近更新 更多