Home > Net >  How to use fix error "does not exist on type string | interface" or can I use satisfies in
How to use fix error "does not exist on type string | interface" or can I use satisfies in

Time:12-30

I have the next interfaces

interface ICustomImage {
  data: string;
  width: number;
  height: number;
}

type UserImage = string | ICustomImage

interface IUser {
  id: number;
  firstName: string;
  lastName: string;
  image: UserImage
}

And I'm getting the IUser from an API and storing it in a useState like this:

const [user, setUser] = useState<IUser[]>();

But when I try to use the autocomplete this happens instead of showing all methods that a string have or ICustomImage properties

enter image description here

user.image.width //Because I know it comes with width, this error appears

Property 'width' does not exist on type 'string | ICustomImage'. Property 'width' does not exist on type 'string'.

How can I fix this, or make it so that I can use the following

user.image.data or user.image.width or user.image.height

CodePudding user response:

You have to narrow the type of user.image before accessing its attributes. If you don't you will only be allowed to access the intersected/shared attributes which are toString and valueOf.

You could do something like:

const someNumber = typeof user.image === 'string' ? user.image.length : user.image.height

CodePudding user response:

Typescript is complaining because user.image can be of type string, so you can't access the width property. You'll need to use the in Keyword to check for types in TypeScript.

if ("width" in user.image) {
    const width = user.image.width; // should be ok
}

As you'll have to check multiple properties (height, data, etc) you can define a type guard function.

If you're absolutely sure it'll be of type ICustomImage then you can cast the value as follow :

const image = user.image as ICustomImage
const width = image.width;

Regarding your state name, it should be plurals (because it's an array of user).

// Multiple users
const [users, setUsers] = useState<IUser[]>();
// Single user
const [user, setUser] = useState<IUser>();

CodePudding user response:

Typescript has no way to know whether the image is a string or your interface object. You haven't narrowed it down for the interpreter and need to do so. Write a type guard to handle this:

interface CustomImage {
    data: string
    width: number
    height: number
}


interface User {
    id: number
    firstName: string
    lastName: string
    image: CustomImage | string
}

const hasCustomImage = (object: any): object is CustomImage => {
    return !!object.data
}

const userCustomImage: User = {
    id: 1,
    firstName: "first",
    lastName: "last",
    image: {
        data: 'data',
        width: 1024,
        height: 768
    }
}

const userStringImage: User = {
    id: 2,
    firstName: "first",
    lastName: "last",
    image: 'image'
}

console.log(`typeguard against custom image object: ${hasCustomImage(userCustomImage.image)}`)
console.log(`typeguard against string image: ${hasCustomImage(userStringImage.image)}`)

const dataImage = hasCustomImage(userCustomImage.image) && userCustomImage.image.data || userCustomImage.image
const stringImage = hasCustomImage(userStringImage.image) && userStringImage.image.data || userCustomImage.image


console.log(dataImage)
console.log(stringImage)

Check it out on the playground. You could also do an inverse check where your type guard checks to see if the object is a string but this should give you some direction.

  • Related