What is the proper way to declare an array of arrays with interdependent types?

Imagine I am creating a directory of tenants in a shopping center, which can be either shops or restaurants. These tenants fall into various categories:

type ShopTypes =
    | `Accessories`
    | `Books`
    | `Clothing`;

type RestaurantTypes =
    | `Dive Bar`
    | `Elevated`
    | `Family`;

type Tenant<TenantCategory extends `shop` | `restaurant` = `shop`> = {
    category: TenantCategory;
    categoryType: TenantCategory extends `shop` ? ShopTypes : RestaurantTypes;
    label: string;
};

To simplify the process, I plan to organize the tenants as an array of arrays and utilize a function to convert each "row" into a Tenant:

function toTenantList(...items: Array<
    [`shop` | `restaurant`, ShopTypes | RestaurantTypes, string]
>): Array<Tenant> {
    return items.map(([category, categoryType, label]) => ({
        category,
        categoryType,
        label,
    }));
}

const tenants: Array<Tenant> = toTenantList(
    [`shop`, `Accessories`, `Alice's Accessories`],
    [`restaurant`, `Dive Bar`, `Dan's Dive`],
    [`shop`, `Books`, `Barry's Books`],
    [`restaurant`, `Elevated`, `Exquisite Epicure`],
);

An error is generated:

Type '{ category: "shop" | "restaurant"; categoryType: ShopTypes | RestaurantTypes; label: string; }[]' is not assignable to type 'Tenant<"shop">[]'.
  Type '{ category: "shop" | "restaurant"; categoryType: ShopTypes | RestaurantTypes; label: string; }' is not assignable to type 'Tenant<"shop">'.
    Types of property 'category' are incompatible.
      Type '"shop" | "restaurant"' is not assignable to type '"shop"'.
        Type '"restaurant"' is not assignable to type '"shop"'.

In essence, TypeScript is concerned that a RestaurantType might be placed in a shop row, and vice versa.

How can I specify the requirement that if a row represents a shop, it must have a ShopType, and if it represents a restaurant, it must have a RestaurantType?

Answer №1

The definition of your Tenant type is as follows:

type Tenant<TenantCategory extends `shop` | `restaurant` = `shop`>;

When using the Tenant type, it looks like this:

function toTenantList(...items: Array<
    [`shop` | `restaurant`, ShopTypes | RestaurantTypes, string]
>): Array<Tenant>

The Tenant type is a generic type, but in this case, no specific arguments are provided, so the default value of 'shop' is used.

If you want the Tenant type to accept both 'shop' and 'restaurant', you can use

Tenant<'shop' | 'restaurant'>[]
instead.

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

What are the steps to set up NextJS 12.2 with SWC, Jest, Eslint, and Typescript for optimal configuration?

Having trouble resolving an error with Next/Babel in Jest files while using VSCode. Any suggestions on how to fix this? I am currently working with NextJS and SWC, and I have "extends": "next" set in my .eslintrc file. Error message: Parsing error - Can ...

typescript add an html element to an existing html document

I'm experimenting with creating a dynamic HTML element using TypeScript to display on a webpage. In my .ts file, my code looks like this: const displayContent = document.getElementById("display-content") as HTMLOutputElement; var displayVariable = " ...

Capturing a webpage through Record RTC integration with Angular

I am looking to record a specific section of the page as a video with audio. For example, when a user enters the page, I want it to automatically start recording the activities in that particular area (similar to how iframe videos work). The recording sh ...

A step-by-step guide on integrating Detox with jest (ts-jest) and Typescript in a react-native app

I've been experimenting with incorporating Typescript into my detox tests. The most relevant information I could find was in this gist. However, when trying to implement it, I encountered an error stating that jasmine is not defined. After researching ...

Anticipate that the function parameter will correspond to a key within an object containing variable properties

As I develop a multi-language application, my goal is to create a strict and simple typing system. The code that I am currently using is as follows: //=== Inside my Hook: ===// interface ITranslation { [key:string]:[string, string] } const useTranslato ...

What is the method for adding a document within an array that is nested within another document?

