Providing a description of the type in a way that permits the use of one key while restricting the usage of another key

Apologies for my limited English skills, could you kindly explain how to specify a type where one key can be entered only if another specific key is also filled?

interface Base {
  top?: number;
  left?: number;
  behavior?: "auto" | "smooth"
}

interface OffsetTopProps {
  offsetTop?: number;
}

interface OffsetLeftProps {
  offsetLeft?: number;
}

// When 'top' is provided, 'offsetTop' can be entered and the same applies for 'left'

I attempted using unions but it didn't yield the expected outcome.

type test = Base | OffsetTopProps | OffsetLeftProps 

Answer №1

If you desire to ensure that in cases where top is not present, then offsetTop should also be absent, you can define that part of the type as a union like so:

type Top = 
  { top: number, offsetTop?: number } | 
  { top?: never, offsetTop?: never };

In TypeScript, there isn't a direct way to prevent a property key in an object type, but you can come close by specifying that the key is an optional property whose value is the impossible never type. Since there are no actual values of type never, the most logical thing to do is to exclude the property (or perhaps set it to undefined). So, Top either has a top property of type number and an optional offsetProperty also of type number, or it lacks both a top and an offsetTop property.

Similarly for the left/offsetLeft pair:

type Left = 
  { left: number, offsetLeft?: number } | 
  { left?: never, offsetLeft?: never };    

By combining those with Base, you can create Test:

interface Base {
  behavior?: "auto" | "smooth"
}

type Test = Base & Top & Left;

As Test represents the intersection of Base, Top, and Left, a value of type Test must adhere to all three definitions.

Let's put it to the test:

let t: Test;    
t = { behavior: "auto", top: 1 }; //valid
t = { top: 1, offsetTop: 2 }; // valid
t = { top: 1, offsetTop: 2, left: 3 }; // valid
t = { top: 1, offsetTop: 2, left: 3, offsetLeft: 4 }; // valid
t = { left: 3 }; // valid
t = { left: 3, offsetLeft: 4 }; // valid

t = { offsetTop: 2 }; // error
t = { offsetLeft: 4 }; // error
t = { top: 1, offsetTop: 2, offsetLeft: 4 }; // error

It seems to be working correctly. The compiler accepts valid values while rejecting invalid ones that contain an offsetTop without a top or an offsetLeft without a left.

Playground link to code

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

Invoke a function within the <img> tag to specify the source path

I have been attempting to achieve something similar to the following: <img id="icon" class="cercle icon" src="getIcon({{item.status}})" alt=""> This is my function: getIcon(status){ switch (status) { case 'Ongoing': ret ...

There was a mistake: _v.context.$implicit.toggle cannot be used as a function

Exploring a basic recursive Treeview feature in angular4 with the code provided below. However, encountering an error when trying to expand the child view using toggle(). Encountering this exception error: ERROR TypeError: _v.context.$implicit.toggle i ...

How to Transfer Deleted List Items from one Unordered List to another in Angular 9 using Event Binding

Greetings to all =) I've recently delved into Angular 9 and I'm really enjoying the component-based approach. To sharpen my skills in property and event binding, I embarked on a project involving building a deck with two lists. English isn't ...

Tips for integrating a variety of components onto a single webpage

Exploring the functionality of Angular, I am looking to include multiple components on a single page. How can this be achieved effectively in Angular? I envision each div representing a distinct component and view, with all components residing in separate ...

Enhance a function by sending it back to save static variables

I have a similar function like this one: export const bar = function () { const myItem = new MyItem(); return function bar(param1?: number, param2?: string): void{ ... }; }(); Where myItem is a variable that I use as a temporary inside ...

Is it possible to implement a redirect in Angular's Resolve Navigation Guard when an error is encountered from a resolved promise?

I have integrated Angularfire into my Angular project and am utilizing the authentication feature. Everything is functioning properly, however, my Resolve Navigation Guard is preventing the activation of the component in case of an error during the resolve ...

Tips on efficiently utilizing stored information in Ionic and Angular applications

I am facing an issue where I can only access my variable inside the this.storage.get function. How can I retrieve this stored data? Here is the content of tab2.page.html: <ion-toolbar> <ion-title> Stats </ion-title> &l ...

Receiving a reply from the axios function

Whenever I try to call the lookUpItem function from ItemSearch.vue, I always get an undefined response. Code snippet from ItemSearch.vue: <script setup lang="ts"> import { lookUpItem } from '../systemApi' async fu ...

Error: The use of await in RequestPromise is not valid

I encountered a TSLint error stating "Invalid 'await' of a non-Promise value." in the line of code below: const response: RequestResponse = <RequestResponse>await this.apiRequest(uri); Additional code context: private apiRequest: Request ...

VS Code using Vue is displaying an error message stating: The property '' does not exist on type '{}'.ts(2339)

While working in Visual Studio Code, I came across the following code snippet: <script lang="ts" setup> const parseCSV = () => { // Code omitted for brevity } } </script> <template> <button @click="parseCSV ...

Encountering a host configuration issue while trying to use next/image in a TypeScript environment

I understand that when using Next.js image components without TypeScript, the URL must be configured in next.config.js, but I'm unsure why this doesn't work with TypeScript. ..., is not set up under images in your next.config.js. Learn more her ...

The parameter type 'router' cannot be replaced with the type 'typeof ...'. The 'param' property is not included in the type 'typeof'

I'm currently working on a node application using TypeScript and have set up routing in a separate file named 'route.ts' import home = require('../controller/homeController'); import express = require('express'); let ro ...

Guide on accessing js file in an Angular application

I have a component where I need to create a function that can search for a specific string value in the provided JavaScript file. How can I achieve this? The file path is '../../../assets/beacons.js' (relative to my component) and it's named ...

TypeScript Error: The Object prototype must be an Object or null, it cannot be undefined

Just recently, I delved into TypeScript and attempted to convert a JavaScript code to TypeScript while incorporating more object-oriented features. However, I encountered an issue when trying to execute it with cmd using the ns-node command. private usern ...

Retrieving information from Next.js and Typescript with the help of getStaticProps

I've been working on a personal project with Next.js and TypeScript. I'm attempting to fetch data from an API and then map the items, but I'm running into issues. When I use console.log, it returns undefined. The file is located in the pages ...

Changing the method signature in a TypeScript interface to replace/override the original one

For this specific scenario, the Array<T> interface is being extended in the following manner: interface BetterArray<T> extends Array<T> { push(this: BetterArray<T>, value: T): this; } Important note - the implementation of Arr ...

What is the method to retrieve the data type of the initial element within an array?

Within my array, there are different types of items: const x = ['y', 2, true]; I am trying to determine the type of the first element (which is a string in this case because 'y' is a string). I experimented with 3 approaches: I rec ...

Incorporating and modifying a component's aesthetics using styled-components: A comprehensive guide

My OverviewItem component has 2 props and is a styled-component. I want to change just one style on this component, which can be done by using the technique of styling any component. Does creating a wrapper component remain the only option for sharing st ...

Utilize the type name as the indexer for the interface

Issue with Abstract Concepts My challenge involves relating two distinct groups of types to one another. // Group A interface Hello { ... } interface Foo { ... } // Group B interface World { ... } interface Bar { ... } To tackle this problem, I am crea ...

Is it possible to automatically open the Tinymce Comments sidebar without the need for a manual button click?

After successfully implementing the Tinymce comments plugin into our configuration, we have come across a request from our users. They would like the 'showcomments' button to automatically trigger on page load, displaying the sidebar containing t ...