The keys and values within a Typescript Interface can be dynamically determined based on the values of other fields

I'm just starting out with Typescript and I'm curious if there's a way to achieve the following:

I need to define properties that will be sent to Hubspot via two different endpoints. Both APIs require data fields labeled property and name, each with their own set of values. For example:

{
  property: 'email',
  value: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="9beffee8efdbfef6faf2f7b5f8f4f6">[email protected]</a>'
}

Or:

{
  name: 'email',
  value: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a6d2c3d5d2e6c3cbc7cfca88c5c9cb">[email protected]</a>'
}

Furthermore, I want to ensure that only specific fields are being sent. For instance:

export interface HubspotFields {
  newsletter: boolean;
  name: string;
  email: string;
}

Is there a way to create a structure like this?:

export interface Test {
  [either "property" or "name"]: [keyof HubspotFields (value can only be one of the keys from HubspotFields)];
  value: [the type of the corresponding dynamic keyof HubspotFields (string 
 boolean)];
}

In simpler terms:

//This fails
{
  something: 'email',
  value: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e296879196a2878f838b8ecc818d8f">[email protected]</a>'
}
//This fails
{
  property: 'something',
  value: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="572332242317323a363e3b7934383a">[email protected]</a>'
}
//This fails
{
  property: 'email',
  value: 2312
}
//This passes
{
  property: 'newsletter',
  value: true
}
//This passes
{
  name: 'email',
  value: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e094859394a0858d81898cce838f8f">[email protected]</a>'
}

I hope my goal is clear. If not, feel free to ask for more details. Thank you!

Answer №1

If you're looking for specific types to achieve your goal, consider the following:

// Defining a type that ensures the return type is either boolean or string
type StringOrBoolReturn = {
  [K: string]: boolean | string;
}

export interface HubspotFields extends StringOrBoolReturn {
  newsletter: boolean;
  name: string;
  email: string;
}

// Defined a type that supports either property XOR name
type ApiData<K extends keyof HubspotFields = keyof HubspotFields> = {
  property: K;
  name?: never;
  value: HubspotFields[K];
} | {
  property?: never;
  name: K;
  value: HubspotFields[K];
}
function sendApiData<K extends keyof HubspotFields>(data: ApiData<K>) {/*... logic ...*/}

