Implementing computed properties: A guide to incorporating type setting

I currently have two separate interfaces defined for Person and Dog.

interface Person {
    name: string;
    weight: number;
}

interface Dog {
    name: string;
    mass: number
}

const specificAttribute = isDog ? 'mass' : 'weight';
const playerDetails: Person | Dog = "Object with specific type"

if (playerDetails[specificAttribute] === 0) return;

Encountering the following error message:
TS7053: Element implicitly has an 'any' type due to expression of type '"mass" | "weight"' not being able to index type 'Person | Dog'.

The API response can be either a Dog or a Person based on other conditions. I am seeking the best approach to instruct TypeScript that if the player is of type Person, use 'weight', and if it's of type Dog, use 'mass'. Avoiding the usage of the 'any' type is preferred.

Answer №1

Utilizing TypeScript proves to be advantageous in this scenario: for instance, if the value of attribute is set to "mass", but the object being referenced by player is an instance of a Person, it will generate a valid error as the property mass does not exist within instances of Person.

To circumvent such errors, a change in approach is necessary. One possible solution is to include a check during runtime to verify that attribute actually exists within player. However, I believe it would be more logical to utilize the isDog variable:

const isDog = "mass" in player;
const mass = isDog ? player.mass : player.weight;

if (mass === 0) return;

Give it a try.

Answer №2

Upon reviewing a valuable suggestion from user @jonsharpe in their comment, it is recommended to converge similar functionalities on one property. For example, if both weight and mass convey the same idea, they should be named identically (similar to how both Person and Dog have a name property conveying the same concept). Typically, this can be achieved through objects implementing a common interface, such as Massive.

Here's a way to implement this:

interface Massive {
  mass: number;
}

interface Person extends Massive {
  name: string;
  // other unique properties for Person
}

interface Dog extends Massive {
  name: string;
  // other unique properties for Dog
}

function doSomethingIfHasMass(value: Massive) {
  if (value.mass === 0) return;

  // perform desired action
}

const player: Person | Dog;

doSomethingIfHasMass(player);
// This assignment works without compile errors,
// as `Person | Dog` can be assigned to `Massive`

Give it a try.

