Spread operator in Typescript for complex nested collection types

I have implemented a Firestore database and defined a schema to organize my data:

type FirestoreCollection<T> = {
  documentType: T;
  subcollections?: {
    [key: string]: FirestoreCollection<object>;
  };
};

type FirestoreSchema<
  T extends { [key: string]: U },
  U = FirestoreCollection<object>
> = T;

export type FirestoreModel = FirestoreSchema<{
  'chat-messages': {
    documentType: ChatMessage;
  };
  chats: {
    documentType: Chat;
    subcollections: {
      'chat-participants': {
        documentType: ChatParticipant;
      };
    };
  };
}>;

The schema is functioning as expected.

I am aiming to define a type for a function that accepts an array of arguments, where each argument follows this structure:

{ collection: string, id: string }

I want the function to detect if the current argument is not a subcollection of the previous argument and raise a type error accordingly.

In essence, I desire the function to operate in the following way:

// This should work because it's a valid root level document
getDocument(
  { collection: 'chat-messages', id: '123' }
);

// This should work as 'chat-participants' is a subcollection of 'chats'
getDocument(
  { collection: 'chats', id: '123' }
  { collection: 'chat-participants', id: '456' }
);

// This should throw an error since 'chats' is not a subcollection of 'chat-messages'
getDocument(
  { collection: 'chat-messages', id: '123' },
  { collection: 'chats', id: '456' }
);

// This should also result in an error since 'invalid' is not a collection at the root
getDocument({ collection: 'invalid', id: '123' })

Any suggestions?

Answer №1

If you're looking to retrieve data, consider utilizing the specified type structure:

type RetrieveDataArgs<T = FirestoreModel> = {
  [K in keyof T]: [{collection: K, id: string}] | ('subcollections' extends keyof T[K] ? 
    [{collection: K, id: string}, ...RetrieveDataArgs<T[K]['subcollections']>]
  : never)
}[keyof T]

Implementation example:

const fetchData = (...args: RetrieveDataArgs<FirestoreModel>) => {

}

sandbox

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

The specified property 'XYZ' is not found in the type 'Readonly<{ children?: ReactNode; }> & Readonly<{}>'

Whenever I try to access .props in RecipeList.js and Recipe.js, a syntax error occurs. Below is the code snippet for Recipe.js: import React, {Component} from 'react'; import "./Recipe.css"; class Recipe extends Component { // pr ...

Unable to locate module within a subdirectory in typescript

The issue I'm facing involves the module arrayGenerator.ts which is located in a subfolder. It works perfectly fine with other modules like Array.ts in the parent folder. However, when I introduced a new module called Sorting.ts, it started giving me ...

Creating a global variable in Angular that can be accessed by multiple components is a useful technique

Is there a way to create a global boolean variable that can be used across multiple components without using a service? I also need to ensure that any changes made to the variable in one component are reflected in all other components. How can this be ac ...

find all the possible combinations of elements from multiple arrays

I have a set of N arrays that contain objects with the same keys. arr[ {values:val1,names:someName},   {values:val2,names:otherName}, ] arr2[   {values:valx,names:someNamex}, {values:valy,names:otherNamey}, ] My goal is to combine all possible c ...

Exploring the Power of PrimeNG and Observables in Angular 4 with RxJS

After configuring my Angular 4 project with a service like this: const usersURL = 'http://my.super.url.php'; @Injectable() export class UserService { users: Observable<User[]> constructor (public http:Http) let tick$ = Observ ...

Filtering data on objects in Angular can be achieved by utilizing the built-in

Retrieving data from the backend using this function: private fetchData(): void { this.dataService.fetchData().pipe( tap((response: any) => { this.persons = response.results; this.familyMembersTrue = this.persons.filter(x =&g ...

Issue encountered in Loopback 4: Error with @repository dependency injection (TypeError: Undefined property 'findOne')

As I set up Bearer Token authentication for my Loopback 4 application, I referenced this implementation guide: https://github.com/strongloop/loopback-next/tree/master/packages/authentication. Within my src/providers/auth-strategy.provider.ts file, I encou ...

Connecting Angular modules via npm link is a great way to share common

Creating a project with a shared module that contains generic elements and components, such as a header, is my goal. This shared module will eventually be added as a dependency in package.json and installed through Nexus. However, during the development ph ...

How do I modify the local settings of the ngx-admin datepicker component to show a Turkish calendar view?

Looking for tips on customizing the new datepicker component in Nebular ngx-admin. Specifically, I want to change the local settings to display the calendar as Turkish. Explored the library but still seeking alternative methods. Any suggestions? ...

Inquiring about Vue 3 with TypeScript and Enhancing Types for Compatibility with Plugins

I've been struggling to find a working example of how to implement type augmentation with Vue3 and TypeScript. I have searched for hours without success, trying to adapt the Vue2 documentation for Vue3. It appears that the Vue object in the vue-class ...

What are the steps for encountering a duplicate property error in TypeScript?

I'm currently working with typescript version 4.9.5 and I am interested in using an enum as keys for an object. Here is an example: enum TestEnum { value1 = 'value1', value2 = 'value2', } const variable: {[key in TestEnum]: nu ...

Using TypeScript with Redux for Form Validation in FieldArray

My first time implementing a FieldArray from redux-form has been quite a learning experience. The UI functions properly, but there seems to be some performance issues that I need to investigate further. Basically, the concept is to click an ADD button to i ...

Using SCSS variables in TypeScript inside a Vue project

Has anyone had experience with importing SASS (scss) variables into JavaScript in a TypeScript Vue 3 project? // @/assets/styles/colors.scss $white: #fff; // @/assets/styles/_exports.scss @import "./colors.scss"; :export { white: $white; } <templat ...

The C# private property is inaccessible during a TypeScript callback as it is not contained within the

I'm encountering an issue with TypeScript where the callback function is only returning _proto in the response's .data property when I set private properties in C# and instantiate an object filled with constructed properties. Strangely, if the pr ...

When using Angular 5's ngModel, the user interface displays the updated value dynamically without requiring the

When filling out my form, I encounter an issue with a select element and a bind variable. If I make a change to the value and save it, everything works as expected. However, if I make a change in a modal window but then close the modal without saving the v ...

Error encountered when using a connected component in Typescript with Redux

Seeking assistance on integrating state from redux into properties within a react component that is outlined in a tsx file. The LoggedInUserState type has been defined elsewhere and is exported as shown below: import { Action, Reducer } from 'redux& ...

Implement a personalized auto-fill feature in Angular triggered by a user's

I am looking to implement a customized autocomplete feature on an input element in Angular 8. Currently, I have the following setup to capture the tab key press: <input (keydown.Tab)="onKey($event)" /> Then, in my .ts file, I have the following me ...

Evaluate TypeScript method against the interface, for example T['methodName']

Suppose we have an interface: class A { method(x:number) : string } And a function: const b = (x: string) : number; Our goal is to test the function against the interface. Here is one way to achieve this: type InterfaceKey = { method: any }; ...

How to Utilize Knockout's BindingHandler to Integrate JQuery.Datatables Select Feature?

I've developed a custom KO bindingHandler (view it here) to assist in updating the DataTable. The documentation for JQuery.DataTable.Select regarding how to access data requires a handle. You can see the details here. var table = $('#myTable&a ...

Having trouble with loading JavaScript during ng build --prod process

The JavaScript file I'm using for multiple emails (multiple_emails.js plugin) works well with ng serve. Here is my code: (function( $ ){ $.fn.multiple_emails = function(options) { // Default options var defaults = { ...