Is there a more efficient method in Typescript to define the types of an object containing arrays?

Suppose I have an object that contains various roles, each granting a specific set of rights:

const allRoles = {
  user: [
    'right1'
  ],
  admin: [
    'right1',
    'right2'
  ],
} as const

If I want to define the types for user rights and admin rights separately, I can use the following:

type userRights = Array<typeof allRoles.user[number]>
type adminRights = Array<typeof allRoles.admin[number]>

To represent both types combined, I would do:

type RoleRights = userRights | adminRights

However, if I want to create a single type declaration for all available rights without specifying individual user or admin rights, is there a way to achieve this?

It is similar to creating an array of all rights using:

const roleRights = new Map(Object.entries(allRoles))

Answer №1

If you wish to define your types by using as const with the allRoles object for inference, here is a way to achieve that:

const allRoles = {
  user: [
    'right1'
  ],
  admin: [
    'right1',
    'right2'
  ],
} as const

type RolesMap = typeof allRoles;
/*
{
  readonly user: readonly ["right1"];
  readonly admin: readonly ["right1", "right2"];
}
*/

type UserRoles = RolesMap['user']; // readonly ["right1"]
type AdminRoles = RolesMap['admin']; // readonly ["right1", "right2"]
type AllRoles = UserRoles | AdminRoles; // readonly ["right1"] | readonly ["right1", "right2"]

Note: This approach solidifies fixed types for ['right1'] and ['right1', 'right2'], instead of collapsing automatically into something like ('right1' | 'right2')[] as you might expect.

If you have prior knowledge of all rights within the system architecture, it may be more logical to type the system in reverse, starting from the rights and constructing the map upwards, for instance:

enum RightsEnum {
  right1 = 'right1',
  right2 = 'right2',
  right3 = 'right3',
  ...
}
enum RolesEnum {
  user = 'user',
  admin = 'admin'
}

// explicit
interface RolesMap {
  [RolesEnum.user]: [RightsEnum.right1],
  [RolesEnum.admin]: [RightsEnum.right1, RightsEnum.right2]
}

// generic
type RolesMap = Record<RolesEnum, RightsEnum>;

const allRoles: RolesMap = {
  user: [
    'right1'
  ],
  admin: [
    'right1',
    'right2'
  ],
}

In this setup, your "all rights" type would simply be RightsEnum

Answer №2


const userAccess = typeof roles.user[number] as UserAccess;
const adminAccess = typeof roles.admin[number] as AdminAccess;

const userAccessList: UserAccess[] = [];
const adminAccessList: AdminAccess[] = [];

type RoleAccess = (UserAccess | AdminAccess)[];

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

How to effectively test @input using Jasmine: Anticipated Object did not match undefined

Having trouble with this error. There seems to be something missing but can't pinpoint what it is. The task at hand is simple: just test this basic component. Let's take a look: import { Component, OnInit, Input } from '@angular ...

Utilizing Typescript to pass props to a material-ui button enclosed in styled-components

After setting up my react project using the typescript template, I decided to style the material-ui Button component using the 'styled' method from the styled-components library as shown below: import React from 'react'; import styled f ...

What is the process of molding items?

