Verify the completeness of data types within an array in typescript

I am currently developing a comprehensive match function that I want to ensure is exhaustive during compile time. Although adding a default case would help with this, I am intrigued by some interesting dependent typing techniques I have come across. It would be really cool if the function could also enforce that there are no duplicate cases.

For instance, here's an example of the function:

function match<T, U>(getKey: (value: T) => U, cases: Array<[U, (value: T) => T]): (value: T) => T {
  return (v) => {
      const key = getKey(v);
      const matchingCase = cases.find(([k, _]) => k === key);
      if (matchingCase) {
         return matchingCase[1](v]);
      }
      throw new Error("Reached unreachable code");
  }
} 

Is it possible for me to enforce that all variants of U are included in the cases? Modifying the function parameters is acceptable.

Answer №1

Here is a unique solution that utilizes a factory to construct the matcher and enforce rules during the build process. This approach might be suitable for your specific situation if you are open to a rewrite.


interface IMatcherFactoryEnd<Item> {
    getMatcher: () => (v: Item) => Item;
};

interface IMatcherFactory<Item, Keys, CheckedKeys extends Keys> {
    when: <K extends Exclude<Keys, CheckedKeys>>(
        key: K,
        fn: (v: Item) => Item,
    ) => (Exclude<Keys, CheckedKeys | K> extends never ? IMatcherFactoryEnd<Item> : IMatcherFactory<Item, Keys, CheckedKeys | K>);
};

class CustomMatcherFactory<Item, Keys, CheckedKeys extends Keys>
implements
    IMatcherFactory<Item, Keys, CheckedKeys>,
    IMatcherFactoryEnd<Item>
{
    constructor (
        private getKeyFunc: (v: Item) => Keys,
        private caseArray: [CheckedKeys, (v: Item) => Item][],
    ) { }

    when <K extends Exclude<Keys, CheckedKeys>>(
        key: K,
        fn: (v: Item) => Item,
    ) {
        return new CustomMatcherFactory<Item, Keys, CheckedKeys | K>(
            this.getKeyFunc,
            [...this.caseArray, [key, fn]],
        );
    }

    getMatcher () {
        const getKeyFunc = this.getKeyFunc;
        const caseArray = this.caseArray;
        return (v: Item) => {
            const key = getKeyFunc(v);
            const b = caseArray.find(([k, _]) => k === key);
            if (b) return b[1](v);
            throw new Error("Hit unreacheable code");
        };
    }
}

export function customMatcherFactory <Item, Keys> (
    getKeyFunc: (v: Item) => Keys,
): IMatcherFactory<Item, Keys, never> {
    return new CustomMatcherFactory<Item, Keys, never>(
        getKeyFunc,
        [],
    );
}

const getKeyValue = (v: boolean) => v ? "true" as const : "false" as const;

export const matcher_OK = customMatcherFactory(getKeyValue)
    .when("true", () => false)
    .when("false", () => true)
    .getMatcher();

export const matcher_KO1 = customMatcherFactory(getKeyValue)
    .when("true", () => false)
    .getMatcher();

export const matcher_KO2 = customMatcherFactory(getKeyValue)
    .when("false", () => true)
    .getMatcher();

export const matcher_KO3 = customMatcherFactory(getKeyValue)
    .when("true", () => false)
    .when("false", () => true)
    .when("true", () => false)
    .getMatcher();

export const matcher_KO4 = customMatcherFactory(getKeyValue)
    .when("true", () => false)
    .when("false", () => true)
    .when(true, () => false)
    .getMatcher();

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 continuity of service value across parent and child components is not guaranteed

My goal is to update a value in a service from one component and retrieve it in another. The structure of my components is as follows: parent => child => grandchild When I modify the service value in the first child component, the parent receives the cor ...

Testing the Angular router-outlet using Jasmine

When testing web-app navigation using Jasmine spec with RouterTestingModule, I am facing challenges with nested fixture.whenStable().then(() => {}). For instance: After clicking on multiple links where the router-outlet changes the displayed component ...

Typescript: Verifying the type of an interface