This approach should address your issue (however, it's suggested to create a separate interface like Named for the name: string property). Additionally, another function that may be beneficial is the isMassive, which checks if an object conforms to the Massive interface:

function isMassive(value: object): value is Massive {
  return "mass" in value;
}

Now you can pass various objects to your function, and it will gracefully handle mismatches without errors, based on whether the object structure is correct:

function doSomethingIfMassiveAndHasMass(value: object) {
  if (!isMassive(value)) return;

  if (value.mass === 0) return;

  // perform required action
}

declare const something: object;

doSomethingIfMassiveAndHasMass(something);
// No compile errors should occur,
// as only `Massive` objects with non-zero mass will trigger its actions

Experiment with it, also check out an example.

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

Utilizing a TypeScript Variable as a Tagname in an HTML File within Angular

This specific problem is originally documented in this post. Despite being flagged as a duplicate, my scenario differs because the HTML content at hand is too extensive for utilizing innerHTML. The structure of my component's HTML file is as follows: ...

Is it possible to access the line number of a node using the TypeScript compiler API?

Is there a method to retrieve the line number of a node besides using node.pos? For example, something like node.lineNumber? ...

What is the best way to restrict the number of iterations in ngFor within Angular HTML

I want to use ngFor to display a maximum of 4 items, but if the data is less than 4, I need to repeat the loop until there are a total of 4 items. Check out this example <img *ngFor="let item of [1,2,3,4]" src="assets/images/no-image.jpg" styl ...

When trying to access the key value of a dynamically generated object, it returns as undefined

I am facing a challenge with my student object structure... { Freshmen: [{id: 3}, {id: 5}], Sophomores: [{id: 2}, {id: 6}], Juniors: [{id: 1}, {id: 8}], Seniors: [{id: 9}, {id: 4}, {id: 7}] } My goal is to retrieve full student objects from the d ...

What sets apart `lib.es6.d.ts` from `lib.es2015.d.ts` in TypeScript?

I'm a bit confused about ES6 and ES2015. In TypeScript, there are two type declarations available for them: lib.es6.d.ts and lib.es2015.d.ts. Can someone explain the difference between these two? And which one is recommended to use? ...

Angular, manipulating components through class references instead of creating or destroying them

I am exploring ways to move an angular component, and I understand that it can be achieved through construction and destruction. For example, you can refer to this link: https://stackblitz.com/edit/angular-t3rxb3?file=src%2Fapp%2Fapp.component.html Howeve ...

Can someone explain how to create a Function type in Typescript that enforces specific parameters?

Encountering an issue with combineReducers not being strict enough raises uncertainty about how to approach it: interface Action { type: any; } type Reducer<S> = (state: S, action: Action) => S; const reducer: Reducer<string> = (state: ...

Limiting the combinations of types in TypeScript

I have a dilemma: type TypeLetter = "TypeA" | "TypeB" type TypeNumber = "Type1" | "Type2" I am trying to restrict the combinations of values from these types. Only "TypeA" and "Type1" can be paired together, and only "TypeB" and "Type2" can be paired tog ...

Enhance Summernote functionality by creating a custom button that can access and utilize

Using summernote in my Angular project, I am looking to create a custom button that can pass a list as a parameter. I want to have something like 'testBtn': this.customButton(context, listHit) in my custom button function, but I am unsure how to ...

Create a custom component that wraps the Material-UI TextField component and includes default

I have been exploring React and had a question regarding wrapping a component like TextField from mui to add new props along with the existing ones. I attempted to do this but am struggling to figure out how to access the other props. Can anyone help me ...

Creating generic types for a function that builds <options>

I have encountered a situation in my application where I need to loop through an array to construct a list of <option> tags. To streamline this process, I am attempting to create a universal function. function optionValues<T, K extends keyof T> ...

Identifying collisions or contact between HTML elements with Angular 4

I've encountered an issue that I could use some help with. I am implementing CSS animations to move p elements horizontally inside a div at varying speeds. My goal is for them to change direction once they come into contact with each other. Any sugges ...

Tips for maintaining a healthy balance of tasks in libuv during IO operations

Utilizing Typescript and libuv for IO operations is crucial. In my current situation, I am generating a fingerprint hash of a particular file. Let's say the input file size is approximately 1TB. To obtain the file's fingerprint, one method involv ...

A guide on updating a MySQL table using a JSON object in Node.js

I have a JSON Object and need to UPDATE a mySQL table without listing all of the keys individually For an INSERT operation, I use the following code: var arrayValue = Object.keys(obj).map(function(key) { return String("'"+obj[key]+"'"); ...

Navigating with Angular: Every time I refresh the page or enter a specific URL, Angular automatically redirects to the parent route

In my CRM module, I have created a custom Routing Module like this: const routes: Routes = [ { path: 'crm', component: CrmComponent, children: [ { path: '', redirectTo: 'companies', pathMatch: 'full&ap ...

Ways to incorporate extension methods through a "barrel file" - how to do it?

When attempting to define extension methods in separate files and import them through a barrel file, the methods don't seem to be added to the prototype. The following approach works: import './rxjs-extensions/my-observable-extension-1'; i ...

A guide on instantly updating displayed flat/section list elements in React Native

I am in the process of creating a screen called ContactListScreen. The direct child of ContactListScreen is ContactItems, which is a sectionList responsible for rendering each individual ContactItem. However, I have encountered a problem where my ContactIt ...

Conflicting TypeScript errors arise from a clash between React version 16.14 and @types/hoist-non-react-statics 3.3.1

Currently in the process of upgrading an older project to React 16.14, as we are not yet prepared for the potential breaking changes that would come with moving up to React 17 or 18. As part of this upgrade, I am also updating redux and react-redux to ver ...

Are there any methods to incorporate Facebook and Google login into an Ionic progressive web app (PWA)?

After successfully developing an app in Ionic 3 for Android and iOS, I encountered a problem when adding the browser platform. The Facebook and Google login features were not functioning as expected. Despite the assurance from Ionic documentation that the ...

Exploring multiple states within an interval function in React Native

I'm struggling to find the right words for this question. I've encountered an issue where I need to constantly check and update a complex state object within an interval loop in my program. To simplify, let's say it consists of just a counte ...