sendApiData({ something: 'email', value: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="7e0a1b0d0a3e1b131f1712501d1113">[email protected]</a>' }) // ✖
sendApiData({ property: 'something', value: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e692839592a6838b878f8ac885898b">[email protected]</a>' }) // ✖
sendApiData({ property: 'email', value: 2312 }) // ✖
sendApiData({ name: 'email', property: "email", value: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="bcc8d9cfc8fcd9d1ddd5d092dfd3d1">[email protected]</a>' })  // ✖
sendApiData({ property: 'newsletter', value: true }) // ✔
sendApiData({ name: 'email', value: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="780c1d0b0c381d15191114561b1715">[email protected]</a>' })  // ✔
sendApiData({ name: 'email', value: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="deaabbadaa9ebbb3bfb7b2f0bdb1b3">[email protected]</a>' })  // ✔

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

Experiencing an issue with mui/material grid causing errors

An error occurred in the file Grid2.js within node_modules/@mui/material/Unstable_Grid2. The export 'createGrid' (imported as 'createGrid2') could not be found in '@mui/system/Unstable_Grid' as the module has no exports. Desp ...

Leverage TypeScript to enforce the value of a property based on the keys of another property

The issue at hand is illustrated in the following example: type ExampleType = { properties: { [x: string]: number; }; defaultProperty: string; }; const invalidExample: ExampleType = { properties: { foo: 123, }, defaultProperty: "n ...

Exploring the usage of asynchronous providers in NestJS

I am currently utilizing nestjs and I am interested in creating an async provider. Below is my folder structure: . ├── dist │ └── main.js ├── libs │ └── dma │ ├── src │ │ ├── client │ ...

Customize YouTube iframe styles in Angular 4+ with TypeScript

Has anyone been successful in overriding the style of an embedded YouTube iframe using Angular 4+ with TypeScript? I've attempted to override a CSS class of the embed iframe, but have not had any luck. Here is the URL to YouTube's stylesheet: ...

displaying variables in tooltips along with their translations

Could you please assist me with this tooltip issue? It seems to be malfunctioning. <div class="btn-edit-nounderline" matTooltipClass="custom-tooltip" (click)="edit(row.widgetAccess)" title="{{getTitle(row.widgetAccess ...

The process of invoking the parent class's Symbol.iterator function from the child class's Symbol.iterator can be achieved by following a specific

I have two TypeScript classes defined below. class BaseIter { constructor(public a: number, public b: number, public c: number, public d: number){} *[Symbol.iterator](): Iterator<number> { yield this.a yield this.b yield this.c y ...

Solving runtime JavaScript attribute issues by deciphering TypeScript compiler notifications

Here is a code snippet I am currently working with: <div class="authentication-validation-message-container"> <ng-container *ngIf="email.invalid && (email.dirty || email.touched)"> <div class="validation-error-message" *ngIf=" ...

Issue TS2322 presents itself when attempting to assign a value of type 'string, number, or Date' to a variable of type 'Date' after upgrading to Angular 16

I recently upgraded my project from Angular 11 to Angular 16 and encountered an issue with the DTO models generated using the NPM package "ng-swagger-gen" from the Swagger JSON file of the Web API. In my C# class on the Web API side, I have a DateTime fiel ...

Customize Monaco Editor: Implementing Read-Only Sections

I am currently working on setting up the Monaco Editor so that specific sections of the text content are read-only. Specifically, I want the first and last lines to be read-only. See example below: public something(someArgument) { // This line is read-onl ...

Encountered an error during npm installation: Fetch Package Metadata error occurred while attempting to request from http://registry.npmjs.org/concurrently, the cause being a socket hangup

I am encountering the following errors: "An unexpected fetchPackageMetaData error occurred while making a request to http://registry.npmjs.org/concurrently failed due to a socket hang up." I am currently connected through a corporate proxy with the firew ...

Create an array with individual key-type pairs for each generic element, then iterate through the array

Consider the enum and type declarations in the code below: enum MyEnum { FIRST, SECOND }; type MyType = { firstKey: string | null, secondKey: boolean, thirdKey: MyEnum } Next, a variable is declared using the type as follows: let glob ...

Navigating through React Native with TypeScript can be made easier by using the proper method to pass parameters to the NavigationDialog function

How can I effectively pass the parameters to the NavigationDialog function for flexible usage? I attempted to pass the parameters in my code, but it seems like there might be an issue with the isVisible parameter. import React, { useState } from 'rea ...

Implement code to execute exclusively on the initial success of react-query

I have a unique scenario where I need to utilize standard useQuery behavior, while also executing a piece of code only on the initial onSuccess event. Although I understand that I can accomplish this using useRef, I am curious if there is an alternative a ...

Utilizing Angular Dependency Injection for Extending Base Services with Subclasses

My service setup includes a base service and two services that inherit from it: @Injectable({ providedIn: 'root' }) export class BaseService { foo(src?: string){ return `speaking from ${src || 'BaseService'}`; } } @Injectable ...

"Storing a collection of PDF files in an array in TypeScript Angular - A step-by-step

Here we have an HTML code snippet that includes an input file element with Angular: <input type="file" class="btn btn-info" id="archivoPDF" #PDFfile value="Seleccionar PDF(s)" accept="application/pdf" multiple /> And this is the TypeScript code sni ...

Issue with assigning objects to an array

I'm currently working on a TypeScript application and I've run into an issue with assigning values. Here are the interfaces for reference: export interface SearchTexts { SearchText: SearchText[]; } export interface SearchText { Value: st ...

Ways to troubleshoot and resolve the error message "SyntaxError: Cannot use import statement outside a module"

In the process of creating my inaugural React Typescript JSX component npm package, I encountered a conundrum. I transplanted operational code from a CRA TypeScript project into a vacant directory, supplementing it with the subsequent package.json and tsc ...

The value specified as type '{ value: BigNumber; }' cannot be assigned to the parameter type 'Overrides & { from?: string | Promise<string> | undefined; }'

I am currently working on a smart contract using Solidity (version 0.8.0) at my buildspace project. Below is a snippet of my code written in TypeScript (4.5.x)/JavaScript and Node.js 16.13.x: ... const waveContractFactory = await hre.ethers.getContractFact ...

Async/Await function is not behaving as intended

Our current approach involves storing short strings as keys. These keys are linked to longer values, which serve as labels. I am attempting to update the corresponding longer value for each key. However, a problem arises where console.log(record) always ...

Mocking a third-party callback function in Jest for method implementation

Utilizing Nest + Cognito for user authentication in an application, I have a method within my Authentication service that requires testing/mocking: async cognitoRegister(userPool: CognitoUserPool, { name, password, email }: AuthRegisterInput): ...