Managing non-mandatory information in a structured domain representation: What's the best approach?

When working on applications or domain models, I often ponder the most effective approach to handling incomplete or optional data. Typed languages like TypeScript and C# offer the advantage of defining models with strict types. While this can be beneficial by enforcing constraints on the data, it becomes challenging when dealing with incomplete real-life data that does not align with these constraints.

Consider a basic data model for a sample application (using TypeScript for the frontend and some backend) where an entity named Project has an id, a name, and an optional description. This data is fetched from the backend.

The frontend defines the data model using interfaces:

export interface IProject {
    id: number;
    name: string;
    description: string;
}

The data is fetched from the backend using the following approach:

export class ProjectService {
    public getProject(projectId: number): Observable<IProject> {
        const url = 'http://server/api/project/' + projectId;
        return this.httpClient.get<IProject>(url);
    }
}

For instance, here is a sample response for a project that includes a description.

{
    "id": 1
    "name": "My first project",
    "description": "My first project is just a demo"
}

In the frontend application, we display the retrieved data. Let's say we want to show the first 10 characters of the project description.

alert(project.description.substr(0,10));

Everything works smoothly so far.

However, now imagine a scenario where the user creates "Project two" without providing a description. In this case, the backend can respond with:

{
    "id": 2
    "name": "Project two"
}

or

{
    "id": 2
    "name": "Project two",
    "description" : null
}

As a result, we encounter a null-reference exception in the frontend: "Cannot read property 'substr' of null". While we can add an if statement to check for null descriptions, this approach would clutter the codebase with numerous checks, potentially masking bugs instead of preventing them.

One possible solution could be to always return a description, even if it is empty when not provided.

{
    "id": 2
    "name": "Project two",
    "description": ""
}

By implementing this change, the frontend no longer needs null checks for descriptions. However, it becomes challenging to differentiate between an intentionally empty description and one that has not been filled in yet.

How should we address these challenges effectively? The example above serves as an illustration, but the issue is applicable in various scenarios within typed languages that struggle with fulfilling a rigid type contract/interface.

Answer №1

C# 8 introduces the use of Nullable Reference Types to explicitly designate the description field as optional1:

class Project
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string? Description { get; set; }
}

In this case, we define Description as a string? - a string that can be null. If we attempt to call a method on Description without first checking if it is null, the compiler will issue a warning.

C# also provides syntax for handling null values. For instance, if you wish to retrieve the first 10 characters of the description but return an empty string if the description is null, you can use the following code:

project.Description?.Substring(0, 10) ?? "";

(Please note that this will raise an exception if the description is not null but is shorter than 10 characters, so additional checking is required in practice.)

It appears that Typescript incorporates similar concepts.


1 This code snippet will trigger a warning because Name is non-nullable but is not initialized. There are several ways to address this: provide a default value of ""; create a constructor for Project that sets Name; temporarily violate the non-nullable contract by assigning null! before the type is deserialized; among other possible solutions.

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

Compiler error occurs when using the Selenium FindsBy attribute

Is the Finder attribute argument valid and usable? I encounter a compiler error stating "'Finder' is not a valid named attribute argument because it is not a valid attribute parameter type" when attempting to use it, such as in the following code ...

The module 'AppModule' is throwing an error with the import of 'MatDialogRef' which is causing unexpected value. To resolve this issue, make sure to include a @

I am currently facing an issue while trying to incorporate Angular Material into my Angular project. Despite successful compilation of the program, I encounter an error when running it in the browser. Uncaught Error: Unexpected value 'MatDialogRef&ap ...

Selenium: The Hunt for the Absent Get Methods

When trying to use the GetAttribute or GetText methods in Selenium, I am unable to find them. For example, there is a Forgotten Password link with instructions above it to click the button if password is forgotten. I wanted to use GetText, but that option ...

Swagger is refusing to cooperate because my model's attributes are set to true

Currently delving into Swagger and its documentation functionality through a YAML file. Previously, I had used @swagger in the controller file and everything worked fine. However, when attempting to transition to a YAML file for better organization, issues ...

Accessing the name and value of an enum in Typescript

One particular enum is causing some confusion: export enum myEnum { name1 = 'my name', name2 = 'my other name', name3 = 'other' } An object of myEnum has been created: const x = myEnum.name1; console.log(x) // prints ...

