Creating a TypeScript optional property that depends on the type of another property

I am working on an interface that has an action property. Depending on the action type, I want to add an optional property to the interface. The current interface looks like this:

const FIGHT = `fight`;
const SWIM = `swim`;
const DANCE = `dance`;

type ActionType =
  | typeof FIGHT
  | typeof SWIM
  | typeof DANCE

interface Creator {
  actionType: ActionType;
  typeId: string;
}

If the actionType is set to SWIM, then we need to include an additional property in the Creator interface, like so:

interface Creator {
  actionType: ActionType;
  typeId: string;
  trained?: string;
}

In my solution, I have created a Creator.

interface CreatorBase {
  actionType: ActionType;
  typeId: string;
}

interface CreatorSwim extends CreatorBase {
  actionType: typeof SWIM;
  trained?: string;
}

export type Creator = 
| CreatorBase
| CreatoeSwim

However, when I try to access the swim property of an object of type Creator, I receive the error TS2339: Property 'trained' does not exist on type 'Creator'.

Answer №1

This is the solution you need:

type ActionType =
  | 'fight'
  | 'swim'
  | 'dane'

interface CreatorBase<TActions> {
  typeId: string;
  actionType:  TActions
}

interface UntrainedCreator extends CreatorBase<Omit<ActionType, 'swim'>>
{
}


interface CreatorSwim  extends CreatorBase<'swim'> {
  trained?: string;
}

export type Creator = 
| UntrainedCreator
| CreatorSwim

const swim: Creator = {actionType: 'swim', typeId : 'i', trained: 'yes'};
const  dane : Creator = {actionType: 'dane', typeId : 'i', };
const  daneError : Creator = {actionType: 'dane', typeId : 'i', trained: ''}; // error

playground

Answer №2

When faced with this scenario, Conditional Types can come to your rescue. By checking if the type of your action is "swim," you can then proceed to map over the desired keys you wish to add.

Take a look at the code snippet below:

type ActionType = "swim" | "run" | "walk"

type Creator <T extends ActionType> = {
    actionType: T
    type: string
} & {[k in T extends "swim" ? 'trained' : never]?: any}

const x: Creator<"swim"> = {
actionType: "run",
type: "swim",
trained: true
}

Answer №3

type ActionType =
  | 'battle'
  | 'dive'
  | 'run'

interface Skilled {
  playerNumber: number,
  type: string
}

interface BaseCreator {
  typeId: string;
  actionType:  ActionType
}

interface UnskilledCreator extends BaseCreator
{
  skilled?: never;
}


interface CreatorDive  extends BaseCreator {
  actionType: 'dive'
  skilled?: Trained;
}

export type Creature = 
| UntrainedCreator
| CreatorDive

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

Facing problem with Angular 7 when making a GET request for non-JSON data

Currently, I am retrieving JSON data from a URL using the following method: this.http.get('http://localhost:3200/mydata').subscribe(data => { console.log(data); }); The response is in JSON format, and everything seems to be working fine. ...

Once the Angular project has been initialized, the Button disable attribute cannot be modified

In my Ionic-Angular project, I am creating registration pages where users input their information in multiple steps. For each step, there is a button that remains disabled until the correct information is entered. An issue arises when transitioning to the ...

Can someone explain why the Next 13 API route is showing up as empty?

I am currently working with Next 13 and I am attempting to create a simple API route. My goal is to have a: "hi" returned when I visit localhost:3000/api/auth. Despite not encountering a 404 error or any errors in the terminal or console, I can&a ...

Can you outline the key distinctions between ref and reactive in Vue.js?

After diving into Vue.js recently, I'm finding myself struggling to completely understand the key distinctions between using ref and reactive. Can someone clarify when it's appropriate to utilize ref versus reactive? ...

Combining TypeScript with Vue3 to implement bootstrapVue

After using BootstrapVue as any, the error was corrected but unfortunately it still doesn't work in the browser. Here is what's inside main.ts: import { createApp }from 'vue'; import App from './App.vue'; import router from & ...

