2
type padding = [number, number, number, number]

interface IPaddingProps {
  defaultValue?: padding
  className?: string
  disabled?: boolean
  min?: number
  max?: number
  onChange?: (value: number | string) => void
}
interface IFieldSate {
  value: padding
}
export default class FieldPadding extends React.Component<IPaddingProps, IFieldSate> {

  readonly state = {
    value: [0, 0, 0, 0]
  }
  constructor(props: IPaddingProps) {
    super(props)
    if (props.defaultValue) {
      this.state.value = [...props.defaultValue]
    }
  }

}

I get an error at state saying:

Type 'number[]' is missing the following properties from type '[number, number, number, number]': 0, 1, 2, 3?

Sky Clong
  • 141
  • 1
  • 8

2 Answers2

5

Your problem is that state is being inferred as an array instead of as a tuple. There are several ways around this, as detailed in this answer. I'll just present one of them here.

Assuming you are using TypeScript 3.0 or above, you can define the following tuple() helper function which takes any number of arguments and returns a tuple of those arguments:

type Narrowable = string | number | boolean | undefined | null | void | {};
const tuple = <T extends Narrowable[]>(...t: T)=> t;

Then, you can use it inside your FieldPadding class definition:

export default class FieldPadding extends React.Component<IPaddingProps, IFieldSate> {
  readonly state = {
    value: tuple(0, 0, 0, 0) 
  }
}

and that gives value the type [0, 0, 0, 0], a tuple type with four elements of numeric literal type 0.


UPDATE:

So you want state to be readonly but you want to change the values of state.value from 0 to other numbers? ‍

In that case you want value to be type [number, number, number, number], which is wider than [0, 0, 0, 0] but narrower than number[]. You can annotate that yourself, which I'd recommend if you want such strict control over the type:

export default class FieldPadding extends React.Component<IPaddingProps, IFieldSate> {
  readonly state = {
    value: [number, number, number, number] = [0, 0, 0, 0]
  }
}

You could also do

export default class FieldPadding extends React.Component<IPaddingProps, IFieldSate> {
  readonly state = {
    value: tuple(0 as number, 0 as number, 0 as number, 0 as number) 
  }
}

or

const z: number = 0;
export default class FieldPadding extends React.Component<IPaddingProps, IFieldSate> {
  readonly state = {
    value: tuple(z, z, z, z)
  }
}

or even make a less narrowing tuple() function

const wideTuple = <T extends any[]>(...t: T) => t;
export default class FieldPadding extends React.Component<IPaddingProps, IFieldSate> {
  readonly state = {
    value: wideTuple(0, 0, 0, 0) 
  }
}

Any of those should work for you. The short answer to the question as asked, though, is that you need a tuple type, not an array. Exactly how you get that is up to you.


Hope that helps; good luck!

jcalz
  • 264,269
  • 27
  • 359
  • 360
  • thanks, when i write `this.state.value = props.defaultValue `, also get error ` Cannot assign type "number" to type "0"`. – Sky Clong Feb 27 '19 at 07:24
  • Sorry, I didn't realize you were trying to modify the `value` property (the `readonly` nature of `state` made me think you were trying to keep it as four `0` elements). Updated with other ways to get `number` instead of `0`. – jcalz Feb 27 '19 at 13:51
1

Instead of writing value's type as an array with 4 elements of type number, you would write type padding = number[] or type padding = Array<number>

type padding = number[]

interface IPaddingProps {
  defaultValue?: padding
  className?: string
  disabled?: boolean
  min?: number
  max?: number
  onChange?: (value: number | string) => void
}
interface IFieldSate {
  value: padding
}
export default class FieldPadding extends React.Component<IPaddingProps, IFieldSate> {

  readonly state = {
    value: [0, 0, 0, 0]
  }
}
adiga
  • 34,372
  • 9
  • 61
  • 83
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • 1
    but, i want limit the value as an array with 4 elements of type number, when i wirte const pd: [number, number, number, number] = [0, 0, 0, 0] is ok. when state got error. – Sky Clong Feb 23 '19 at 06:06
  • If you want to restrict to only 4 elements, write a function that will check the contents of the array whenever that array is modified. If the length is already 4, don't allow to add by either just skipping the operation or you can throw an exception. – Sagar Agrawal Feb 23 '19 at 06:14
  • @SagarAgrawal No, the OP doesn't want to write a function to check the contents of the array, he wants the typechecker to do its job. – Bergi Feb 23 '19 at 14:35
  • @Bergi - if that is the case, a new method can be added to array using prototype (like addItem) and internally it will call push method of the array. If the count is already 4 and trying to add more items, this method can throw exception. – Sagar Agrawal Feb 23 '19 at 16:02
  • @SagarAgrawal I said *type checker*. An array method doesn't throw errors at compile time. – Bergi Feb 23 '19 at 16:22
  • @Bergi What you said is what I mean. – Sky Clong Feb 27 '19 at 07:31