Angular 4 error: Property 'XX' does not exist in the type '{xx, xx}'

As a newcomer to this particular type of scripting, I have developed a model class which looks like this:

export class Project {
     constructor(
          public projectId: number,
          public description: string,
          public startDate: Date,
          public endDate: Date
     ) {}
     getDaysRemaining() {
          var result = this.endDate.valueOf() - Date.now().valueOf();
          result = Math.ceil(result / (1000 * 3600 * 24));
          return result < 0 ? 0 : result;
     }
}

Furthermore, here is the way I'm initializing it, following an Angular tutorial:

 let PROJECTS: Project[] = [
     { projectId: 1, description: "Sample", startDate: new Date("2016-12-12"), endDate: new Date("2017-1-13") },
     { projectId: 2, description: "Sample 2", startDate: new Date("2016-12-12"), endDate: new Date("2017-1-13") }
];

I intend to use the model in the template in this manner:

{{ project.getDaysRemaining() }}

However, I am encountering the following error:

Property 'getDaysRemaining' is missing in type '{ projectId: number; description: string; startDate: Date; endDate: Date; }'.

Can someone explain why I need to declare the function in TypeScript or if there's something I am overlooking?

Answer №1

Your issue stems from the way a class is instantiated.

Instead of:

 let PROJECTS: Project[] = [
     { projectId: 1, description: "Sample", startDate: new Date("2016-12-12"), endDate: new Date("2017-1-13") },
     { projectId: 2, description: "Sample 2", startDate: new Date("2016-12-12"), endDate: new Date("2017-1-13") }
];

