The Kotlin counterpart to the TypeScript Omit utility type

Two data classes in Kotlin are very similar, with the only difference being that one contains an ID field while the other does not (the ID is generated only if the model is stored in the database).

data class RouteWithId(
    val id: String,
    val name: String,
    val description: String,
    val comments: List<Comment>,
    val media: List<Media>,
    val points: List<RoutePoint>,
    val userId: String,
    val status: RouteState,
    val tracks: List<TrackInfo>,
) {
    enum class RouteState(val value: String){
        IN_REVIEW("in-review"),
        PUBLISHED("published");
    }
}
data class Route(
    val name: String,
    val description: String,
    val comments: List<Comment>,
    val media: List<Media>,
    val points: List<RoutePoint>,
    val userId: String,
    val status: RouteState,
    val tracks: List<TrackInfo>,
) {
    enum class RouteState(val value: String){
        IN_REVIEW("in-review"),
        PUBLISHED("published");
    }
}

An attempt was made to combine them with a nullable ID field, which resulted in unnecessary complexity since the ID is expected to exist in many instances (such as post-processing after database retrieval).

In TypeScript, there is a utility type called Omit that can derive a type from another type by excluding certain fields.

This approach would be ideal for this scenario, as it is known that the field will exist after database retrieval and will not exist before that.

Is there a way to achieve the same functionality in Kotlin?

Answer №1

Summary: Achieving the desired outcome "properly" in an Object-Oriented Language like Kotlin may pose challenges.

Solution:

To delve into the core of Object-Oriented Languages, let's explore the concept of Inheritance. (Click here for a detailed explanation)

Inheritance allows objects to establish relationships and subclasses, facilitating reuse of common logic while retaining a distinct hierarchy. By adhering to this principle, developers conduct comprehensive data analysis, streamline development processes, and enhance precision.

According to the essence of Inheritance, a subclass is obligated to inherit all properties of its parent class without discretion. It cannot selectively choose which properties to inherit.

For instance, if the parent class features a property such as Lung Cancer, then the child class must also incorporate the same Lung Cancer property. The child class cannot simply decline inheriting certain properties based on personal preferences.

Furthermore, let's examine the definition of Data class in Kotlin. (Explore the Data Class definition here) Kotlin imposes strict guidelines on Data class, specifying that it cannot have both a parent and child class relationship. Therefore, using Inheritance with Data Class is not feasible. Instead, traditional classes should be employed.

If absolutely necessary, one can resort to creatively utilizing abstract and interface classes in Kotlin to work around this limitation. However, this practice contradicts fundamental OOP principles and warrants reconsideration of modeling systems. (Have you pondered leveraging Optional in Kotlin instead?)

Here's a sample illustration:

//Define an interface enforcing an ID
interface HasID {
    val id: Int
}

//Data Class ensuring presence of ID
data class RouteWithId(val length: Int, override val id: Int) : HasID

//Data Class lacking an ID
data class RouteWithoutId(val name: String)

If you require further clarification, feel free to inquire. Appreciate your attention.

Answer №2

In Kotlin, there isn't direct support for union types, but you can utilize inheritance to simulate this functionality:

class Route(val name: String, val description: String, /* ... */) {
    enum class RouteState(val value: String){
        IN_REVIEW("in-review"),
        PUBLISHED("published");
    }
    
    fun withId(id: Int): DBRoute = DBRoute(id, name, description, /* ... */)
}

class DBRoute(val id: Int, name: String, description: String, /* ... */) : Route(name, description) {    
    fun asRoute(): Route = Route(name, description, /* ... */)
}

This approach allows you to easily convert a Route instance to a DBRoute and vice versa. One downside is that you need to list all fields from Route in the constructor of DBRoute.

If creating a new class seems excessive, you may consider using a Map<Int, Route> instead. However, if you require specific operations involving the ID, a dedicated class would be more suitable.

Answer №3

I have been longing for something like this for quite some time. Unfortunately, I am unable to find a satisfactory solution. One approach that comes to mind is creating a custom annotation library using tools like KAPT or KotlinPoet to automatically generate the required classes. Perhaps an API similar to the following could be implemented:

// generates SimplePerson class with name and age fields
@Pick("SimplePerson", fields=["name", "age"])
// creates AgelessPerson class without the age field
@Omit("AgelessPerson", fields=["age"])
data class Person(val name: String, val age: Int, val heightCm: Int, val weight: Int)

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

How can I import multiple variables in TypeScript?

