Type undefined is not assignable error in React Typescript

Last updated : August 22, 2022

The error "Type 'undefined' is not assignable to type 'number'" occur when we try to assign a potentially undefined value to a string or number variable. In the case of a string variable, the error reads as "Type 'undefined' is not assignable to type 'string'"

In Typescript, when you define a variable with the type of string or number, only the defined type can assign to that variable. It is important to note that these type check errors happen when you have the strict mode turned on by setting "strict": true in the tsconfig.json file.

How to type-check optional variables

Let's take a look at a simple example. My interface PersonId defines the id as optional.

interface PersonId {
  id?: number
  name: string
}
export const Person = (props: PersonId) =>{
  const ids: number[] = []
  ids[0] = props.id // Type 'number | undefined' is not assignable to type 'number'
}

Now I am trying to insert the id variable into a number array. But Typescript is smart enough to figure that the id variable may potentially be undefined. My ids array only accepts numbers.

Solution 1

I can check the id to see if it is not undefined and then assign the value to the array.

!!props.id && (ids[0] = props.id)

Solution 2

Doing the same thing, but with better readability.

if(!!props.id){
    ids[0] = props.id
}

Solution 3

I can use the non-null assertion operator to convince Typescript that my id cannot be null. However, I consider this a workaround, not a good practice. That is not a guaranteed fix, as Eslint can restrict using non-null assertions. Anyways, here is how to do that.

ids[0] = props.id!

The above solutions work on a single variable at a time. What if I have an array of objects that I want to convert into a number or string array?

Object array to string or number array with type safety

Now I receive a very long array of PersonId objects. Here is what my object array looks like.

const idList: PersonId[] = [
  {id: 100, name: "Smith"},
  {id: 101, name: "John"},
  {id: 102, name: "Satoshi"},
  {id: 103, name: "Ravi"}
]

The array is a type of PersonId, where the id is optional.

interface PersonId {
  id?: number
  name: string
}

My hardcoded array guarantees to have all the id variables filled. But Typescript only cares about what the interface guarantees, and the interface does not guarantee the id. It is optional.

interface PersonId {
  id?: number
  name: string
}
export const Person = (props: PersonId[]) =>{
  const idList: PersonId[] = [
    {id: 100, name: "Smith"},
    {id: 101, name: "John"},
    {id: 102, name: "Satoshi"},
    {id: 103, name: "Ravi"}
  ]
  const idArray: number[] = idList.filter((list) => // Undefined error in this line
     list.id !== undefined
  ).map((id) => id.id)
  console.log(ids1)
}

In the above code, I filter out the idList array to remove the undefined id's and then collect the id's with values to build the number array idArray. But Typescript is not convinced that the map returns only defined ids. I have to tell Typescript explicitly that the map function returns only defined values.

Solution 1

I can use the non-null assertion operator to convince Typescript that the map function only returns defined values. Again, that is not recommended.

const idArray: number[] = idList.filter((list) => 
     list.id !== undefined
).map((id) => id.id!)

Solution 2

I can use the Required type to hint at Typescript that my filter function guarantees to discard undefined values.

const idArray: number[] = idList.filter(
(list: PersonId): list is Required<PersonId> => 
     list.id !== undefined
).map((id) => id.id)
L Raney
By: L Raney
Lance is a software engineer with over 15 years of experience in full-stack software development.
Read more...

Comments are disabled

No Comments