you should use:

 let PROJECTS: Project[] = [
     new Project(1, "Sample", new Date("2016-12-12"), new Date("2017-1-13") ),
     new Project(2, "Sample 2", new Date("2016-12-12"), new Date("2017-1-13")
];

Explanation

Project represents a class. getDaysRemaining() is a member of that class. To create an instance of a class, you can utilize new Project(...).

{ projectId: 1, ... } denotes an object that is not an instance of class Project. Hence, all the members are listed as properties within curly braces.

You can determine if { projectId: 1, ... } is an instance of Project by using

{ 
project1: 1, ... } instanceof Project
, resulting in false.

ES6? TypeScript shares many syntax features with modern JavaScript language advancements. Class definitions in ES6 are quite similar to those in TypeScript, excluding types and modifiers since JavaScript lacks static typing.

class Project {

    constructor(projectId, description, startDate, endDate) {
        this.projectId = projectId;
        this.description = description;
        this.startDate = startDate;
        this.endDate = endDate;
    }

     getDaysRemaining() {
          var result = this.endDate.valueOf() - Date.now().valueOf();
          result = Math.ceil(result / (1000 * 3600 * 24));
          return result < 0 ? 0 : result;
     }
}

In ES6, you can call new Project(), leading to undefined for all arguments. However, in TypeScript, this isn't feasible due to missing constructor arguments being checked at compile time. A competent IDE could also perform syntax checks at design time.

To allow optional arguments in TypeScript, you can define them with question marks near the parameters:

 constructor(
      public projectId?: number,
      public description?: string,
      public startDate?: Date,
      public endDate?: Date
 ) { }

Common Issue - fetching data from a source like a web service

If you receive your Project data from a web service, it will likely be deserialized into plain objects like { projectId: 1, ... }. Converting these into Project instances requires additional manual work.

An easy approach involves passing each property individually from one side to another using a factory method:

class Project implements IProject {
    ...
    static create(data: IProject) {
        return new Project(data.projectId, data.description, data.startDate, data.endDate);
    }
    ...
}

You can use an interface (instead of any) for your data to aid with typing and implementation (

class Project implements IProject
):

interface IProject {
    projectId: number;
    description: string;
    startDate: Date;
    endDate: Date;
}

Another possibility is utilizing optional arguments, although this may not suit every scenario:

class Project implements IProject {
     ...
     constructor(
          public projectId?: number,
          public description?: string,
          public startDate?: Date,
          public endDate?: Date
     ) { }

     static create(data: IProject) {
          return Object.assign(new Project(), data);
     }
     ...
}

In this case, the interface would feature optional parameters denoted by the question mark, in line with the above implementation:

interface IProject {
    projectId?: number;
    description?: string;
    startDate?: Date;
    endDate?: Date;
}

Compilation and Interfaces - During transpilation of TypeScript code to JavaScript, interfaces are removed since JavaScript doesn't support interfaces natively. Interfaces in TypeScript serve to provide additional type information during development.

Strive for SOLID Principles - S = Single Responsibility Principle

The current implementation where Project handles both storing data and computing remaining days violates the Single Responsibility Principle. It's advisable to split these responsibilities.

Create an interface to represent Project data structure:

interface IProject {
    projectId: number;
    description: string;
    startDate: Date;
    endDate: Date;
}

Move the getDaysRemaining() function to a separate class such as:

class ProjectSchedulingService {
    getDaysRemaining(project: IProject) {
        var result = project.endDate.valueOf() - Date.now().valueOf();
        result = Math.ceil(result / (1000 * 3600 * 24));
        return result < 0 ? 0 : result;
    }
}

This separation has benefits such as having clear data representation with IProject, enabling additional functionalities in independent classes like ProjectSchedulingService.

It also facilitates working with plain objects instead of instantiating more classes. By casting data to an interface from a web service, developers can operate in a typed manner.

Splitting business logic into multiple classes allows for easier definition of dependencies, maintaining business semantics, mocking dependencies for unit testing, and ensuring individual units remain testable.

In frameworks like Angular, employing multiple injectable services enhances flexibility compared to a monolithic service. This aids in adhering to Interface Segregation Principle.

While these changes may seem minor initially, they contribute significantly to code maintenance over time. Refactoring early on prevents complexity issues, enhances testability, and promotes better code organization.

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

Fetching information from Angular4 Service and populating it into a Material Design Table

Seeking assistance with populating a Material Design table using an Angular4 service. Most examples demonstrate using an array within the component, but I am struggling to find a straightforward example of fetching data from a service. Despite various atte ...

What is the process of applying arguments to a class constructor automatically?

In my code, there is an ES6 class called User and a global function named map(): class User { constructor(public name: string) {} } const map = <T, R>(project: (value: T) => R) => {} Instead of the usual way of calling map like this: map ...

Displaying a dynamic menu using Angular's ngFor loop

I am attempting to create a menu with sub-menus. The desired structure for my menu is outlined below. However, the demo I'm testing is not producing the correct structure. Check out the demo here. "Sub Test": { // Main menu "Example1":"hai",//sub ...

There seems to be an issue with the authorization function in nextauthjs utilizing TypeScript

In my NextJS application utilizing nextAuth with TypeScript, I am encountering difficulties implementing the credentials provider. Below is a snippet from my api\auth\[...nextauth]\route.ts file: CredentialsProvider({ name: 'cre ...

Obtaining a JSON file from Firebase Storage

Is it possible to retrieve a JSON file from Firebase Storage for use without needing to push it back? If so, how can I accomplish this using Angular with Firebase Storage? If not, would the Firebase database be a better option for this scenario? I have ...

``Is it possible to iterate through a collection of objects using a loop?

I am facing an issue with updating a global array that contains objects, where each object includes another array. My goal is to update the main array with values from the arrays within the objects following a specific logic! generalArray = [{name:String, ...

Transform object into data transfer object

Looking for the most efficient method to convert a NestJS entity object to a DTO. Assuming the following setup: import { IsString, IsNumber, IsBoolean } from 'class-validator'; import { Exclude } from 'class-transformer'; export clas ...

Implementing edit hyperlink functionality in Angular 4 using Jquery Datatable

Our Angular project is currently utilizing Jquery Datatable, and the grid is displaying successfully. Now, I am seeking assistance in adding an edit hyperlink at the end of each row. Could someone please guide me on how to achieve this? Below is a snippet ...

Unable to navigate beyond the boundaries of an app

I am currently facing an issue with my authentication service where it redirects to the identity server in case of certain errors. I attempted to achieve this using window.location.href = environment.authCodeFlowIssuer;, however, an error occurred: Ref ...

Unidirectional data flow from the document object model to the variable within the

When working with Angular, there are multiple methods available for binding variables to DOM properties. You can utilize the `{{}}` syntax, `[property]=expression`, or implement two-way ngModel binding. One aspect that I have not yet discovered is how to ...

Function in Typescript that can return multiple data types

I recently started learning about TypeScript and its concepts. During my practice sessions, I encountered a problem that left me puzzled. There is a function named `functionA` which returns an object based on the response received from an API. type Combina ...

Transform a 3D text rotation JavaScript file into an Angular component tailored TypeScript file

I have a javascript file that rotates text in 3D format, and I need help converting it into an Angular component specific TypeScript file. You can find the codepen for the original JavaScript code here. Below are my Angular files: index.html <!doctyp ...

Establishing database connection in Angular 2

Being new to Angular 2, I am seeking guidance on how to connect an Oracle database using this framework. I have attempted using Meteor, but found that it only allows connections with MongoDB. However, my requirement is to connect to an Oracle 10G database ...

Factory for TypeScript Objects

I'm currently grappling with developing an object factory in TypeScript that requires all generated objects to share a common base type. However, I'm encountering difficulty in properly defining this requirement. Below is my current approach, wh ...

Instructions for adding a method to a prototype of a generic class in TypeScript

My current challenge involves adding a method to a prototype of PromiseLike<T>. Adding a method to the prototype of String was straightforward: declare global { interface String { handle(): void; } } String.prototype.handle = functi ...

Guide to wrapping column headers in ag-Grid with Angular

I am facing an issue with wrapping column headers in my HTML table. Some columns have four words like Pre Trading Follow Up and Post Trading Follow Up, while others have three words. I attempted to use the following CSS to wrap the text to multiple lines: ...

Creating an observer for a multiple selection dropdown in Aurelia: step by step tutorial

In my current setup, I have a dropdown menu that allows users to select a single option. This selection automatically provides me with the filtering value as an observable. Here is how it works: public months: any=[]; @observable public selectedMonth: ...

Utilizing Typescript to troubleshoot linting issues

After running the TypeScript linter, I received the following error message: Do not use Function as a type. The Function type accepts any function-like value, providing no type safety when calling the function. This lack of specificity can lead to common ...

Angular 5 - Issue with Conditional Validator Functionality Being Ineffective

If the email field is not left empty, then the re-email field must be marked as 'required'. In order to achieve this functionality, I have implemented conditional validators for the re-email field using the code snippet below: HTML <div cla ...

Understanding the attribute types in Typescript React

While working on code using Typescript + React, I encountered an error. Whenever I try to set type/value in the attribute of <a> tag, I receive a compile error. <a value='Hello' type='button'>Search</a> This piece o ...