Creating a Record instance consisting of a specific key and its corresponding value

Sorry for the complexity, I've struggled to simplify this further. Feel free to update the question title for more specificity.

I aim to define a foundational data type structure:

type AbstractBaseTypes = {
    [key: string]: {
        inputTypes: Record<string, unknown>;
        outputType: unknown;
    }
}

This structure consists of keys, each with corresponding input data and output data types.

For example, a concrete version of this data type can be defined as:

type KnownBaseTypes = {
    "text": {
        inputTypes: {
            label: string;
            maxLength: number;
        }
        outputType: string;
    },
    "number": {
        inputTypes: {
            label: string;
            min: number;
            max: number;

        },
        outputType: number;
    }
}

My ultimate objective is to create a map where each key has a function that takes three parameters:

  • control object = details about the key and the input data defined in KnownBaseTypes
  • data - a partially known record with a single known key and its value type
  • callback - accepts the output type value

Here is a JavaScript implementation:

const myDataManipulationMap = {...

My typing approach involves:

Control object:

type ControlObject<T extends AbstractBaseTypes, K extends keyof T> = {...

Partially known data object:

type PartiallyKnownDataObject<T extends AbstractBaseTypes, K extends keyof T> = {...

Combining it all:

type DataGenerationMap<T extends AbstractBaseTypes> = {...

This approach almost accomplishes my goal.

The issue is that 'partially known data' is assumed to have multiple keys with the outputType, whereas I want to restrict assumptions to the data accessible by controlObject.key only.

const myMap: DataGenerationMap<KnownBaseTypes> = {...

What's the problem here?

Answer №1

Check out this code snippet:

type AbstractBaseTypes = {
    [key: string]: {
        inputTypes: Record<string, unknown>;
        outputType: unknown;
    }
}


type KnownBaseTypes = {
    "text": {
        inputTypes: {
            label: string;
            maxLength: number;
        }
        outputType: string;
    },
    "number": {
        inputTypes: {
            min: number;
            max: number;
        },
        outputType: number;
    }
}

type ControlObject<T extends AbstractBaseTypes, K extends keyof T> = {
    key: keyof KnownBaseTypes; // <-- you used `string` as a key type, that's why you ended up with {[index:string]:number} type
    inputData: T[K]["inputTypes"];
};

type PartiallyKnownDataObject<T extends AbstractBaseTypes, K extends keyof T> = {
    [key in ControlObject<T, K>["key"]]: T[K]["outputType"]
}

type DataGenerationMap<T extends AbstractBaseTypes> = {
    [K in keyof T]: (
        data: PartiallyKnownDataObject<T, K>,
        controlObject: ControlObject<T, K>,
        doSomethingWithdData: (data: PartiallyKnownDataObject<T, K>) => void
    ) => void;
}

type Result = PartiallyKnownDataObject<KnownBaseTypes, "number">


const myMap: DataGenerationMap<KnownBaseTypes> = {
    "text": (data, controlObject, callback) => {

        const {
            key, inputData
        } = controlObject;

        const {
            label, maxLength
        } = inputData

        const newString = (label + data[key]).substr(0, maxLength);

        const newData = {
            ...data,
            [controlObject.key]: newString
        };

        callback(newData);
    },

    "number": (data, controlObject, callback) => {

        const {
            key, inputData
        } = controlObject;

        const {
            min, max
        } = inputData

        const a: string = min; // expected error 

        console.log(data.foo); // <-- should error

        callback(); //expected error 
        console.log(controlObject.inputData.foo); //expected error;  

        const newNumber = min + Math.random() * (max - min);

        const newData = {
            ...data,
            [controlObject.key]: newNumber
        };

        callback(newData);
    }
}

Does this code work for you?

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

Webpack 4 combines the power of Vue with the versatility of Typescript classes and JavaScript code simultaneously

Currently, I am in the process of migrating JS files to Typescript with the objective of being able to utilize both JS and Typescript classes within Vue. While I understand that I can convert Vue scripts into Typescript, I prefer not to do so at this momen ...

Angular is unable to start the variable_INITIALIZation process

I've created a method called get that returns data of type ProductModel. getProduct(id:number): Observable<ProductModel[]> { const url = `${'api/product/getProductById/'}/${id}`; return this.http.get<ProductModel[]>(u ...

Is it possible to enable tooltips to function through the innerHTML method?

Currently, I am utilizing the innerHTML attribute to modify the inner HTML of a tag. In this instance, it involves the <td></td> tag, but it could be applied to any tag: <td *matCellDef="let order" mat-cell [innerHTML]="order. ...

The string is being added to an array twice

I am managing two sets of lists where strings will be transferred between them. One set contains a list of strings for searching purposes. The other set contains the same list of strings but is not used as a filter. The second set functions in a similar ...

What is the best way to bind the value of total when working with forms and the bind method?

I am working on a form where I need to pass the value of total. Regarding the total: I have successfully passed the value of the cart, which is an array. const [total, setTotal] = useState<number | undefined>(undefined); const calculateTotal = () ...

Exploring the world of chained JavaScript Promises for automatic pagination of an API

Dealing with a paged API that requires fetching each page of results automatically has led me to construct a recursive promise chain. Surprisingly, this approach actually gives me the desired output. As I've tried to wrap my head around it, I've ...

Error in AngularX TS: Trying to invoke a type that does not have a callable signature

Encountering an issue while working on a component, specifically during ng serve/build process. Please note that this error is different from any console errors, despite what some may think. The expected outcome is for the code to successfully build and ru ...

Get the file without specifying type in request - Angular

Is it possible to download a file solely through the response without specifying a responsetype in the header? I am looking for a way to download the file without including any additional parameters in the header. ...

Store the selected checkbox values in an array when submitting in Ionic

One issue I am facing is that the checked checkboxes are returning true instead of the value of input (type="checkbox"). Array displaying responded checked or unchecked items I am unable to store this data in an array as needed. Additionally, I cannot sp ...

connecting and linking template content with an Observable

I have a CRUD page that needs to be updated after every operation. I have implemented Observable and the CRUD functions (specifically Add and Delete) are working fine, but I need to manually refresh the page to see the changes reflected. After trying to ...

Expanding the typings for an established component in DefinitelyTyped

Is there a way to define new typings for additional props in DefinitelyTyped? After updating the material-ui library with some new props for the SelectField component, I realized that the typings in DefinitelyTyped are outdated. Is it possible to extend th ...

Having trouble with react-responsive-carousel in Next.js version 13?

I have been following a tutorial to create an eBay clone. One of the steps involves creating a carousel. However, when I add it, the carousel does not transition to the next page. I have attempted to uninstall and reinstall packages, but the issue persists ...

Is it possible for TypeScript to convert a generic enum type into a string at runtime?

Enumerations and interfaces are an important part of my codebase: enum EventId { FOO = 'FOO', BAR = 'BAR', } interface EventIdOptionsMap { [EventId.FOO]: { fooOption: string; }, [EventId.BAR]: { barOption: number; } ...

When using Angular, receiving a "Bad Request" error message with a status code of 400 after making a POST

I am encountering an issue when trying to send register form data to my server as I keep receiving a (Bad Request) error. Interestingly, the server works correctly when tested with postman using the following data: { "username": "root", "email": " ...

Tips for handling catch errors in fetch POST requests in React Native

I am facing an issue with handling errors when making a POST request in React Native. I understand that there is a catch block for network connection errors, but how can I handle errors received from the response when the username or password is incorrec ...

Webpack: The command 'webpack' does not exist as a recognized cmdlet, function, script file, or executable program

Attempting to set up a new project using webpack and typescript, I have created the project along with the webpack file. Following the instructions on the webpack website, I successfully installed webpack using npm install webpack webpack-cli --save-dev ...

Discover the geolocation data for post code 0821 exclusively in Australia using Google Maps Geocoding

I'm having trouble geocoding the Australian postcode 0821. It doesn't seem to reliably identify this postcode as being located within the Northern Territory, unlike 0820 and 0822 which work fine. Here's an example of what I'm doing: ...

Having trouble with ESLint in VSCode? The ESLint extension seems to be ignoring linting rules after starting a new project within VSCode

I recently started using the ESLint extension in my VSCode editor for my React project. After creating the starter files, I ran the following command in my terminal: eslint --init This allowed me to choose the AirBnb style guide with React, which generat ...

What is the best way to open and view files in an NPM dependency that do not use JavaScript?

I am facing an issue with an NPM project setup where my-config is a dependency of my-api. In the my-config project, there is a line of code that fetches the aws-config.ini file from the etc folder: instance.configs.aws = ini.parse(fs.readFileSync('./ ...

Is there a way to create a universal getter/setter for TypeScript classes?

One feature I understand is setting getters and setters for individual properties. export class Person { private _name: string; set name(value) { this._name = value; } get name() { return this._name; } } Is there a w ...