Topic: Creating specific return types for methods that are chained and reusable

Here is a code snippet I am currently working on:

const mixed = {
  validations: [] as any[],
  formattings: [] as any[],
  exceptions: [] as any[],
  required(message?: string) {
    this.validations.push({
      name: 'required',
      message: message || config.messages.mixed.required,
      test: (value: string) => {
        return value && value.trim() ? true : false
      },
    })

    return this
  },
  // other methods that will be reusable
}

const string = () => ({
  ...mixed,
  maxWords(maxWords: number, message?: string) {
    type Params = { maxWords: number }

    this.validations.push({
      name: 'maxWords',
      params: { maxWords },
      message: message || config.messages.string.maxWords,
      test: (value: string, { maxWords }: Params) => {
        const wordCount = value.trim().split(' ').length

        return wordCount <= maxWords
      },
    })

    return this
  },

  trim() {
    this.formattings.push({
      name: 'trim',
      format: (value: string) => {
        return value.trim()
      },
    })

    return this
  },
})

const number = () => ({
  ...mixed,
  positive(message?: string) {
    this.validations.push({
      name: 'positive',
      message: message || config.messages.string.maxWords,
      test: (value: string) => {
        // make a validation
      },
    })

    return this
  },
})

const schema = {
  string() {
    return string()
  },
  number() {
    return number()
  },
  // file, date, etc..
}

const form = {
  name: schema.string().required().trim().maxWords(3),
  age: schema.number().required().positive(),
}

Despite the successful execution of the code, I am facing an issue with IntelliSense not working on all methods in my form validation library.

Here is the problem I am encountering.

Here is a link to the TS Playground where you can test the code in real-time.

The issue lies in the typing of the returned values of each function.

Answer №1

If you define the method type in an interface or class, you can utilize required(message?: string): this. Here's a simplified example:

interface Validation<T> {
  message: string
  test: (value: T) => boolean
}

interface FormField<T> {
  validations: Validation<T>[];
  required(message: string): this;
}

const mixed: FormField<unknown> = {
  validations: [],
  required(message) {
    this.validations.push({
      message,
      test(value: string) {
        return value.trim()
      }
    })
    return this
  }
}

interface StringField extends FormField<string> {
  maxWords(maxWords: number, message: string): this;
}
const string = (): StringField => ({
  ...mixed as StringField, // this is a bit ugly, would work better if mixed() was a function or superclass
  maxWords(maxWords, message) {
    this.validations.push({
      message,
      test: value => value.trim().split(' ').length <= maxWords,
    })
    return this
  }
})

interface NumberField extends FormField<number> {
  positive(message: string): this;
}

const number = (): NumberField => ({
  ...mixed as NumberField,
  positive(message) {
    this.validations.push({
      message,
      test: value => value > 0,
    })

    return this
  },
})

const form = {
  name: string().required('missing name').maxWords(3, 'too few words'),
  age: number().required('missing age').positive('must be born already'),
}

(TypeScript playground)

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

Problem with jQuery's .prepend method being called twice on list items

Looking to enhance the appearance of a list by adding some icons before the anchor links within each list item. <ul class="submenu-children"> <li><a href="#">Link</a></li> <li><a href="#">Link</a></li> ...

I noticed that my API call is being executed twice within the router function

In my NextJs project, I am utilizing Express for routing. I have implemented a router with a dynamic :id parameter which triggers an axios call to check the ID in the database. However, I am facing an issue where the API is being called twice when the :id ...

Employing setTimeout within a repetitive sequence

function displayColors() { $.each(colors, function(index) { setTimeout(function(){ revealColor(colors[index]); }, 1000); }); } I'm attempting to create a loop where the revealColor function is executed every second until all colors ...

Developing an Easy-to-Use PDF Popup Interface

I need a simple PDF modal where I can input the text: 'The sky is blue' and have it link to a PDF that opens up when clicked. I found a similar sample online, but it includes an image plus an image modal that pops up. I want to replace the imag ...

Issue with auto formatting quotes in IntelliJ / WebStorm is no longer functioning as expected

