Trouble retrieving key from props due to variable being passed to function in TypeScript

In my programming scenario, I have defined two distinct interfaces:

interface Cat {
    name: string,
    color: string,
    feedingTimes: number[]
}

interface Dog {
    name: string,
    height: number,
    dateOfBirth: number,
}

Next, I proceeded to create an array named ANIMALS with elements of type Cat and Dog.

const ANIMALS = [{...} as Cat, {...} as Dog]
    

I then utilized a mapping function on the ANIMALS array, invoking the function foo().

ANIMALS.map((animal, index) => {foo(animal, identifier: getIdentifier(index)})

The functions foo() and getIdentifier() are crucial for this operation. getIdentifier(), in particular, returns a string based on the provided index.

getIdentifier = (index: number):string => {...};

const foo = (animal: Cat | Dog) => {
    if(identifier === 'cat') {
        const {name, color, feeding} = animal
    } else {
        const {name, height, dateOfBirth} = animal
    }
}

However, there seems to be an issue at hand as I try to access certain properties within the objects using destructuring assignments like {name, color, feeding} or {name, height, dateOfBirth}. The error message received states:

Property '...' does not exist on type 'Cat | Dog'.

This predicament raises the question: why can't I access these specific attributes? What could be causing this problem?

Answer №1

If the identifier is set to 'cat', then the animal type will be of type Cat

type FooParams = [
    animal: Dog,
    identifier: 'dog'
] | [
    animal: Cat,
    identifier: 'cat'
]

type Foo = (...params: FooParams) => void

const foo: Foo = (animal, identifier) => {
    if (identifier === 'cat') {
        const { name, color, feedingTimes } = animal
    } else {
        const { name, height, dateOfBirth } = animal
    }
}

The code provided does not demonstrate any relationship between the animal and the identifier.

TS Playground

Answer №2

One common misconception is assuming Cat | Dog means "of type cat or of type dogs", when in reality it represents a new type, "Cat | Dog", which is an union of both types.

This doesn't imply that the variable has all properties of both types; rather, it signifies that it can be either type and at compile time, the specific type is unknown.

Therefore, the variable only contains the shared property: "name".

In Typescript, there's a feature that enables the compiler to infer the type based on a switch statement. Here's an example:

 // Interfaces and function declaration omitted for brevity
const ANIMALS = [{name: "cat", color: "red", feedingTimes: [1, 2]} as Cat, {name: "name", height: 2, dateOfBirth: 2} as Dog]

const foo = (animal: Cat | Dog) => {
    switch(animal.typeOfAnimal) {
        case('cat'): 
            return (animal.name, animal.color, animal.feedingTimes)
        case('dog'):
            return (animal.name, animal.height, animal.dateOfBirth)    
    }    
}

foo(ANIMALS[1])
foo(ANIMALS[0])

The same result can also be achieved using if statements:

const foo = (animal: Cat | Dog) => {
    if(animal.typeOfAnimal == 'cat') {
        const {name, color, feedingTimes} = animal
    } else {
        const {name, height, dateOfBirth} = animal
    }
}

Alternatively, you can simply typecast the variable back to its original type.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error encountered when packaging WebAssembly using Rollup

When I use wasm-pack to compile some rust code into webassembly, specifically with the option --target browser (which is the default), these are the files that I see in typescript/deps/ed25519xp: ed25519xp_bg.wasm ed25519xp_bg.d.ts ed25519xp.d.ts ed25519 ...

Creating PropTypes from TypeScript

Currently in my React project, I am utilizing TypeScript along with PropTypes to ensure type checking and validation of props. It feels redundant to write types for both TypeScript and PropTypes, especially when defining components like ListingsList: inte ...

Ways to develop a dynamic Promise-returning Function

I find myself in a situation where I am aware of the type of data that will be returned when making a request to an API. Here is the function I have: const fetchData = async (url: string, options?: Object): Promise<any> => { const res = await f ...

"My NodeJS code is returning a string instead of JSON data when using the MongoDB populate

My current project involves managing a product with multiple providers, each having its own price. The challenge I am facing is that when using populate() to retrieve provider information by ID, it returns the data as a string instead of JSON format. Below ...

Using rxjs takeUntil will prevent the execution of finalize

I am implementing a countdown functionality with the following code: userClick=new Subject() resetCountdown(){this.userClick.next()} setCountDown() { let counter = 5; let tick = 1000; this.countDown = timer(0, tick) .pipe( take(cou ...

TypeScript's version of Java's enum (or C#'s structure)

I'm facing the challenge of creating an enum in Typescript that mimics the functionality of Java enums. In TypeScript, only integer-based enums like C# are supported, unlike in Java where we can have custom objects with non-integer related properties ...

Commitments shatter amidst constructing a website

Utilizing promise and http.get to retrieve data from a JSON API in Wordpress. Once the data is retrieved, it should be displayed on a page... However, an error occurs when attempting to build the page due to the data not being available. What steps can ...

Implementing more stringent type checking in TypeScript instead of utilizing the 'as' keyword

Check out the code snippet below: type DataSets = 'Users' | 'Products' | 'Accounts'; DB.collection('Users' as DataSets).doc(docId).get().then(...) DB.collection('User' as DataSets).doc(docId).get().then(. ...

Is there a way to eliminate the right margin in React?

I am currently working with React to layout three elements below the topElement. My goal is to have these 3 elements fill up the space equally beneath topElement, removing the right-hand gap highlighted in red in the provided image (while keeping the gap a ...

What steps can I take to stop Vetur and TypeScript from displaying duplicate TypeScript warnings in VSCode?

I have a Vue2 project using TypeScript in VSCode with Vetur and TypeScript extensions installed. Whenever there is a TypeScript warning, both the TypeScript and Vetur overlays show duplicate warnings. Example of duplicate warnings Also, the intellisense ...

Issues arising from TypeScript error regarding the absence of a property on an object

Having a STEPS_CONFIG object that contains various steps with different properties, including defaultValues, I encountered an issue while trying to access the defaultValues property from the currentStep object in TypeScript. The error message indicated tha ...

Utilizing Express JS to make 2 separate GET requests

I am facing a strange issue with my Express API created using Typescript. The problem revolves around one specific endpoint called Offers. While performing operations like findByStatus and CRUD operations on this endpoint, I encountered unexpected behavior ...

The issue of login/sign up difficulties arises from the import of AngularFireDatabase in Angular

Code snippet for sidebar service file related to dashboard component: constructor(private db: AngularFireDatabase) {} getUsers(): Observable<any[]> { return this.db.list('/users').valueChanges(); } Code snippet for dashboard compo ...

Building a comprehensive project using Prisma and Next.JS in TypeScript - encountering an issue where the props "Component" and "pageProps" are deemed invalid

I'm currently in the process of developing my very first full-stack application. In this project, I have chosen to use Next.js as my React framework and Prisma as my ORM for handling database access and migrations. Once I established the connection to ...

What is the best way to include bootstrap using webpack?

I am currently building a webapp using Typescript and webpack. I have been able to successfully import some modules by including them in my webpack.config.js file as shown below. However, no matter how many times I attempt it, I cannot seem to import the b ...

Understanding how to link data between sibling elements using typescript

How can I bind a value between two sibling controllers, child1.controller.ts and child2.controller.ts? /* file : index.html |_app.ts |_app.confing.ts (routing with parent.html and invoke parent controller) | |_parent.controller.t ...

Chess.js TypeScript declaration file for easy implementation

Currently, I am delving into learning typescript and have taken up the challenge of crafting a declaration file for the chess.js library. However, it seems that I am struggling to grasp the concept of creating one. Whenever I attempt to import the library ...

"What is the best way to determine the data type of an object retrieved from an API in TypeScript

Hey there, I'm currently developing a web application using Angular 2 and I'm focusing on implementing an exception handling mechanism. To achieve this, I've created a model that mirrors the object structure I will receive from the server (E ...

How can you easily tell if TypeScript code is being executed in a Jasmine/Karma test environment?

In my situation, there exists a static method that generates an object with service-like functionality, which can be accessed through another static method. Here is an example: expect class FooService { private static instance : FooService; static ge ...

Angular - Utilizing NgRx selector for efficient data update notifications

Is there a method to create a "data updated" indicator when I am not interested in the actual updated data itself? Consider a scenario with a reducer: const initialState: SomeReducer = { dataInQuestion: Array<SomeDto>, ... } Following an action ...