Here is an object that I have: { "id": "Test", "firstname": "Test", "lastname": "Test", "age": 83 } However, I only want to return the object with this value: { "id&quo ...

Creating a versatile protractor framework to streamline multiple project implementations

There's a concept in the works for creating a 'protractor core' that will be utilized by various projects for UI testing. Currently, I have an Angular project called 'project1' with e2e tests (cucumber-protractor-typescript) that c ...

No pathways can be established within Core UI Angular

I've been attempting to use the router link attribute to redirect to a new page, but instead of landing on the expected page, I keep getting redirected to the dashboard. Below is an overview of how my project's structure looks: [![enter image de ...

Error: TypeScript cannot find @types declaration for restify-client

Currently, I am attempting to upgrade to the most recent version of restify (6.4.2). Our application is written in TypeScript. The clients have now been separated into their own package starting from the previous version of restify we were using (4.3.2) - ...

How can we use tsyringe (a dependency injection library) to resolve classes with dependencies?

I seem to be struggling with understanding how TSyringe handles classes with dependencies. To illustrate my issue, I have created a simple example. In my index.tsx file, following the documentation, I import reflect-metadata. When injecting a singleton cl ...

What are the steps to locally test my custom UI library package built with tsdx in a React.js project

I am currently utilizing tsdx to develop a React UI library, and I am looking to test it within my Next.js project before officially publishing it to the npm package. Initially, I attempted using npm link, which worked initially. However, when I made ch ...

Changing JSON names to display on a webpage

I am looking to modify the name displayed in a json file for presentation on a page using ion-select. mycodehtml ion-select [(ngModel)]="refine" (ionChange)="optionsFn(item, i);" > <ion-option [value]="item" *ngFor="let item of totalfilter ...

What are the steps to effectively utilize an interface within a TypeScript file that contains its own internal import?

Currently, I am in the process of developing a React JavaScript project using WebStorm and attempting to enable type hinting for our IDEs (including VS Code) by utilizing TypeScript interfaces and JSDoc annotations. Our goal is to potentially transition to ...

What purpose does the "dom" serve in the "lib" array found in the tsconfig.json configuration file of an

Within my Angular 6 project, I encountered the following segment in tsconfig.json and ts.config.spec.json: "lib": [ "es2016", "dom" ] I am curious about the role of dom. The official documentation explains: "... you can exclude declarati ...

MQTT Broker specialized in Typescript

I'm looking to create a MQTT Broker using TypeScript and Angular. I've attempted a few examples, but I keep encountering the following error: Uncaught TypeError: http.createServer is not a function Here's a simple example of what I'm c ...

What are the steps for changing this JavaScript file into TypeScript?

I'm currently in the process of converting this JavaScript file to TypeScript. However, I've encountered an error with the onClick function as shown below: import React from 'react'; import { Popover } from 'antd'; import A fr ...

Having trouble rendering Next.JS dynamic pages using router.push()? Find out how to fix routing issues in your

The issue I am facing revolves around the visibility of the navbar component when using route.push(). It appears that the navbar is not showing up on the page, while the rest of the content including the footer is displayed. My goal is to navigate to a dy ...

Is it possible to use an object's attribute as a switch case in TypeScript with useReducer?

I am attempting to convert switch case String into an object, but for some reason typescript is misunderstanding the switch case within useReducer: Prior to version update, everything was functioning correctly: export const LOGIN_USER = "LOGIN_USER&qu ...

Create keys for accessing objects based on props

I'm facing challenges with dynamisation in Typescript. I understand that TS primarily works statically, but there are instances where I need to access an object dynamically and can't do so directly through generics because the values are dynamica ...

Exploring Geofirestore's capabilities with advanced query functionalities

Thinking about updating my firestore collection structure to incorporate geoquery in my app. Geofirestore requires a specific structure: interface GeoDocument { g: string; l: GeoPoint; d: DocumentData; } I understand that geofirestore does ...

Is it necessary to manually validate parameters in TypeScript when developing a library?

Understanding the basic workings of TypeScript, it's clear that TypeScript transpiles code to JavaScript without adding extra behavior like type checking during execution. For instance, function example(parameter: string): void { console.log(paramet ...

Utilize the prototype feature from a versatile source

Can a class with a generic like class Foo<A> {} access A's prototype or use a typeguard on A, or perform any kind of logic based solely on A's type - without being given the class, interface, or instance to Foo's constructor (e.g. when ...

Using RxJS for various scenarios with Angular HttpInterceptor

Take a look at the following code block containing AuthInterceptor: @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor(private authService: AuthService) { } intercept(req: HttpRequest<any>, next: HttpHand ...