Mapped types: Specify mandatory properties depending on whether an array of identical objects includes a specific string value

Can an object property be set to required or optional based on the presence of a specific string in an array within the same object?

type Operator = "A" |  "B"
type SomeStruct = {
    operators: Operator[];
    someProp: string; // this should be required if operators include "A", optional if not
}

// desired result
const structWithA: SomeStruct = {
  operators: ["A", "B"],
  someProp: "" // correct, since operators contains "A", someProp is required
};

const structWithB: SomeStruct = {
  operators: ["B"],
  // currently errors, but desired outcome is that it should not error, since operators does not contain "A"
};

declare const structs: SomeStruct[];

structs.map(struct => {
  if(struct.operators.includes("A")) {
    // someProp is safely accessible
  }

  // since .includes has a narrow type signature, maybe another way to safely access someProp is needed 
})
 

Answer №1

To achieve this, you can apply a generic to SomeStruct and utilize a predicate for type discrimination:

Explore the solution in the TS Playground

type A = 'A';
type B = 'B';
type Operator = A | B;

type SomeStruct<O extends Operator> = { operators: O[] } & (
  Record<'someProp', string> extends infer T ?
    A extends O ? T : Partial<T>
    : unknown
);

const s1: SomeStruct<Operator> = { operators: ['B', 'A'], someProp: '' };
const s2: SomeStruct<Operator> = { operators: ['B', 'A'] }; // Error (2322)
const s3: SomeStruct<A> = { operators: ['A'], someProp: '' };
const s4: SomeStruct<A> = { operators: ['A'] }; // Error (2322)
const s5: SomeStruct<B> = { operators: ['B'] };
const s6: SomeStruct<B> = { operators: ['B'], someProp: '' };
const s7: SomeStruct<A> = { operators: [] }; // Error (2322)
const s8: SomeStruct<A> = { operators: [], someProp: '' };
const s9: SomeStruct<B> = { operators: [], someProp: '' };
const s10: SomeStruct<B> = { operators: [], someProp: '' };

declare const structs: (SomeStruct<Operator>)[];

function includesA (struct: SomeStruct<Operator>): struct is SomeStruct<A> {
  return struct.operators.includes('A');
}

structs.map(struct => {
  if(includesA(struct)) {
    struct.someProp; // string
  }
});

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

Showing elapsed time similar to YouTube in an Angular 8 application

Currently, I am developing an Angular application to replicate certain features found on YouTube by utilizing data fetched from an API. This API provides video timestamps in a string format Each timestamp follows this structure : YYYY-MM-DDTHH:MM:SS For ...

The names of properties in Typescript are determined by the values of the outer type properties

In my project, I have various interfaces (or types) defined as follows: export type simpleValue = string | number | boolean | Date | null; export interface Options { inline?: OptionsItem[] | unknown[]; promptField?: string; selectedValues?: unknown[ ...

NextJS Typescript Player

Encountering an issue during the build process, specifically with the error in audioRef.current.play(). The error message indicates that there is no play function available. I attempted to use an interface but it seems to not accept boolean values. Could ...

Display a single unique value in the dropdown menu when there are duplicate options

Hey there, I'm currently working on retrieving printer information based on their location. If I have multiple printers at the same location, I would like to only display that location once in the dropdown menu. I am aware that this can be resolved at ...

How come a null variable continues to widen to type any even when strictNullChecks is enabled?

According to the TypeScript Documentation, if strictNullChecks is true, there should be no type widening. Also, the typeof nul should be null. let nul = null; // typeof nul = any let undef = undefined; // typeof undef = any Check it out in the Playground ...

The value of Angular Input remains unchanged within a FormArray

I am experiencing an issue with the Sequence No in my PreprocessingForm's FormArray. When I add a new row, the Sequence No does not change as expected. <tr class="mat-row" *ngFor="let dynamic of PreprocessingForm.controls.arithmeticI ...

Ensuring that an object containing optional values meets the condition of having at least one property using Zod validation

When using the Zod library in TypeScript to validate an object with optional properties, it is essential for me to ensure that the object contains at least one property. My goal is to validate the object's structure and confirm that it adheres to a sp ...

Errors encountered when using TypeScript with destructured variables and props not being recognized

I have a function that returns data. The object is structured with properties such as headerMenu, page, content, and footer. These properties are defined in DataProps interface. When I try to destructure the data object using the line: const { headerMenu, ...

What is the best way to include 'SCSS' in the 'rollup' project that I have developed?

I am embarking on the creation of a React UI library and have chosen 'rollup' as my build tool. To enhance the project, I plan to incorporate TypeScript and utilize SCSS for styling. How can I implement SCSS within this setup? You can find the s ...

Error: Attempting to access the 'tokenType' property of an undefined object is not allowed

We encountered an error while attempting to embed a report using the Power BI Angular library. TypeError: Cannot read properties of undefined (reading 'tokenType') at isSaaSEmbedWithAADToken (reportEmbed?navContentPaneEnabled=false&uid=am ...

Angular2 Interactive Modal Pop Up

Here is an example of a modal in HTML code: <app-modal #modal1> <div class="app-modal-header"> header </div> <div class="app-modal-body"> You c ...

TypeScript failing to correctly deduce the interface from the property

Dealing with TypeScript, I constantly encounter the same "challenge" where I have a list of objects and each object has different properties based on its type. For instance: const widgets = [ {type: 'chart', chartType: 'line'}, {typ ...

Determine the conditional type based on the type of another variable

function updateFilterData( mode: 'PaymentType' | 'Origin' | 'Destination', value: string, ) { } I need to modify this function so that when mode is 'Origin' | 'Destination', the value should b ...

Typescript enums causing Safari to display blank screen in Next.js

The website performs well on Chrome and Edge, but encounters difficulties on Safari for iOS. Although all the elements, styling, and scripts load properly, nothing appears on the screen. After spending countless hours debugging, I discovered that the pro ...

Most effective method for initiating model class attributes

Is there a more efficient way to initialize model classes without explicitly defining each member as undefined? The original concept was to be able to simply use super(data); in extended classes. class Model { construct(data: any) { Object.ke ...

Are all components in Next.js considered client components by default?

I have created a Next.js app using the app folder and integrated the Next Auth library. To ensure that each page has access to the session, I decided to wrap the entire application in a SessionProvider. However, this led to the necessity of adding the &apo ...

What could be causing my Angular 7 header to be unresponsive?

I am facing issues with my http, header, and RequestOption in the API. I am working with Angular 7.1 and have tried various methods to resolve the problem without success. The error message I am receiving is: The token is not being passed in the header ...

I'm getting errors from TypeScript when trying to use pnpm - what's going

I've been facing an issue while attempting to transition from yarn to pnpm. I haven't experimented with changing the hoisting settings yet, as I'd prefer not to do so if possible. The problem lies in my lack of understanding about why this m ...

Unlock the full potential of working with TaskEither by utilizing its powerful functionality in wrapping an Option with

After exploring various examples of using TaskEither for tasks like making HTTP requests or reading files, I am now attempting to simulate the process of retrieving an item from a database by its ID. The possible outcomes of this operation could be: The i ...

Challenges arise with dependencies during the installation of MUI

[insert image description here][1] I attempted to add mui styles and other components to my local machine, but encountered a dependency error. How can I resolve this issue? [1]: https://i.stack.imgur.com/gqxtS.png npm install @mui/styles npm ERR! code ERE ...