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

Encountered an issue when attempting to establish a connection with the REST

I am encountering an issue with connecting to a web service deployed on an Apache server using Jersey. The error message I receive is: Failed to load http://192.168.1.200:8199/CheckinnWeb/webapi/myresource/query: No 'Access-Control-Allow-Origin' ...

Using TypeScript import statements instead of the <reference path...> in an ASP.NET Core web application: A step-by-step guide

Understanding the Setup I initially had a functional TypeScript Hello World in my ASP.NET Core Web application. To compile TypeScript, I used the NuGet package "Microsoft.TypeScript.MSBuild" Version="4.4.2" along with a tsconfig.json f ...

Is TypeScript being converted to JavaScript with both files in the same directory?

As I begin my journey with TypeScript in my new Angular project, I find myself pondering the best approach for organizing all these JS and TS files. Currently, it appears that the transpiler is placing the .js files in the same directory as the correspondi ...

Craft a unique typings file tailored to your needs

After recently creating my first published npm package named "Foo", I encountered some difficulties while trying to consume it in a TypeScript project. The tutorials on how to declare modules with custom typings were not clear enough for me. Here are the k ...

Getting the current page name within the app.component.ts file in Ionic 3 - a simple guide

Is it possible to retrieve the current active page name within the app.component.ts file in Ionic without having to add code to any other pages? ...

Updating the state of Formik

Currently, I'm knee-deep in a React project that requires a slew of calculations. To manage my forms, I've turned to Formik, and for extra utility functions, I've enlisted the help of lodash. Here's a peek at a snippet of my code: impor ...

It is not possible to use an async function with Ionic 4 ToastController buttons

Incorporating a new function into the handler of my ToastController button to return a promise (in this case: this.navCtrl.navigateForward()) is something I need assistance with. This is what my code looks like: const toast = await this.toastController.c ...

Utilizing a created OpenAPI client within a React application

Using the command openapi-generator-cli generate -i https://linktomybackendswagger/swagger.json -g typescript-axios -o src/components/api --additional-properties=supportsES6=true, I have successfully generated my API client. However, despite having all th ...

Setting a maximum limit for selections in MUI Autocomplete

Code updated to display the complete script logic. I want to restrict the number of selections based on a value retrieved from the database, but in this example, I have set it to 3 manually. The error message I'm encountering is "Cannot read properti ...

The ngOnChanges method fails to exhibit the anticipated modifications in a variable

Trying to grasp the concept of the ngOnChanges() callback, I created an example below. Despite having values for the attributes title and content in the Post interface during compile time, I do not see any logs from ngOnChanges. Please advise on the corre ...

Is there a way to help my KafkaJS consumer stay patient while waiting for a broker to become available?

My KafkaJS consumer setup looks like this: // Create the kafka client const kafka = new Kafka({ clientId, brokers, }); // Create the consumer const consumer = this.kafka.consumer({ groupId, heartbeatInterval: 3000, sessionTimeout: 30000, }); // ...

Error: Oops! The super expression can't be anything other than null or a function in JavaScript/TypeScript

I am facing an issue with class inheritance in my code. I have a class A that extends class B, which in turn extends class C. Whenever I try to create a new instance of class A within a function, I encounter the following error message: Uncaught TypeError: ...

What is causing Angular to consistently display the first object in the array on the child view, while the child .ts file correctly prints information from a different object?

Once a card of any object is clicked, the information of that specific object will be printed to the console. However, the child view will only display the details of the first object in the array it retrieves from. All pages are included below. A visual e ...

Suggestions for importing by Typescript/VS Code

Imagine you have a file called a.ts that contains 4 named imports: export const one = 1 export const two = 2 export const three = 3 export const four = 4 Next, you have a file named b.ts and you want to import some variables from a.ts. import {} from &a ...

Is there a way to verify the presence of multiple array indices in React with TypeScript?

const checkInstruction = (index) => { if(inputData.info[index].instruction){ return ( <Text ref={instructionContainerRef} dangerouslySetInnerHTML={{ __html: replaceTextLinks(inputData.info[index].instruction) ...

Error in Protractor Typescript: The 'By' type does not share any properties with the 'Locator' type

https://i.stack.imgur.com/8j2PR.png All the different versions Error. Protractor version : 5.2.0 npm : 3.10.10 node :6.9.5 typescript :2.6.0 The 'By' type does not share any properties with the 'Locator' type What is the solution to ...

Using *ngFor to populate an array in an ion-list within Ionic 2

Hi there, I'm currently learning Ionic 2 and I recently created an array that I want to loop through in an ion-list. This is my produk.ts import { Component } from '@angular/core'; import { NavController, NavParams } from 'ionic-angul ...

The slice() method in arrays provides a reference to the elements rather than copying

In my file, I am exporting an object in the following manner: export const LINECHART2_DATA = { series: [{ data: [], name: 'HR', }, { etc... }] } The way I import it is like this: import { LINECHART2_DAT ...

Combining tuples with their corresponding data types

Trying to define a new type: type Union = [1, "one"] | [1, "first"] | [2, "two"] type GetTuple<T, U> = Extract<T, [U, ...unknown[]]>; type ObjectFromUnion<T extends Union[0] = Union[0]> = { number: T, word: ?? } Looking to utiliz ...

Do constructors in TypeScript automatically replace the value of `this` with the object returned when using `super(...)`?

I’m having some trouble grasping a concept from the documentation: According to ES2015, constructors that return an object will automatically replace the value of “this” for any instances where “super(…)” is called. The constructor code must ...