Converting types conditionally in TypeScript depending on their names

Having developed my own TypeScript ORM, I've implemented a unique approach where the model classes used for INSERT operations are distinct from the ones utilized for SELECT queries in order to maintain immutability...

  • The INSERT models contain optional properties, such as fields that will be automatically filled by the database using default values or SQL TRIGGERs.
  • In contrast, SELECT models do not include any optional properties; they always consist of a scalar value or null, never undefined.

For instance, if we have two SQL tables - user and blog - this results in separate models:

class Insert_user {
    readonly id: string;
    readonly username: string;
    readonly joined_at?: string; // Optional due to default value set by SQL during INSERT

    constructor(props: Insert_user) { Object.assign(this, props); Object.freeze(this); }
}
class Select_user {
    readonly id: string;
    readonly username: string;
    readonly joined_at: string; // Guaranteed presence when fetching an existing record

    constructor(props: Select_user) { Object.assign(this, props); Object.freeze(this); }

}
class Insert_blog {
    readonly id: string;
    readonly blog_title: string;
    readonly view_count?: number; // Optional because of default value set by SQL during INSERT

    constructor(props: Insert_blog) { Object.assign(this, props); Object.freeze(this); }
}
class Select_blog {
    readonly id: string;
    readonly blog_title: string;
    readonly view_count: number;  // Always present when retrieving an existing record

    constructor(props: Select_blog) { Object.assign(this, props); Object.freeze(this); }

}

I aim to create multiple functions capable of accepting "Insert" models, with the typing system correctly inferring the corresponding "Select" model based on the input. For example:


type AnyInsertModel = Insert_user | Insert_blog;
type AnySelectModel = Select_user | Select_blog;

function takeAnInsertModelButReturnItsSelectModel(insertModel: AnyInsertModel) {
    // Data from insert model is inserted into SQL database
    // Once the INSERT operation is completed, data is selected accordingly 
    const selectModel = {/* Data retrieved through SELECT */} as Select_???;
}


/**
 * The output variable below should have type Select_user
 */
const selectedUser = takeAnInsertModelButReturnItsSelectModel(new Insert_user({id: 'd110ec70-9a16-4ad0-a73b-82e241a054eb', username: 'Neo'}));

/**
 * The output variable below should have type Select_blog
 */
const selectedBlog = takeAnInsertModelButReturnItsSelectModel(new Insert_blog({id: '2068bc9d-f19d-4043-a13a-6af4b2207be2', blog_title: 'I like milk'}));

I want the types to be determined simply from function arguments without necessitating redundant generics for each function call (though generics in function definitions are acceptable), given that the argument already carries the appropriate type information.

It seems like I might have found a solution myself (see answer below), but I'm open to exploring alternative approaches too.

Answer №1

Exploring various methods while crafting this, I stumbled upon"conditional types" which seems tailored for this specific situation:

type InsertToSelect<T extends AnyInsertModel> =
         T extends Insert_user ? Select_user :
         T extends Insert_blog ? Select_blog :
         never

function captureAnInsertModelAndReturnItsCorrespondingSelectModel<InputGeneric extends AnyInsertModel>(insertArg: InputGeneric): InsertToSelect<InputGeneric> {
    const selectedModel = someCodeThatInsertsAndThenSelects();
    return selectedModel as InsertToSelect<InputGeneric>;
}

I'm open to suggestions on any alternative methods that could have been effective in this scenario.

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

Exploring the potential of sharing a library folder in a monorepo with Nest.js and Angular.js

I am currently facing an issue while trying to share models and functions between two separate typescript projects - nest.js and angular.js. To initiate this process, I have set up boilerplate nest.js and angular.js projects in folders named api and ui res ...

Unleashing the power of await with fetch in post/get requests

My current code has a functionality that works, but I'm not satisfied with using it. await new Promise(resolve => setTimeout(resolve, 10000)); I want to modify my code so that the second call waits for the result of the first call. If I remove the ...

Is there a way to assign a value to an Angular-specific variable using PHP?