Error: $initialize.t is not a valid method (Vite, Vue 3, TypeScript, vue-i18n)

I've been working on unit testing a simple component that utilizes translations with the vue-i18n module. Here's an overview of the files involved: src/i18n/index.ts import { createI18n } from 'vue-i18n'; export function loadLan ...

Do not include ChangeDetectionStrategy when creating component

Is it possible to eliminate the default ChangeDetectionStrategy for each component creation? (Please note that I am working with Angular V 10 in a controlled environment for project maintenance) @Component({ xyz, changeDetection: ChangeDetectionStrategy. ...

Formatting is not preserved in the SQL Server TEXT data type

When working with a SQL Server 2005 column having a TEXT datatype, I encountered an issue where the formatting of the string containing newlines and tab characters was lost when copied into the database cell. Prior to executing the stored procedure, I ins ...

Managing image elements as attributes in React using styled-components

In my React project, I am working towards eliminating the use of .css files. Currently, I am refactoring existing .css code to utilize styled-components in a Typescript React project. Previously, images were included as backgrounds in a .css class a.item. ...

Error in Firebase Functions: Promises must be properly managed

Currently, I am in the process of creating a Firebase function using TypeScript to deliver push notifications to multiple users. However, whenever I execute the command firebase deploy --only functions, TSLint flags an error stating "Promises must be han ...

Encountering an error with the Typescript 'any' type in Ionic 2

I need help understanding an error I encountered: EXCEPTION: Error: Uncaught (in promise): EXCEPTION: Error in build/pages/search/search.html:17:14 ORIGINAL EXCEPTION: Cannot find a differ supporting object 'function () { return [ { ...

Tips for saving the generated POST request ID for future use in another function

I am facing a challenge where I want to use the ID of a newly created Order as the OrderId for an OrderLine that needs to be created immediately after. After creating the Order, if I log the orderId INSIDE THE SUBSCRIBE METHOD, it shows the correct value. ...

What causes an array to accumulate duplicate objects when they are added in a loop?

I am currently developing a calendar application using ExpressJS and TypeScript. Within this project, I have implemented a function that manages recurring events and returns an array of events for a specific month upon request. let response: TEventResponse ...

The condition will be false if a number is present, even if it is zero

I am facing an issue with a class containing an optional field called startDateHour: export class Test { startDateHour?: number; // more fields, constructor etc. } I need to perform an action only if the startDateHour exists: if (test.startDateHour ...

All authentication logic in Angular encapsulated within the service

I am considering moving all the business logic into the auth service and simply calling the method on the component side. Since none of my functions return anything, I wonder if it's okay or if they will hang. COMPONENT credentials: Credentials = ...

Encountering an issue stating "Assignment error: Cannot assign type 'void' to type 'ReactNode'"

I'm attempting to execute the compatiblelist() function compatiblelist() { CompatibleDevicesAPI().then( response => {this.getCompatList(response)} ); } Whenever I try to call {this.compatiblelist()}, an error oc ...

Tips for sending code confirmation in Amazon Cognito identity using nest.js

Having some issues with implementing AWS Cognito login in Nest.js? Check out this helpful guide on token validation: https://medium.com/weekly-webtips/token-validation-with-aws-cognito-and-nestjs-6f9e4088393c. I need to add a feature where users receive ...

Exploring the potential of React with Typescript: Learn how to maximize

Having some difficulties working with Amplitude in a React and Typescript environment. Anyone else experiencing this? What is the proper way to import Amplitude and initialize it correctly? When attempting to use import amp from 'amplitude-js'; ...

Developing and deploying a Typescript Node.js project for a smooth production workflow

What is the best way to set up my distribution build and production deployment workflow for my Node.js server app? Specifically, I am using NestJS API. Here is my current workflow: Commit changes to the production branch Production server with pm2 autom ...

Ensuring the correct type for an object's interface property value

I am currently working on defining a new interface interface SUser { ID: number; NAME: string; MAIL: string; PASSWORD: string; GENDER: number; BIRTHDATE: string; ID_FB: string; CREDIT: number; ID_REFERRAL: number; } My objective is to c ...