Currently, my TSLint configuration is set to permit the use of single quotes (') instead of double ones ("). Previously, I was able to conveniently switch all instances of " to ' in a file by using the Reformat Code shortcut CTRL + ALT ...

Create a lockscreen feature in AngularJS that becomes active after a period of inactivity

I am looking to integrate a lockscreen feature into my app using Angular.js. This lockscreen will consist of a route and an HTML template containing a form that prompts the user to re-enter their password in order to keep their session active. The purpose ...

Why does Javascript execute in this specific order?

I'm puzzled as to why JavaScript behaves in a certain way, or if I made an error in my code. How is it that the code after $.getJSON runs before the callback completes? window.ratingCallback = function(data){ if(data[0].code == 3){ ratin ...

Switching an array with a single string in MongoDB

I'm facing an issue with grouping data in mongoDB and wondering if there's a way to transform an array into a string. The objects included in $lookup are currently displayed in an array format, but I need them in a single string. Here is my code ...

Using multiple ng-controller directives with the "controller as x" syntax on a single element

What is the reason that Angular does not allow two ng-controller directives on a single element and What are some potential solutions for this issue - such as using custom directives or nesting HTML elements with a single ng-controller directive, among oth ...

Switching ng-Idle countdown time from seconds to minutes is possible by adjusting the configuration settings

I have implemented ng-idle in my application, where the warning popup appears after 10 seconds with the message: "Your session will be close in 10 seconds" However, I need to change this to minutes. The session should be closed in 5 minutes instead. How ...

The design of Foundation's 4 form dropdown is not displaying correctly on Internet Explorer versions 8 and below

During the development of a website using Foundation 4 framework, I encountered an issue with form dropdowns not displaying correctly on Internet Explorer 8 and older versions. Instead of using Foundation's javascript rendering, they appear as the def ...

Switching back and forth between two different numbers with the help of React.useState hooks

Can someone help me with the logic using React.useState hooks? I'm trying to toggle the "volume" option in the state of the "PlayOn" object between 0 and 0.75. I am not very experienced with React. I need help with the function logic for soundChange ...

Creating a dynamic table using jQuery and XML content

Hello everyone, I am new to jQuery and JavaScript in general. My goal is to generate a table based on XML data, but I'm struggling to achieve the correct output. Here's what I have tried so far: Here is my HTML code: <table id="daily_fruit"& ...

What is the best way to reorganize an object's properties?

Looking for a way to rearrange the properties of an existing object? Here's an example: user = { 'a': 0, 'b': 1, 'c': 3, 'd': 4 } In this case, we want to rearrange it to look like this: user = { &a ...

NPM is complaining about the absence of a start script

Apologies for any language barriers in my English. I'm currently facing an issue while trying to deploy an app on Heroku using the heroku local web command in the terminal. The error message ERR! missing script: start keeps popping up, even though the ...

Displaying svg files conditionally in a react native application

I have developed an app specifically for trading dogs. Each dog breed in my app is associated with its own unique svg file, which are all stored in the assets folder (approximately 150 svg files in total). When retrieving post data from the backend, I re ...

Issue with md-stretch-tabs in Angular-Material causing a problem

I am in the process of developing an Angular-based application. ISSUE: I included md-stretch-tabs in my md-tabs element, but when I switch to tab#2, the tab retracts as if there is not enough space to flex. Is this problem related to dependencies or the c ...

How can I dictate the placement of a nested Material UI select within a popper in the DOM?

Having trouble placing a select menu in a Popper. The issue is that the nested select menu wants to mount the popup as a sibling on the body rather than a child of the popper, causing the clickaway event to fire unexpectedly. Here's the code snippet f ...

Limit the ng-repeat directive based on the length of the array, using an ng-if condition

<div ng-repeat="badgesData in deptUSersData.badges | limitTo : 7 track by $index"> ........content....... </div> If the length of deptUSersData.badges is more than 8, I want to apply a limit of 7. How can I achieve this? My desired ...

Implementing jQuery getScript in a Ruby on Rails 3 application

After watching a railscast tutorial on loading records through ajax when a link is clicked, I implemented the following javascript: $(function() { $("#dash_container th a").live("click", function() { $.getScript(this.href); return false; }); } ...