How can you create a function in typescript that only allows parameters of a specific type?

Here's what I'm trying to accomplish:

function validateNumber(param: ???): param is number {
}

If the parameter can be a number, such as number | string or number | boolean, it should be accepted by the function. However, if the type is something like string or boolean, compilation errors should occur.

Is there a way to create this type of validation?

Answer №1

The main idea is to modify the generic function isNumber() so that the type T of parameter param is limited to being a sub-type of number. This goes against the usual extends constraint in TypeScript, which sets an upper bound. Unfortunately, TypeScript doesn't currently provide direct support for lower bounded generics. There is a feature request for this functionality in microsoft/TypeScript#14520, but it might involve using super instead of extends, like this:

// INVALID TS (as of 4.7), AVOID THIS
function isNumber<T super number>(param: T): param is number {
  return typeof param === "number"
}

Since we can't do this directly, one alternative approach is to simulate lower bounded generics by utilizing a standard upper bound within a conditional type. The expression

T extends (U extends T ? unknown : never)
somewhat mimics the behavior of T super U. If T is a supertype of
U</code, then <code>(U extends T ? unknown : never)
results in unknown, satisfying the constraint (T extends unknown). Otherwise, if T is not a supertype of U, it evaluates to never, potentially failing the constraint (T extends never).

In this scenario, where T needs to be either a supertype or subtype of number, we can use

T extends (number extends T ? unknown : number)
. This way, even if T represents a numeric literal type like 14, everything should function correctly.

A complication arises because the compiler does not automatically recognize that param will likely be a supertype of

number</code, preventing the use of the type predicate <code>param is number
. Instead, it's necessary to define a type that satisfies both T and number, achieved through an intersection construct like param is T & number.

Therefore, a revised version of the isNumber() function incorporating these changes would look like:

function isNumber<T extends number extends T ? unknown : number>(
  param: T
): param is number & T {
  return typeof param === "number"
}

Finally, testing confirms that the implementation behaves as expected:

isNumber("oops"); // error, Argument of type 'string' is 
// not assignable to parameter of type 'number'.
isNumber(Math.random() < 0.5 ? "abc" : 123); // okay
isNumber(14); // okay

Everything seems to be working well.

Link to Playground with Code Example

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

Modifying the State in a Class Component

private readonly maxSizeOfDownloadedFiles: number = 1000000; state = { totalSum: this.maxSizeOfDownloadedFiles }; handleCallback = () => { this.setState({ totalSum: 12 }) alert('totalSum ' + this.state.totalSum); }; Upon executing the ...

Is it recommended for TypeScript to automatically resolve the index.ts file as the default module file?

Struggling with getting the module resolution to work in TypeScript. Consider the following file structure: /modulename/index.ts Should it be resolved like this? import * as modulename from "modulename" I can't seem to make it work. However, imp ...

The switchMap function in Angular does not trigger the async validator as expected

To ensure that the username entered by the user is unique, I am sending an HTTP request for every input event from the target element. It is important to debounce this operation so that only one HTTP request is made for X consecutive input events within a ...

The type 'Observable<void | AuthError>' cannot be assigned to 'Observable<Action>'

I am encountering an error that reads: error TS2322: Type 'Observable<void | AuthError>' is not assignable to type 'Observable<Action>'. Type 'void | AuthError' is not assignable to type 'Action'. Type &a ...

Tips for implementing UI properties in React

Utilizing an external UI Library, I have access to a Button component within that library. My goal is to create a customized NewButton component that not only inherits all props from the library Button component but also allows for additional props such as ...

Oops! There seems to be an issue. The system cannot locate a supporting object with the type 'object'. NgFor can only bind to Iterables

onGetForm() { this.serverData.getData('questionnaire/Student Course Review') .subscribe( (response: Response) => { this.questionnaire = response.json(); let key = Object.keys(this.questionnaire); for ...

Ways to obtain a tab and designate it as the default in angular when using angular material Tabs

I am facing an issue with accessing tabs within a nested component. The parent component contains the tab feature and to reach the tabs inside the child component, I am using the following code: document.querySelectorAll('.mat-tab-group'); The a ...

Determining the type of <this> in an Object extension method using TypeScript

I am attempting to incorporate a functionality similar to the let scope function found in Kotlin into TypeScript. My current strategy involves using declaration merging with the Object interface. While this approach generally works, I find myself missing ...

Converting ts files to js: A comprehensive guide

I am looking for a way to transform my TypeScript files into JavaScript files smoothly. The conversion process goes well with the command: nodemon --watch assets/ts --exec tsc assets/ts/*.ts --outDir assets/js However, I have encountered an issue where im ...

What is the best way to retrieve a variable that has been exported from a page and access it in _

Suppose this is my pages/visitor.tsx const PageQuery = 'my query'; const Visitor = () => { return <div>Hello, World!</div>; }; export default Visitor; How can I retrieve PageQuery in _app.tsx? One approach seems to be by assi ...

Utilize an array of JSON objects to populate an array of interfaces in Angular/Typescript

I am currently facing a dilemma - my code is functioning without any errors when executed, but my text editor is flagging an issue stating that the property 'categories' does not exist on type 'CategoryInterface[]' (specifically on the ...

What's the best way to replicate a specific effect across multiple fields using just a single eye button?

Hey everyone, I've been experimenting with creating an eye button effect. I was able to implement one with the following code: const [password, setPassword] = useState('') const [show, setShow] = useState(false) <RecoveryGroup> ...

Tips for toggling visibility in Angular 2

I utilized [hidden] in the following way where the value of "secondTab" is set to true. <form #siteForm="ngForm" novalidate (ngSubmit)="saveSite(siteForm.value,siteForm.valid)" class="admin-modal"> <div class="txt-danger">{{errorMessage}}&l ...

Automapper for AngularJS to format results retrieved from a RESTful service

When using the $resource service for data access from a restful service, I encounter a small challenge. The JSON data retrieved is in the following format: { "name_surname": "john_smith", "years_of_employment": "10" } However, I would like to map ...

What is the best way to retrieve a string from a URL?

Is there a way to extract only a specific string from a URL provided by an API? For instance: I'm interested in extracting only: photo_xxx-xxx-xxx.png Any suggestions on how to split the URL starting at photo and ending at png? ...

Exploring Typescript within React: Creating a property on the current instance

Within my non-TypeScript React component, I previously implemented: componentWillMount() { this.delayedSearch = _.debounce((val) => { this.onQuerySearch(val); }, 1000); } This was for debouncing user input on an input field. The corres ...

onmouseleave event stops triggering after blur event

I am facing an issue with a mouseleave event. Initially, when the page loads, the mouseleave event functions correctly. However, after clicking on the searchBar (click event), and then clicking outside of it (blur event), the mouseleave functionality stops ...

React: The useContext hook does not accurately reflect the current state

I'm currently facing a dilemma as I attempt to unify data in my app. Whenever I click the button, the isDisplay value is supposed to be set to true; even though the state changes in my context file, it does not reflect in the app. Thank you for your ...

The attribute 'pixiOverlay' is not found in the property

Working on my Angular 8 project, I needed to display several markers on a map, so I chose to utilize Leaflet. Since there were potentially thousands of markers involved, I opted for Leaflet.PixiOverlay to ensure smooth performance. After installing and imp ...

Guide to asynchronously loading images with Bearer Authorization in Angular 2 using NPM

I am in search of a recent solution that utilizes Angular2 for my image list. In the template, I have the following: <div *ngFor="let myImg of myImages"> <img src="{{myImg}}" /> </div> The images are stored as an array w ...