Can you suggest a more efficient method for organizing this typescript type declaration?

Recently, I've started incorporating TypeScript into my Node projects and I'm curious if there's a more streamlined and concise way to implement the following:

import { XOR } from "ts-xor";

type _RemoveNull<T> = {
    [P in keyof T] : string;
}

type UserIdParam = {
    a: string;
}

type BudgetIdParam = UserIdParam & {
    b: string | null;
}

type AccountIdParam = _RemoveNull<BudgetIdParam> & {
    c: string | null;
}

type TransIdParam = _RemoveNull<AccountIdParam> & {
    d: string | null;
}

type IdsParam = XOR<XOR<XOR<UserIdParam, BudgetIdParam>, AccountIdParam>, TransIdParam>;

I was looking for a type that would be able to accept any of these example objects:

const a = {a: "1"};
const b = {a: "1", b: "2"};
const c = {a: "1", b: "2", c: "3"};
const d = {a: "1", b: "2", c: "3", d: "4"};

In addition, only the last property of the object can be null, which is why I needed to intersect with the previous type and remove the null from the union. Initially, I attempted to create a union of the four types UserIdParam, BudgetIdParam, AccountIdParam, and TransIdParam. However, after coming across other questions like this one, I opted to use an XOR instead (provided by ts-xor) to achieve my desired result.

I'd love to hear your thoughts on this approach. Thanks!

--

EDIT: as pointed out by @Thomas in the comments, there isn't a defined order for the properties within an object, so there isn't really a "last" one.

Answer №1

After referencing this article, I successfully re-implemented it using generics, mapped types, and conditional types:

type UserIdKeys = "userId";
type BudgetIdKeys = UserIdKeys | "budgetId";
type AccountIdKeys = BudgetIdKeys | "accountId";
type TransactIdKeys = AccountIdKeys | "transactionId";

// introduced for clarity
type AllIdKeys = TransactIdKeys;

type IdParamGroup<T extends AllIdKeys, N extends AllIdKeys> = {
    [P in T]: P extends N ? (string | null) : string;
};

Here's how you can utilize it:

// valid
const trans1: IdParamGroup<TransactIdKeys, "transactionId"> = {
    userId: "1",
    budgetId: "3",
    accountId: "56",
    transactionId: null,
}
// invalid
const budget3: IdParamGroup<BudgetIdKeys, "budgetId"> = {
    userId: "1",
    budgetId: null,
    accountId: "23"
}

Experiment with the code here: TS Playground

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 possibilities of integrating jQuery into Angular 2 using Javascript

