What is the best way to write a function in typescript that verifies whether the argument extends a type argument and then returns the argument?

I need to create a function that checks if the argument's type extends a specific type variable and then returns the argument. Something like this:

declare function checkType<T, X extends T>(argument: X): X

However, TypeScript gives an error when using it, saying that the function requires two type arguments. Another method I tried is:

declare function verifyType<T, X extends T = T>(argument: X): X

But this approach returns an incorrect type.

Playground Link

How can I make TypeScript take the type of the argument, assign it to the type variable X, ensure that it extends T, and then return X?

An example of usage would be:

const numberRecord = checkType<{[K: string]: readonly number[]}>({
"hello":[10,200]
} as const); // successful
numberRecord. // intellisense for all keys
const numberRecord = checkType<{[K: string]: readonly number[]}>({
"hello":[10,200,"hi"]
} as const); // should throw an error

Answer №1

Hey there! Your attempt at implementing the "satisfies" operator, as mentioned in microsoft/TypeScript#7481, is quite interesting. The concept revolves around writing val satisfies Type to trigger a compiler error if val does not match type Type, without widening the type of val to Type. Unfortunately, TypeScript doesn't have this operator yet, so you're resorting to a workaround by renaming extendsA to satisfies.


Implementing satisfies as a single generic function with two type parameters isn't straightforward due to TypeScript's lack of partial type parameter inference, as discussed in microsoft/TypeScript#26242. You'll either need to manually specify all type parameters or let the compiler infer them all. This limitation leads us to consider refactoring to a curried function approach.

// declare function satisfies<I>(): <X extends I>(argument: X) => X;
function satisfies<I>() {
    return <X extends I>(argument: X) => argument
}

This refactor requires an additional step for calling:

satisfies<{ [K: string]: readonly number[] }>()({
    "hello": [10, 200]
} as const); // good

While this method may seem cumbersome, reusing the partially applied function can alleviate some of the complexity when working with the same I type repeatedly:

const satisfiesRecordOfNumberArray =
    satisfies<{ [K: string]: readonly number[] }>();    

const recordOfNumberArray = satisfiesRecordOfNumberArray({
    "hello": [10, 200]
} as const); // good
recordOfNumberArray.hello // okay    

const recordOfNumberArray2 = satisfiesRecordOfNumberArray({
    "hello": [10, 200, "hi"]
} as const); // error! 
// Type 'readonly [10, 200, "hi"]' is not assignable to type 'readonly number[]'

An alternative approach entails having both I and X type parameters in one function, including a dummy parameter dummyI of type I. This method requires providing a bogus value for I:

// declare function satisfies<I, X extends I>(dummyI: I, argument: X): X;
function satisfies<I, X extends I>(dummyI: I, argument: X) { return argument }

const recordOfNumberArray = satisfies(null! as { [K: string]: readonly number[] }, {
    "hello": [10, 200]
} as const); // good

const recordOfNumberArray2 = satisfies(null! as { [K: string]: readonly number[] }, {
    "hello": [10, 200, "hi"]
} as const); // error! 
// Type 'readonly [10, 200, "hi"]' is not assignable to type 'readonly number[]'

Although functional, this method trades off a cleaner syntax for explicitness. Ideally, a built-in satisfies operator would be preferred over these workarounds. Both approaches involve trade-offs between elegance and verbosity.

Check out the code in the 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

Creating a unified environment variable for Angular 2 and ASP.NET Core MVC: A comprehensive guide

In my ASP.NET Core MVC project, I am utilizing an Angular 2 application. Both the Angular 2 app and the Startup.cs file contain code that is specific to different environments. For example, using http://localhost as the web service URL during development ...

Issue arises when trying to implement sidebar navigation in Angular with Materialize CSS

Just starting my Angular journey and running into some trouble trying to set up a practical and responsive menu using SidebarNav and Dropdown. I used CLI to install and configure angular2-materialize and materialize-css. To create the menu, I made a comp ...

Issue: Oops! The digital envelope routines are not supported in Angular while attempting to run the project

I encountered an error when running the command below: ng s The error message is as follows: Error: error:0308010C:digital envelope routines::unsupportedat new Hash (node:internal/crypto/hash:68:19)at Object.createHash (node:crypto:138:10)at BulkUpdateDe ...

What causes TypeScript to be unable to locate declared constants?