I have a React application built with TypeScript, and my project directory is structured as follows: App.tsx /pages Page1.tsx The contents of Page1.tsx are shown below: Page1.tsx class PageParams { constructor() { } } class Page1 { co ...

Issues with mat-tab-group not rendering properly after switching between parent tabs

I am facing an issue involving nested tabs and tables in my example. Check out the example here After switching between parent tabs and child tabs, there seems to be an issue where the tabs do not render properly. It takes multiple attempts of switching ...

Exploring ways to ensure robust typing for the body of NextApiRequest within a Next.js environment

Are you trying to figure out how to correctly define the body type of an API POST route in Next.js for better type safety? In NextApiRequest, the body is currently defined as "any" and NextApiRequest itself is not generic. I have tried forcefully assigni ...

Building a frontend and backend using Typescript with a shared folder for seamless integration

I am currently exploring the idea of transitioning to TypeScript, but I am facing challenges in figuring out how to create a shared folder between the frontend and backend. This is the project structure that I have come up with: frontend - src -- server.t ...

What is the best way to create a nullable object field in typescript?

Below is a function that is currently working fine: export const optionsFunc: Function = (token: string) => { const options = { headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, } ...

Conditional Return Types in a Typescript Function

There is a function that can return two different types, as shown below: function doSomething(obj: {a: string, b?: string}): string | number { if (obj.b) { return 'something' } return 1 } When the function is called with an object cont ...

The Vue data retrieved from an API using onMounted() is not initially showing up in the DOM. However, it magically appears after I make changes to the template

Hello and thank you to those taking the time to read this. I am new to Vue, so I may be overlooking something obvious here, but after being stuck for several days, I am reaching out for help. In my SFC file, I have an onMounted function fetching data from ...

The property 1 cannot be added because the object is not extendable in React

Does anyone know what is causing the following issue? I am unable to insert a new object into a key object within my arrays of objects. For example, when I try to insert a new email at index 1 in the 'emails' array, it throws an error stating "ca ...

Step-by-step guide for deploying an Angular 2 CLI app on GitHub

As a front-end engineer, I have limited experience with deployment. Currently, I am working on my pet project using angular-cli. What is the best way to deploy it on GitHub Pages? Are there any other straightforward methods for deployment? ...

TS7053: The element is implicitly assigned an 'any' type as the expression of type 'string' cannot be used to index the type '{ username: string; email: string; '

Having trouble incorporating TypeScript into a custom React Form Component, and I keep encountering an error that I can't seem to resolve. Error message TS7053: Element implicitly has an 'any' type because expression of type 'string&apo ...

Update the class attributes to a JSON string encoding the new values

I have created a new class with the following properties: ''' import { Deserializable } from '../deserializable'; export class Outdoor implements Deserializable { ActualTemp: number; TargetTemp: number; Day: number; ...

Transitioning to TypeScript has brought the promise of imports returning once again

I've been facing some challenges while migrating my extensive project to TypeScript, particularly with handling imports. Being relatively new to programming, I'm unsure if my previous approach was considered bad practice. Previously, I organized ...

Angular encountering the "TS2531: Object may be 'null' within object properties" error

Attempting to store form control values in an object for passing into an onSubmit method, encountered an error when assigning the form control values to the object. The error message TS2531: Object is possibly 'null' appears when trying to access ...

Finding the precise Time zone with date-fns: A comprehensive guide

I've implemented a date pipe using the date-fns library for formatting dates. Here is the code: date.pipe.ts import { Pipe, PipeTransform } from '@angular/core'; import { format } from 'date-fns'; @Pipe({ name: 'formatDate ...

Assigning different data types with matching keys - "Cannot assign type '...' to type 'never'."

I have a question regarding my application, where I am utilizing values that can either be static or functions returning those values. For TypeScript, I have defined the static values along with their types in the following manner: type Static = { key1: ...

How can I exclude *.d.ts files from tslint checking?

Recently, I decided to integrate tslint into my workflow. Following the installation steps, I used the command: npm install tslint tslint-config-ms-recommended --save-dev My configuration file tslint.json now looks like this: { "extends": "tslint-co ...

"Implementing an abstract method in a class by overloading it with a generic type that

// Greetings from the TypeScript Playground, a platform where you can experiment with TypeScript code. type Constructor<T> = new (...args: any[]) => T; class ServiceChecklistResponse { } class AnotherModel { } abstract class AbstractView { ...

A single pledge fulfilled in two distinct ways

My code ended up with a promise that raised some questions. Is it acceptable to resolve one condition with the token string value (resolve(token)), while resolving another condition with a promise of type Promise<string>: resolve(resultPromise); con ...

Is there a way to use a single url in Angular for all routing purposes

My app's main page is accessed through this url: http://localhost:4200/ Every time the user clicks on a next button, a new screen is loaded with a different url pattern, examples of which are shown below: http://localhost:4200/screen/static/text/1/0 ...

How can RxJS be used to handle only the first value returned when calling multiple URLs?

I am faced with the challenge of having multiple URLs containing crucial information. My goal is to find a specific ID within these URLs, but I do not know which URL holds the necessary details. The approach I'm taking involves calling each URL and us ...