Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I create generic function that only accepts instances of a type with a specific nested type?

Tags:

typescript

UPDATE

I made a walkaround myself, by move nested type from static field to instane field, which seems to work but was imperfect: https://tsplay.dev/w26Xjw


UPDATE

The main purposes are:

  1. I want create a generice function<T,U> which accept two arguments;
  2. The types of these arguments could be connected/related
  3. While I pass the first argument to the with type of T it should auto infer the type U of the second argument

So the questions would be:

  1. How to properly make these two types connected/related? I tried to use nested type, as my previous post below, which may not be a suitable way but I don't how. Any advises are welcome.
  2. Once these types are connected/related, if possible, could the later one be auto infered from the first one?

So that when the user passes the first argument the generic function should restrict the type of the second argument.

Something similar in C++ I'd like to write:

class A {
    class InnerType {}
}

template<typename T>
void test(const T& first, const typename T::InnerType& second);

I want to created a generic function which accepts two args:

  1. The first arg should be a type of T and there would be a nested type T.Param
  2. The second arg should be a type of T.Param
class A {
    static Param = class {
        ....
    }
}

class B {
    static Param = class {
        ....
    }
}

// How can I make the T/P match the requirements explain above?
function test<T, P>(first:T, second:P) {
}

And then such invoking should pass:

test(new A(), new A.Param()); // OK
test(new A(), new B.Param()); // Fail
test(new B(), new A.Param()); // Fail
test(new B(), new B.Param()); // OK
like image 585
jayatubi Avatar asked Jun 18 '21 07:06

jayatubi


People also ask

How do you create a generic type?

A Generic Version of the Box Class To update the Box class to use generics, you create a generic type declaration by changing the code "public class Box" to "public class Box<T>". This introduces the type variable, T, that can be used anywhere inside the class.

How do you pass a generic type as parameter TypeScript?

Assigning Generic Parameters By passing in the type with the <number> code, you are explicitly letting TypeScript know that you want the generic type parameter T of the identity function to be of type number . This will enforce the number type as the argument and the return value.

What is type T in TypeScript?

This article opts to use the term type variables, coinciding with the official Typescript documentation. T stands for Type, and is commonly used as the first type variable name when defining generics. But in reality T can be replaced with any valid name.


1 Answers

There are a couple of different ways to do it, depending in part on which argument you want to drive the process. You can drive it with the first argument:

function test1<T extends {Param: unknown}, P extends T["Param"]>(first: T, second: P) {
    // ...
}

or the second:

function test2<P, T extends {Param: P}>(first: T, second: P) {
    // ...
}

With those, you get these results:

test1(A, A.Param); // works
test1(B, A.Param); // error on second argument

test2(A, A.Param); // works
test2(B, A.Param); // error on first argument

Playground with both


Note that because TypeScript's type system is structural (based on the shapes of types) rather than nominal (based on the names of types), in the playground link I added things to A.Param and B.Param so they wouldn't have the same shape. Otherwise, since their shapes are the same (empty classes), either can be used in place of the other.

like image 119
T.J. Crowder Avatar answered Oct 18 '22 09:10

T.J. Crowder