I am facing an issue with the following simple code snippet: const getMethod = 'get'; const postMethod = 'post'; export type RequestMethod = getMethod | postMethod; When I try this code in TypeScript Playground, it shows an error sta ...

ReactJS Error: The property 'hubConnection' is not defined on type 'JQueryStatic'

I am currently working with the Signalr library in React, but I keep encountering the following error: Property 'hubConnection' does not exist on type 'JQueryStatic'. Is there a solution to this issue? declare var window : any; import ...

Nuxt - asyncData ISSUE: "Variable '$axios' is inferred to have an 'any' type."

Referencing the guidelines provided at Encountering an error logged in console while executing yarn dev: ERROR ERROR in pages/index.vue:51:21 ...

Automatically convert TypeScript packages from another workspace in Turborepo with transpilation

I have set up a Turborepo-based monorepo with my primary TypeScript application named @myscope/tsapp. This application utilizes another TypeScript package within the same repository called @myscope/tspackage. For reference, you can view the example reposit ...

"Exploring the concepts of inheritance in Typescript

I'm seeking answers regarding inheritance in TypeScript/JavaScript. Below is my base class (express controller router): abstract class BaseCtrl { abstract model; // Get all getAll = (req, res) => { this.model.find({}, (err, docs) => ...

Closing Popover Instance from another Component (Ionic, Typescript)

I've been dealing with an issue where a function that I imported from another class isn't getting called and the parser isn't recognizing it. The Popover in my code also can't be closed. I've tried various similar solutions, but no ...

Incorporating timed hover effects in React applications

Take a look at the codesandbox example I'm currently working on implementing a modal that appears after a delay when hovering over a specific div. However, I've encountered some challenges. For instance, if the timeout is set to 1000ms and you h ...

What is the best way to retrieve the height and width of a device's display in Angular 2 using Typescript

I came across this code snippet. Do you think it's valid? import {Component} from '@angular/core'; import {Platform} from 'ionic-angular'; @Component({...}) export MyApp { constructor(platform: Platform) { platform.ready().then ...

Explain a category of instance used in a template parameter

I am currently working on implementing a basic IOC container with type-checking capabilities. My goal is to pass the "register" method an abstract class type along with an instance of a derived type. In the "resolve" function, I aim to provide an abstrac ...

Ways to conceal a component based on a specific condition?

In my Angular 8 application, I need to dynamically hide a component based on a specific condition. The condition I want to check is: "status === EcheqSubmissionStatus.EXPIRED" Initially, I attempted the following approach: EcheqProcessComponent templat ...

Typescript-powered React component for controlling flow in applications

Utilizing a Control flow component in React allows for rendering based on conditions: The component will display its children if the condition evaluates to true, If the condition is false, it will render null or a specified fallback element. Description ...

The battle of environmental impact versus utility value in the realm of constants

Imagine having a collection of links that you want to organize in a separate file like this: export const SITE_LINKS = [ { text: 'About', path: '/about' }, { text: 'Projects', path: '/projects} ] You plan to utiliz ...

Angular: Granting an external module access to a provider

One of the modules I imported provides a service with an optional dependency. Although it being optional didn't affect my application, as it just prevented any errors from occurring when not present. Here's an example: import { FooModule } from ...

What is the abbreviation for indicating a return type as nullable?

Is there a way to use shorthand for nullable return types in TypeScript similar to using "name?: type" for parameters? function veryUsefulFunction(val?: string /* this is OK */) // but not this or other things I've tried. // is there a way a ...

Due to the feature in VISUAL STUDIO CODE that presents folders and subfolders at the same level

While working on my Angular project, I encountered an issue with creating a subfolder within a folder. Despite trying the same process in Windows Explorer, I faced the same problem of them appearing on the same level. What could be causing this discrepan ...

Issue: formGroup requires an input of type FormGroup. Please provide one; Error: Unable to access property 'controls' as it is undefined

In the process of building a login and registration form using Angular with .Net core, I encountered an error upon running the program. The error is showing up in the Browser Console tab. This is my userlog.component.ts file: import { Component, OnInit, O ...

Converting custom JSON data into Highcharts Angular Series data: A simple guide

I am struggling to convert a JSON Data object into Highcharts Series data. Despite my limited knowledge in JS, I have attempted multiple times without success: Json Object: {"Matrix":[{"mdate":2002-02-09,"mvalue":33.0,"m ...