Using Typescript to ensure that objects cannot be added to an array of objects if a specific boolean property is set to true

I've been struggling to figure out how to prevent an object from being added to an array based on a property value in generics.

During an online interview, I was given an exercise to create a zoo scenario within an hour:

There are five animals in the zoo, along with two birds. Two zookeepers work at night and four in the morning. Only Norman is allowed to enter the tiger's cage.

I started working on the exercise but got stuck in index.ts where I'm able to assign handlers to animals, but I want TypeScript to restrict this assignment based on certain conditions.

You can view the sandbox for this exercise at: https://codesandbox.io/s/zoo-example-yi3op

export default interface Dangerous {
    canBeHandledBy(employees: Employee[]): void;
}

import Employee from "./Employee";

export default class Animal {
    private _handlers: Employee[] = [];
    constructor(private _name: string){}
    public get name() {
        return this._name;
    }
    public get handlers(): Employee[] {
        return this._handlers;
    }
    public set handlers(employees: Employee[]) {
        this._handlers = employees;
    }
    public assignHandler(employee: Employee): void {
        this._handlers.push(employee);
    }
}

export default class Employee {
    constructor(private _name: string, private _gender: Gender, private _employeeTitle: EmployeeTitle, private _dangerHandler: boolean = false) {}
    public get name():string {
        return this._name;
    }
    public get gender(): Gender {
        return this._gender;
    }
    public get isDangerHandler(): boolean {
        return this._dangerHandler;
    }
    public get title(): EmployeeTitle {
        return this._employeeTitle;
    }
    public set title(title: EmployeeTitle){
        this._employeeTitle = title;
    }
}


export default class Lion extends Animal implements Dangerous {
    constructor(_name: string) {
        super(_name)
    }
    
    canBeHandledBy(employees: Employee[]): void {        
        try {
            const ordinaryEmployees = employees.filter(emp => !emp.isDangerHandler);
            if(ordinaryEmployees.length >0 ){
                throw new Error('Ordinary meployees not allowed to handle Lion');
            }
            this.handlers = employees;
        }
        catch(e) {
            console.log(e);
        }
    }
}

In app.js:

I want typescript to prevent me from adding John to the lion.

const norman:Employee = new Employee("norman", Gender.MALE, EmployeeTitle.ZOOKEEPER, true);
const john:Employee = new Employee("john", Gender.MALE, EmployeeTitle.ZOOKEEPER);

const lion = new Lion("zumba");
lion.assignHandler(john)

Answer №1

Here is a simplified version of your example. You can draw inspiration from this model and see if it fits your needs:

type AnimalType = 'Wild' | 'Domestic'

interface Animal<T extends AnimalType> {
  name: string
  assignCareTaker: (careTaker: CareTaker<T>) => void
}

class Tiger implements Animal<'Wild'> {
  constructor(readonly name: string) {}
  assignCareTaker(careTaker: CareTaker<'Wild'>) {
    console.log(careTaker)
  }
}

class CareTaker<T extends AnimalType> {
  constructor(readonly name: string, readonly canHandle: T) {}
}

const jane = new CareTaker('Jane', 'Wild')
const mike = new CareTaker('Mike', 'Domestic')

const tiger: Tiger = new Tiger('Shera')

tiger.assignCareTaker(jane) // compiles
tiger.assignCareTaker(mike) // does not compile

Answer №2

To ensure safety, you can easily modify the assignhandler() function within the Animal.ts file. Just add a condition to verify if the employee being passed in has the isDangerHandler attribute set to True.

public assignHandler(employee: Employee): void {
  if (employee.isDangerHandler) {
    this._handlers.push(employee);
  }
}

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

Converting strict primitive types to primitive types in Typescript

I have a function that parses a string into a value and returns a default value if it fails. The issue is that this code returns too strict types for primitives, such as `false` instead of `boolean`. How can I resolve this? Should I utilize some form of ...

"Navigate to another screen with the FlatList component upon press, displaying specific

I've created a flatlist of countries with a search filter using an API. I need help implementing a feature where clicking on a country's name in the list redirects to a screen that displays the country's name and number of cases. The screen ...

Combining and grouping objects by their IDs in a JavaScript array