Unable to add chosen elements to array - Angular material mat select allowing multiple selections

Can anyone assist me in figuring out what I am doing wrong when attempting to push data to an empty array? I am trying to only add selected values (i.e. those with checked as true), but I can't seem to get inside the loop This is the current conditi ...

What is the best way to execute a method in a component for each iteration in an *ngFor loop in Angular 2

Is there a way to call a method for each iteration of *ngFor and pass the iterated variable as a parameter? For example: <li *ngFor="let element of componentModel | keys">{{element.key}}--{{element.value}}</li> Then, in the component, I have ...

Having trouble resolving the typescript package while integrating with another module

Attempting to develop a basic npm package with TypeScript and bundle it using webpack. When trying to use the package in another module, such as a react application named 'module A.' Within module A, the 'myLibrary' package is installe ...

What is the proper way to incorporate generics into a function in TypeScript when you plan to call it using .call()?

interface Wrapped<T> { data: T; } interface BetterWrapper<T> { betterData: T; } function abc<T>(test: Wrapped<T>): BetterWrapper<T> { return {betterData: test.data} } const result = abc<string>.apply({}, { data: ...

Tips for optimizing the performance of nested for loops

I wrote a for loop that iterates over 2 enums, sending them both to the server, receiving a value in return, and then calculating another value using a nested for loop. I believe there is room for improvement in this code snippet: const paths = []; for awa ...

The function record.factory does not exist

Here is the code for the AppComponent: import { Component, OnInit } from '@angular/core'; import { APICommunicationService } from './api-comm/api-communication.service'; import { Observer } from 'rxjs'; @Component({ sel ...

Exploring the process of using ASP.NET Core 2 middleware to encapsulate the response from a GraphQL.NET endpoint

Currently, I have a REST API developed using asp.net web api2 but now I am transitioning to GraphQL.net endpoints with asp.net core 2. One challenge I'm facing is that my existing code uses a Delegating handler to add localization data to the response ...

Utilizing the null conditional operator with JsonConvert.DeserializeObject in a dynamic JSON - a step-by-step guide

Currently, I am utilizing Newtonsoft for the purpose of deserializing a familiar JSON object whereby I extract certain values from it if they exist. The main challenge lies in the fact that the structure of the object may change constantly. Hence, I have ...

Encountering TS2344 error when referring to the Component Ref in Vue.js during typing operations

I received a component reference on my FormCheckbox component from a patternlib. When I tried to incorporate the component into my own TestComp component, I encountered this TypeScript error that left me puzzled: TS2344: Type '{ name: string; mixins: ...

The 'admin' attribute is not found in the 'Object' data type

I have been facing this issue for quite some time now. The backend API response is indicating that a certain property does not exist, even though it clearly does. My Angular application suddenly started showing 18 errors today, and I am at a loss on how ...

Utilizing COM Interop to migrate code from C++ to C#/.NET

Converting from C++ to C#, I have a couple of COM libraries (TISCOPELib and MIPPARAMLib) that I'm working with. This piece of code is functional in C++: TISCOPELib::IFilterBlockCasette *casette; ... initialization ... int position = casette->Posit ...

Ways to verify if a function has completed execution and proceed to invoke another function

I am seeking to verify if a user has chosen an item from the ngFor form and then redirect them to another page upon submitting the form with the updated value. HTML: <mat-select placeholder="Treatment" [(ngModel)]="model.TreatmentA" name="TreatmentA" ...

What is the best way to delete elements from a two-dimensional array using randomly generated criteria?

In the process of developing a small game using XNA consisting of 5 classes, I have balls bouncing around the window, moving at right angles upon collision with the window's sides. The player creates a marquee by dragging the mouse within the window, ...

What is the best way to toggle a card within a collection of cards using Angular?

Wishing you a wonderful day! I simply desire that when I click on a card, only that specific card flips over. But unfortunately, all the cards flip when I click on just one. HTML https://i.sstatic.net/B0Y8F.png TypeScript https://i.sstatic.net/mVUpq.png ...

Leveraging WebStorm's TypeScript support in conjunction with node_modules

Attempting to set up a TypeScript project in WebStorm to import a Node.js module has been a struggle. I made sure to download the necessary TypeScript definition in settings and specified --module commonjs in the compiler settings. However, I keep running ...