Apologies if the title seems complex... I struggled to find a better way to describe it. The scenario I am dealing with fits the following Schemes: Collection1: const mongoose = require('mongoose'); const itemSchema = mongoose.Schema({ _id: ...

Issue when transferring properties of a component to a component constructed Using LoadingButton MUI

Check out my new component created with the LoadingButton MUI: view image description here I'm having issues passing props to my component: view image description here Dealing with a TypeScript problem here: view image description here I can resolv ...

Customize the date format of the Datepicker in Angular by implementing a personalized pipe

I am dealing with a datepicker that defaults to the MM/dd/yyyy format, and I need it to adjust based on the user's browser language. For example, if the browser language is English India, then the format should be set to dd/MM/yyyy as shown below. Be ...

react-hook-form replaces the onChange function causing delays in updating the value

Recently, I created a unique Select component utilizing useState and onChange. I attempted to integrate this custom component with the powerful react-hook-form. Allow me to share the code snippet for the bespoke Select component. const Select = forwardRef ...

Can someone please explain how to prevent Prettier from automatically inserting a new line at the end of my JavaScript file in VS Code?

After installing Prettier and configuring it to format on save, I encountered an issue while running Firebase deploy: 172:6 error Newline not allowed at end of file eol-last I noticed that Prettier is adding a new line at the end when formatting ...

Choose a date range from the date picker

I am currently attempting to combine two dates using a rangepicker. Below is the command used to select the date: Cypress.Commands.add('setDatePickerDate', (selector, date) => { const monthsShort = [ 'janv.', 'févr.& ...

What is the best approach for implementing line coverage for object literal in Typescript Mocha unit-tests?

Lead: I am a newcomer to using typescript and writing unit tests with Mocha and Chai. Question: Can anyone provide tips on achieving 100% line coverage in unit tests for an object literal that isn't within a class? I want to avoid going static if pos ...

Unexpected behavior: ng2-dragula modelDrop event leading to undefined array

Struggling to figure out the issue with this code. As shown in this GIF, when dragging a div from one container to another, the object disappears and the array becomes undefined. https://i.stack.imgur.com/TELyc.gif Here is the code snippet: Main View.ht ...

Innovative approaches to enhancing Feathers services through the use of relational data in design patterns

I'm in the process of developing a straightforward application that involves a one-to-many relationship between data entities. Specifically, I am working with feathers js and sequelize (utilizing sqlite) to create a system where each site can have mul ...

Extract JSON values based on a given condition

I am working on a Typescript project that involves an array and a JSON object. I need to extract the value of a property from the object based on another property's value being in the array. Here is the array: let country: string[] = [ 'AR' ...

Determine the maximum and minimum numbers by inputting a number and utilizing jQuery

<script type="text/javascript"> function findLargestNumber() { var number1, number2; number1 = Number(document.getElementById("N").value); number2 = Number(document.getElementById("M").value); if (number1 > numb ...

Issue with Angular modal not opening as expected when triggered programmatically

I am working with the ng-bootstrap modal component import { NgbModal, ModalCloseReasons } from "@ng-bootstrap/ng-bootstrap"; When I click on a button, the modal opens as expected <button class="btn labelbtn accountbtn customnavbtn" ...

What is the method of duplicating an array using the array.push() function while ensuring no duplicate key values are

In the process of developing a food cart feature, I encountered an issue with my Array type cart and object products. Whenever I add a new product with a different value for a similar key, it ends up overwriting the existing values for all products in the ...

Whenever I am building a React application, I encounter a bug that states: "node:fs:1380 const result = binding.mkdir()"

Whenever I try to enter the command: create-react-app my-app --template typescript I keep encountering this error message: node:fs:1380 const result = binding.mkdir( ^ Error: EPERM: operation not permitted, mkdir 'D:\ ...

Struggling to comprehend the intricacies of these generic declarations, particularly when it comes to Type Argument Lists

I'm currently reviewing the code snippet from the TypeScript definitions of fastify. I am struggling to understand these definitions. Although I am familiar with angle brackets used for generics, most TypeScript tutorials focus on simple types like Ar ...