In the development of my Angular 4 application, I encountered an issue while receiving JSON data based on an id value through a PHP script. Upon examining the code, it seems that there should be a value passed into this.PropertiesList. examineProperties(i ...

The AppModule has imported an unexpected value of '[object Object]', causing errors

How can I properly import the Pipe Module into my Angular app? import { NgModule } from '@angular/core'; import { PipeTransform, Pipe } from '@angular/core'; @Pipe({ name: 'values', pure: false }) export class CustomPipe im ...

Creating a dropdown menu using Bootstrap and Angular

I am struggling to get a dropdown menu to display on my app. Despite trying various solutions, nothing seems to be working. Below is the code snippet from my angular.json file: "styles": [ "src/styles.css", ...

A single click is required for Observables to load on an HTML page

While working on my Angular web application, I encountered an issue with displaying data when using Observables and Subjects. Typically, when searching the Firebase DB, I use *ngFor="let myvar of _myvar | async" in my HTML to display the retrieve ...

Tips for effectively handling the response of a post request in Angular 5

Hi there, I've hit a roadblock with a login situation. It's supposed to send this data using a post method: POST URL: /user/login?_format=json Header: Content-Type: application/json POST data: { "name": "username", "pass": "password" } ...

Expressions without a call signature cannot be invoked

When using an adapter in the given example, I encountered a type error specifically on the last line of the getGloryOfAnimal method. Despite having clearly defined types, I am puzzled by this issue. interface ICheetah { pace: string; } interface ILio ...

Special characters, such as Unicode emoji, may not be displayed correctly in certain browsers and appear

Attempting to incorporate emoji into a dropdown (select tag) menu in an Angular 6 project using unicode has been challenging. The browser only displays certain emojis like ♥, but others such as 😄, 😐, and 😡 appear as empty squ ...

The generic parameter is extending a type but is being used in a contravariant position, causing TypeScript to struggle to unify it

When developing my functions, I aim to provide flexibility for consumers to use a wider type. However, I encounter issues when the type is used in a contravariant position and TypeScript raises complaints. Here is the simplified code snippet: function wra ...

The Angular BehaviorSubject observable is returning a null value

I am experiencing an issue where I pass data through components using behavior subject. However, when I try to retrieve it with subscribe, it shows null even though the service returns a real value. To reproduce the issue, you can check out the code here: ...

What is the method for retrieving all documents that contain an array field with at least one object-element having a property value of 'X'?

I have a collection of MongoDB documents structured like this: { "group": "P32666", "order": [{ "_id": { "$oid": "5e8e9b40e7999f6b90fd88bf" }, "name": "Dmitriy A", "login": "example", "password": "example", "email": "exampl ...

Material UI Grid has a problem with inconsistent column width when using direction='column' and flexWrap='wrap' attribute combination

I'm a newcomer to React and Frontend development in general. Currently, I am working on setting up a MUI Grid that organizes items in columns which wrap around when necessary. So far, I've been able to accomplish this by adjusting the direction a ...

converting nested object structures in typescript

I'm trying to flatten a nested object in my Loopback and Typescript controller Here's the structure of my model : export class SampleModel { id: number; code: number; guide?: string; gradeData?: string; } Take a look at this example obj ...

Switching Facebook accounts on Firebase

I'm currently working on an Angular2 App that utilizes Firebase as its User system, with authentication providers including Email + Password, Facebook, and Google. One issue I have encountered is that when logging in with Facebook, I am unable to swi ...

Which is the more accurate calculation based on intuition: multiplying blabla by 255.99999999999997 and casting as an integer, or rounding the product of blabla

Recently, while browsing through webkit sources, I stumbled upon an interesting find regarding color conversions from hsl to rgb: const double scaleFactor = nextafter(256.0, 0.0); // it seems like it's approximately 255.99999999999997 // .. some co ...

What is the best method for saving data from a reactive form to local storage?

I'm looking for a way to store reactive forms data in local storage and then access that data on another page. <form [formGroup]="testform" > firstname: <input type="text" formControlName="fname"><br> lastname: ...

Creating a universal type for an arrow function in Typescript

I like to write my TypeScript functions in a functional style. When it comes to simple functions, I usually do something like this: type A = (value: number) => string; const a: A = value => value.toString(); But when it comes to using generic type ...

Hierarchy-based state forwarding within React components

As I embark on the journey of learning Typescript+React in a professional environment, transitioning from working with technologies like CoffeeScript, Backbone, and Marionettejs, a question arises regarding the best approach to managing hierarchical views ...

What is the best way to establish a connection between a child and parent component using a click event?

I am working on a scenario where I have two components interacting with each other. The parent component features a button, and upon clicking this button, the child component is disabled while also opening up to display its own button for closing. How can ...