In my code, I have a function called getUniqueId that can handle two different types of interfaces: ReadOnlyInfo and EditInfo. Depending on the type passed to this function, it will return a uniqueId from either interface: interface ReadOnlyInfo { item ...

Typescript's implementation of AngularJs provider

After creating an Angularjs provider in typescript, I found myself wondering if there might be a more efficient way to achieve the same outcome. My current provider serves as an abstraction for a console logger, with the interface primarily designed to s ...

What is the best way to relocate the styles folder to the src folder while using nextjs, typescript, and tailwind

I am currently working with Next.js using TypeScript and Tailwind CSS. My goal is to relocate the styles folder into the src folder. I have already updated the baseUrl in my tsconfig.json file to point to the src directory, but I encountered the following ...

Deactivate the rows within an Office UI Fabric React DetailsList

I've been attempting to selectively disable mouse click events on specific rows within an OUIF DetailsList, but I'm facing some challenges. I initially tried overriding the onRenderRow function and setting CheckboxVisibility to none, but the row ...

Angular Update Component on Input ChangeEnsuring that the component is automatically

<div class=" card-body"> <div class="row"> <div class=" font-icon-list col-lg-2 col-md-3 col-sm-4 col-xs-6 col-xs-6" routerLinkActive="active" *ngFor="let subject of subjects"> <div class=" fon ...

Updating parent components through child components in ReactWould you like another unique

In my current project, I am attempting to change the state of the main component labeled App.tsx by using a child component called RemarksView.tsx. I have attempted passing props such as setRemarks and remarks, but unfortunately the state in the parent c ...

Updating a label dynamically in Angular

QUESTION: Is there a way to dynamically change the text of a label based on a certain condition? Specifically, I want the label to be blank when I'm on a specific route in my App. CURRENT APPROACH: <RadSideDrawer allowEdgeSwipe=&quo ...

Retrieve the value of a property within the same interface

Is there a way to access an interface prop value within the same interface declaration in order to dynamically set types? I am attempting something like this: export type MethodNames = "IsFallmanagerUpdateAllowed" | "UpdateStammFallmanager& ...

How to verify in HTML with Angular whether a variable is undefined

When it comes to the book's ISBN, there are instances where it may not be defined. In those cases, a placeholder image will be loaded. src="http://covers.openlibrary.org/b/isbn/{{book.isbn[0]}}-L.jpg?default=false" ...

Encountering Error TS2411 when upgrading from Typescript version 1.0.0 to 1.1.0-1

After updating my TypeScript using npm install typescript -g, I have been encountering a recurring error during compilation. Although the compilation is successful, it's becoming tedious. cmd.exe /D /C C:/Users/Vado/AppData/Roaming/npm/tsc.cmd --sour ...

Can dynamic string types be declared in Typescript?

Let's consider the following scenario: export enum EEnv { devint, qa1 }; export type TEnv = keyof typeof EEnv; export const env:Record<TEnv, {something:number}> = { devint: { something: 1, }, qa1: { something: 1, }, } Now, I ai ...

In Visual Studio, the .js.map files and .js files seem to be mysteriously hidden, leaving only the TypeScript .ts files visible

In the past, I utilized Visual Studio Code for Angular 2 development and had the ability to hide .js and .js.map files from the IDE. Now, I am working on a project using VS 2017 Professional with Typescript, Jasmine, Karma, and Angular 4. Task Runner, etc. ...

Exploring Local Gems with Google Places API in Ionic 2

I recently integrated the Google Maps API into my app and now I am looking to incorporate Google Places as well. I have managed to successfully implement Geolocation, but I am facing difficulties when trying to perform a nearby search for "supermarkets" in ...

Leveraging a component as a property of an object in Vue version 3

I'm trying to figure out if there's a way to use a Component as a property in Vue 3. Consider the TypeScript interface example below: import type { Component } from 'vue' interface Route { url: string icon: Component name: ...

Update the function's argument type signature if the current argument is a function with properties

Looking for input on a potential title change, but for now, here are the details of my specific use case: I'm currently developing a library that facilitates executing methods remotely and accessing properties across serialized boundaries like those ...

NGXS Alert: Unable to resolve parameters for TranslationEditorState: (?)

I'm currently implementing NGXS for state management within my Angular 9 application. I've encountered a specific issue where any attempt at dependency injection in one of the state classes results in an error message stating "Error: Can't r ...

What is the best approach for integrating a Material UI Autocomplete component with graphql queries?

Hello there! I'm currently working with React Typescript and trying to incorporate query suggestions into an Autocomplete Material UI component in my project. Below is a snippet of my GraphQL queries: Query Definition: import gql from 'graphql- ...

Using injected services within static methods may seem tricky at first, but once you

I am exploring the integration of angularjs and typescript in my project. Currently, I am working on creating an Orm factory using typescript but have encountered some challenges. The structure of my factory class is as follows: class OrmModel implements ...