Can the data type of a property in an interface be altered based on the value of a property within it?

Presented below are the two interfaces:

    export interface IQuestion {
        type: 'single' | 'sort'
        prompt: string;
        options: {
            id: string,
            text: string
        }[]
    }
    export type ICurrentQuestion  = {
        correctAnswerId: string | string[],
        question: IQuestion, 
        extraData?: IResultsAnswerData
    }

I aim to ensure that correctAnswerId is of type string[] when the question type is sort. I prefer not to relocate the type to the top level because the content within the question is directly sent to the client. This would result in duplication of the type at both levels. While my research suggests my desired approach may not be feasible, I am struggling to determine the most effective way to maintain type safety here.

A big thank you in advance to all the brilliant minds out there!:)

Answer №1

If you need to define a type that can be either one thing or another, you should consider using a union type.

One of the types has a correctAnswerId as a string and a question.type property set to "single":

interface SingleCurrentQuestion {
   correctAnswerId: string,
   question: SingleQuestion,
   extraData?: IResultsAnswerData
}
interface SingleQuestion {
   type: 'single'
   prompt: string;
   options: {
      id: string,
      text: string
   }[]
}

The other type has a correctAnswerId as an array of strings and a question.type property set to "sort":

interface SortCurrentQuestion {
   correctAnswerId: string[],
   question: SortQuestion,
   extraData?: IResultsAnswerData
}    
interface SortQuestion {
   type: 'sort'
   prompt: string;
   options: {
      id: string,
      text: string
   }[]
}

To combine these types, you would create a union type like this:

type CurrentQuestion =
   SortCurrentQuestion | SingleCurrentQuestion;

Keep in mind that because it's a union type, it can't be expressed as an interface directly, but must be defined as a type alias. However, for most use cases, this distinction should not matter.

It's also important to note that due to the nested nature of the "sort" versus "single" property inside the question object, the resulting CurrentQuestion type is a union but not a discriminated union. This means that you won't be able to use conditional checks like

if (q.question.type === "single") { ⋯ } else { ⋯ }
to narrow down the type within those blocks. The language does not currently support nested discriminants, but there is an open feature request for this functionality.

Link to interactive code example

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

Can you explain the distinction between Array<string> and string[]?

Can you explain the contrast between Array<string> and string[]? var companies: Array<string> = ['Samsung', 'Sony', 'LG']; var businesses: string[] = ['Lenovo', 'Asus', 'Acer']; ...

Exploring the concept of object inheritance in Angular 5 with Typescript

I'm facing a challenge related to inheritance while building my initial angular 5 application. The error message I encounter is: Property 'message' does not exist on type 'CouponEvent', as reported by the angular-cli. export class ...

The issue with launching a Node.js server in a production environment

I'm currently facing an issue when trying to start my TypeScript app after transpiling it to JavaScript. Here is my tsconfig: { "compilerOptions": { "module": "NodeNext", "moduleResolution": "NodeNext", "baseUrl": "src", "target": " ...

Enhance Express Middleware with Typescript for advanced functionality

Currently in the process of developing a REST API using Express and Typescript, I am encountering difficulties when trying to extend the Request/Response objects of Express. Although my IDE shows no errors, Typescript throws TS2339 Errors during compilati ...

Learning about React and TypeScript: Easy steps to import a component

Here is the structure of my react components in TypeScript: -App.tsx -NewRequestForm.tsx -EmployeeInfo.tsx -AssetInfo.tsx When trying to import EmployeeInfo & AssetInfo in NewRequestForm, only the Prop & State interfaces are visible, not the ...

The young one emerges within the SecurePath component temporarily

Setting up authorization in React has been a priority for me. Ensuring that users cannot access unauthorized pages within the application is crucial. To achieve this, I have created a custom component as shown below. import { ReactNode } from "react&q ...

You cannot use ca.select(....).from function after the code has been minified

My Angular application utilizes squel.js and functions correctly in development mode. However, upon building the app for production and attempting to use it, I encounter the following error message: ca.select(...).from is not a function This error ref ...

Experimenting with Date Object in Jest using Typescript and i18next

I have included a localization library and within my component, there is a date object defined like this: getDate = () => { const { t } = this.props; return new Date().toLocaleString(t('locale.name'), { weekday: "long", ...

Cached images do not trigger the OnLoad event

Is there a way to monitor the load event of my images? Here's my current approach. export const Picture: FC<PictureProps> = ({ src, imgCls, picCls, lazy, alt: initialAlt, onLoad, onClick, style }) => { const alt = useMemo(() => initial ...

Navigating through ionic2 with angularjs2 using for-each loops

I developed an application using IONIC-2 Beta version and I am interested in incorporating a for-each loop. Can anyone advise if it is possible to use for each in Angular-V2? Thank you. ...

Transfer my testing utilities from React Router version 5 to version 6

I am currently transitioning my project to React V6 router and encountering an issue with my test utility function. Every time I run the test, all my expectations fail because jest cannot locate the object. Has anyone faced this error during a similar migr ...

Choosing everything with ngrx/entity results in not selecting anything

Issue with Selector I am facing an issue with my selector in the component. Despite making the call, the component does not update with books from the FakeApiService. The actions and effects seem to be functioning correctly. The problem seems to be relat ...

How can I implement a user notification service using rxjs within Angular?

As a newcomer to reactive programming, I am exploring ways to create an Angular service that can present notifications to the user. Check out what I have accomplished so far: https://stackblitz.com/edit/angular-rxjs-notifications?file=app%2Fapp.component. ...

Tips for Ensuring the Observable Completes Before Subscribing

I utilized RXJS operators in my code to retrieve an array of locations. Here is the code snippet: return O$ = this.db.list(`UserPlaces/${this.authData.auth.auth.currentUser.uid}`, { query: { orderByChild: 'deleted', equalTo: fal ...

What is the significance of the colon before the params list in Typescript?

Consider the following code snippet: import React, { FC } from "react"; type GreetingProps = { name: string; } const Greeting:FC<GreetingProps> = ({ name }) => { // name is string! return <h1>Hello {name}</h1> }; Wha ...

Error exporting variables in NodeJS causing confusion

I'm currently in the process of transitioning a Meteor application from TypeScript to Javascript. While working on the server side, I've encountered some issues with the import/export code that functioned smoothly in TypeScript but now seems to b ...

The callback function `(err: any, data: any) => void` does not share any properties with the type `RequestInit`

Inspired by the tutorial at , I am working on a time-based visualization. I am currently using version "d3": "^5.4.0". Here is the code snippet: d3.json('http://127.0.0.1:5000', function (err, data) { if (err) throw err; // Cre ...

What is the process of compiling TypeScript code?

When attempting to use tsc, I encountered issues. Even when having typescript but lacking tsc, the problem persisted. What steps should I take next? https://i.sstatic.net/Djgqb.png ...

Error: Unexpected character U found at the beginning of the JSON data when using JSON.parse in Angular 8

Lately, I came across an issue while making changes to some parts of my previous code. The error caught my attention as it occurred when trying to pass a specific object or a part of that object in Angular 8, NodeJS with express, and mongoose. Upon checki ...

Defining Zepto's Velocity.js with Typescript

I've been searching for type definitions for velocity, but the only one I could find is on tsd `tsd install velocity-animate', which unfortunately does not support velocity using Zepto. Any suggestions? ...