import {Component, ElementRef, OnInit} from 'angular2/core'; declare var jQuery:any; @Component({ selector: 'jquery-integration', templateUrl: './components/jquery-integration/jquery-integration.html' } ...

What is the proper way to define the types for the lodash flow function in TypeScript?

lodash.flow is a powerful function that can combine two or more functions to create a new function. For example, using lodash.flow(double, addTwo) would result in a function that doubles a number and then adds two to it. However, when working with TypeScr ...

Executing the setDeleted loop causes changes to the entities which are then reflected in the saveChanges

My goal is to delete a hierarchy of objects: Customer->Orders->OrderItems->OrderItemOptions I attempted to set up a nested loop to perform the operations in the correct order - deleting child records before deleting parent records as required by ...

Exploring the Impact of 2 HostBindings on Class Generation from Inputs in Angular 4

I'm struggling with the code in my component, which looks like this: @Component({ selector: "home", templateUrl: "./home.html" }) export class Home { constructor() {} @HostBinding("class") @Input() public type: string = "alert" @HostBindi ...

Ensure the privacy of your app with Ionic secure storage by prompting the user to establish a personalized lock

I'm running into an issue while trying to set up the secure storage plugin. If initialization fails, it typically indicates that the user hasn't configured a secure lock screen. Following guidelines from the project's GitHub page, I am attem ...

Using Typescript in a definition file requires classes and interfaces to be included in the compiled .js file

I am currently working on a Typescript project that consists of two crucial files: app.ts models.d.ts The initial lines of code in app.ts are as follows: ///<reference path="models.d.ts"/> 'use strict'; import * as fs from 'async-f ...

Load Angular template dynamically within the Component decorator

I am interested in dynamically loading an angular template, and this is what I have so far: import { getHTMLTemplate } from './util'; const dynamicTemplate = getHTMLTemplate(); @Component({ selector: 'app-button', // templat ...

What are some typical ways Generics are used in Typescript?

Trying to comprehend the practical application of Typescript generics in real-life scenarios, beyond just niche cases, is a common challenge. While it's understood that generics provide an additional layer of abstraction to enable reuse of functions/c ...

Modifying column array properties using jsstore

I am working with a jsstore table called tblInvoice const tblInvoice: ITable = { name: "invoice", columns: { // Here "Id" is name of column id: { autoIncrement: true, primaryKey: true, notNull: false }, branchId: { ...

Error code 2769 in Typescript occurs when attempting to transfer process information to the data state in order to utilize it within a modal

I'm encountering an issue while trying to pass a process to a setData state from a .map function in order to display it on a modal. The error message I'm receiving is: "No overload matches this call. Overload 1 of 2, '(props: { compone ...

What is the Kth smallest element in the grid?

We are presented with a grid of dimensions N * N where each element A[i][j] is obtained using the equation (i + j) ^ 2 + (j-i) * 10^5. The task at hand is to identify the Kth smallest element in an optimized manner. Restrictions : 1 <= number of test ...

Issue with Typescript and eslint errors occurring exclusively in fresh code - Anticipated a colon.ts(1005)

Lately, in my extensive react-typescript project, I have encountered a peculiar issue. It seems that syntax errors are popping up everywhere, but only within the new code that I write. For instance, when creating a new component: import React from 're ...

How can you properly expand TypeScript definitions within an external library?

I am currently utilizing TypeScript version 4.5.5 In a third-party library called dva, there is an interface named DvaInstance within the existing index.d.ts file: export interface DvaInstance { // ... } export { connect, connectAdvanced, useSelector ...

What is the reason behind the failure of this RecursivePartial implementation to properly handle Dates?

Exploring the topic of implementing a recursive partial in typescript, a question on Stack Overflow sparked some interesting discussions. The answers provided seemed promising, but upon further examination, the latest answer pointed out their incompletenes ...

The error message "The 'save' property is not found on the 'CardType' type" indicates that there is no 'save'

Within a function, I am manipulating the properties of an object of type CardType. I need to update these changes in my MongoDB database using the document.save() function. However, I have encountered an issue with my code: import { Types } from 'mong ...

Can you explain the functionality of the statement a:bc = 9 in JavaScript?

Currently in my JavaScript code, I have the equation a:bc = 9. Upon executing `console.log(bc)`, it correctly shows 9. However, if I try to `console.log(a)`, I receive an error stating that "a" is not defined. Can someone provide clarification on what i ...

How can we define the types of two arguments in a function using Typescript?

Is there a way to achieve this functionality? type SomeType = { name: string; quantity: number; }; const someFunc = ( keyName: keyof SomeType /* what should be here? */, keyValue: SomeType[keyof SomeType] /* what should be here? */ ) => { // . ...

Error thrown due to missing property in type '{}' when using TypeScript arrow function parameter

As outlined in the documentation for interfaces in TypeScript, An interface declaration serves as an alternative way to define an object type. I'm puzzled by the error I encounter in the following code snippet. My attempt is to restrict the object ...

What are the benefits and drawbacks of utilizing two distinct methods to regulate a component in Vue?

I've developed two components that display "on" or "off" text, along with a button that toggles the state of both components. Here is the link to the codesandbox for reference: https://codesandbox.io/s/serene-mclean-1hychc?file=/src/views/Home.vue A ...

What is the best approach for managing both an object and a string?

My function can accept either a string or an object. If it receives an object, it uses the name property of the object. If it gets a string, it uses the string itself: const foo = (bar: ({ name: string } | string)) => // accepts string or object bar ...