Information: [ { "id": "ewq123", "name": "Joshua", "order": "Pizza" }, { "id": "ewq123", "name": "Joshua", "order": ...

Discover the process of accessing and setting values in Angular 8 to easily retrieve and manipulate data from any page!

Greetings! I am currently utilizing Angular 8 and I have a query regarding how to access the set value in any given page. Here is a snippet of my code: class.ts export class testClass { get test():string{ return this.sexe; } ...

Implement Angular and RxJS functions sequentially

this.functionalityClient.activateFeature(featureName) .pipe( concatMap( feature => { this.feature = feature; return this.functionalityClient.setStatus(this.feature.id, 'activated'); } ), con ...

Why isn't the background-image displaying with the use of the url() function?

I've been attempting to set an image as the background using background-img:url(imageLing), but it's not working properly. When I inspect the element, it shows me background-image: url(assets/backgrounds/5.jpg);. What could be causing this issue? ...

Working with Typescript to map and sort the key values of a new datasource object

Managing a large datasource filled with objects can be challenging. My goal is to rearrange the order of objects in the array based on new values for each key. Whenever a new value for a key is found, I want the corresponding object to move to the top of t ...

Creating a generic that generates an object with a string and type

Is there a way to ensure that MinObj functions correctly in creating objects with the structure { 'name': string }? type MinObj<Key extends string, Type> = { [a: Key]: Type } type x = MinObj<'name', string> Link to Playgr ...

Tips for migrating an AngularJS application to Angular

My current project involves implementing a basic search function using AngularJS (link). I want to integrate this feature into an existing Angular application. To do this, I created a new Angular app and transferred the view to app.component.html. <hea ...

What is the best way to determine which option is most suitable: types, classes, or function types in TypeScript for

Currently, I am developing a small todo command line utility with a straightforward program structure. The main file is responsible for parsing the command line arguments and executing actions such as adding or deleting tasks based on the input provided. E ...

Steps for updating a server component after redirectionWould you like to know how

One of my server components fetches and displays data only when the user is authorized: function CheckAuthorization() { const isAuthenticated = // check if user is authorized return ( <div> {isAuthenticated ? ( <DisplayAutho ...

What other options exist for searching objects of functions?

Can you suggest some good, easy-to-read, scalable, and efficient alternatives for this basic pattern? type Figure = { kind: "square", sideLength: number } | { kind: "rectangle", length: number, width: number } | { kind: "circle", radius: numbe ...

Why is it that the component passed in props fails to function properly when invoked as a function? React is signaling a shift in the order of Hooks being called

Here is a simple example I've prepared to illustrate how I am passing a component and then calling it like a function, as well as another example where it works just by calling it normally. You can switch between the working and not working examples b ...

Unable to upload file in angular2 due to empty Body (FormData)

Attempting to upload a photo with Angular2 to my REST Service (Loopback). The Loopback service has been successfully tested using Postman and is able to accept files with the x-www-form-urlencoded header. Below is a simplified version of the service metho ...

`Express.js Controllers: The Key to Context Binding`

I'm currently working on a project in Express.js that involves a UserController class with methods like getAllUsers and findUserById. When using these methods in my User router, I have to bind each method when creating an instance of the UserControlle ...

user interface grid element in Materia

After writing this code, I encountered the following error: No overload matches this call. Overload 1 of 2, '(props: { component: ElementType<any>; } & SystemProps<Theme> & { children?: ReactNode; classes?: Partial<GridClasses>; .. ...

The Application Insights Javascript trackException function is giving an error message that states: "Method Failed: 'Unknown'"

I've been testing out AppInsights and implementing the code below: (Encountering a 500 error on fetch) private callMethod(): void { this._callMethodInternal(); } private async _callMethodInternal(): Promise<void> { try { await fetch("h ...

What methods can I utilize from Google Maps within Vuex's createStore()?

Currently, I am in the process of configuring my Vuex store to hold an array of Marker objects from the Google Maps Javascript API. Here is a snippet of how my createStore function appears: import { createStore } from "vuex"; export default ...

Getting the PlayerId after a user subscribes in OneSignal with Ionic2

Currently working on an app with Ionic2 and facing a challenge with retrieving the player id after a user subscribes in order to store it in my database. Any suggestions on how I can retrieve the unique player id of OneSignal users post-subscription? ...

Error in TypeScript when accessing object using string variable as index

Currently, I am facing a challenge in my project where I am dynamically generating routes and managing them in an Elysia(~Express) application. The issue arises when TypeScript's type checking system fails to index an object using a string variable. S ...