javascript - Typescript type error while destructuring on empty object with default values? - Stack Overflow

I'm writing a web application in ReactJS + Typescript. I've a functional ponent defined like

I'm writing a web application in ReactJS + Typescript. I've a functional ponent defined like below.

My problem is the following: in the props, for the property exercise, the parent ponent is passing an object, either initialized empty or of a certain type that I specify, Exercise. Then Typescript raises the following errors:

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

How could I refactor it so that if the object is indeed empty, it will use the default values, and otherwise, use the values passed?

EDIT: Added the other props that I use

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 = 'Wele!'
  }
}: 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>
)

I'm writing a web application in ReactJS + Typescript. I've a functional ponent defined like below.

My problem is the following: in the props, for the property exercise, the parent ponent is passing an object, either initialized empty or of a certain type that I specify, Exercise. Then Typescript raises the following errors:

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

How could I refactor it so that if the object is indeed empty, it will use the default values, and otherwise, use the values passed?

EDIT: Added the other props that I use

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 = 'Wele!'
  }
}: 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>
)
Share Improve this question edited Feb 6, 2019 at 20:03 Théo Lavaux asked Feb 6, 2019 at 17:02 Théo LavauxThéo Lavaux 1,4543 gold badges25 silver badges58 bronze badges 1
  • 1 Related: github./Microsoft/TypeScript/issues/8032 – adiga Commented Feb 6, 2019 at 17:55
Add a ment  | 

2 Answers 2

Reset to default 3

I think something similar to this should work

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: 'Wele!', 
      ...props.exercise
    }

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

edit: align code

So overall I don't think your API design is correct for this ponent. You're basically misusing exercise entity as some default "Wele message stuff", which is rather miss leading to consumers of this ponent.

What I would do, is to provide these intro defaults when there is no exercise present, but would definitely not use exercise prop to assign those defaults.

Next thing, don't use {}, that's not empty object (you can define empty object like following https://github./Hotell/rex-tils/blob/master/src/guards/types.ts#L39 ) . It used to be a bottom type prior to TS 3.0 ( now unknown is bottom type ). What does it mean? {} can be anything except null/undefined:

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

Also if you really wanna support passing "empty" non primitive data types to ponents, prefer null:

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

Anyways if your really wanna keep your API as is. You have following option:

  1. Extract defaults to constant which needs to be cast to Exercise
const defaultExercise = {
  description: 'Please select an exercise',
  title: 'Wele!',
} as Exercise
  1. you need to type narrow exercise prop outside function default parameter, as that's not possible within function params
const Exercises = ({ exercises, category, onSelect, exercise }: Props) => {
  // $ExpectType Exercise
  const { title, description } = exercise ? exercise : defaultExercise

  return <>your markup</>
}

Now while this works it gives you false assumptions. As your exercise might be a partial one (if defaults are used), which may lead to runtime errors. You'll need additional type narrowing via guards ( if, ternary ).

You can improve this situation on type level, by some type mappings:

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

With that type if you would use id or muscles within your ponent, you'll get proper types as they might be undefined, which mirrors correctly our ternary

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

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745442515a4627894.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信