【问题标题】:How can I create generic function that only accepts instances of a type with a specific nested type?如何创建只接受具有特定嵌套类型的类型实例的泛型函数?
【发布时间】:2021-06-18 07:24:03
【问题描述】:

更新

我自己做了一个解决方案,将嵌套类型从静态字段移动到实例字段,这似乎可行但并不完美:https://tsplay.dev/w26Xjw


更新

主要用途是:

  1. 我想创建一个接受两个参数的泛型函数;
  2. 这些参数的类型可以是connected/related
  3. 当我将第一个参数传递给 T 的类型时,它应该自动推断出第二个参数的类型 U

所以问题是:

  1. 如何正确地使这两种类型连接/关联?我尝试使用嵌套类型,就像我在下面的上一篇文章中一样,这可能不是一种合适的方式,但我不知道。欢迎任何建议。
  2. 一旦这些类型连接/相关,如果可能的话,后一个类型是否可以从第一个类型自动推断?

这样当用户传递第一个参数时,泛型函数应该限制第二个参数的类型。

我想在 C++ 中写一些类似的东西:

class A {
    class InnerType {}
}

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

我想创建一个接受两个参数的通用函数:

  1. 第一个参数应该是T 的类型,并且会有一个嵌套类型T.Param
  2. 第二个参数的类型应该是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) {
}

然后这样的调用应该通过:

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

【问题讨论】:

  • 请提供更多细节。如您所见,试图回答您问题的两个人仍然不明白您想要什么。根据 StackExchange 规则,您应该提供一个 complete 示例,以准确显示您正在尝试做什么。例如上面你需要显示test 对参数的作用。正如我在下面和您的欺骗问题 (stackoverflow.com/questions/68032159/…) 中已经说明的那样,您所提出的问题是不可能的。
  • @Inigo 帖子已更新。希望这次能让你明白。
  • 好的,这很有帮助,因为您刚刚承认使用“嵌套类型”并不重要,这只是您正在尝试的东西。重要的是解释您的根本问题,而不是要求我们解决您可能首先出错的解决方案。
  • @jayatubi 我添加了我自己的答案,让我们继续在那里聊天)

标签: typescript


【解决方案1】:

有几种不同的方法可以做到这一点,部分取决于您想要推动流程的参数。你可以用第一个参数来驱动它:

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

或第二个:

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

有了这些,你会得到这些结果:

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


请注意,因为 TypeScript 的类型系统是 structural(基于类型的形状)而不是名义上的(基于类型的名称),所以在操场链接中我添加了一些东西到 A.Param 和B.Param 所以他们不会有相同的形状。否则,由于它们的形状相同(空类),因此可以使用其中一个来代替另一个。

【讨论】:

  • 它看起来非常完美。我可以使这项工作不仅适用于类本身,而且适用于类的实例?如test1(new A(), new A.Param());
  • @jayatubi - 我相信你可以,但遗憾的是我不知道怎么做。恐怕我不擅长构造函数类型。我试过InstanceType&lt;T&gt;,但没有运气。您是否希望我删除答案,以免人们觉得问题已得到回答?
  • (我建议用你想工作的电话和你想失败的电话来编辑问题。)
  • 尽管这个答案不符合我的要求,但它非常有启发性。我想保留它。
  • 问题是无法从实例推断构造函数类型,尤其是静态属性。
【解决方案2】:

This 的回答可能对你很感兴趣。

下一个解决方案呢?

class ParamA {
    foo: number = 0
}

class ParamB {
    bar: string = ''
}

class A {
    InnerType = () => ParamA
}

class B {
    InnerType = () => ParamB
}


function test1<
    T extends { InnerType: any },
    P extends InstanceType<ReturnType<T["InnerType"]>>,
    >(first: T, second: P) {
    // ...
}

test1(new A(), new ParamA());
test1(new B(), new ParamA()); // error
test1(new A(), new ParamB()); //error
test1(new B(), new ParamB());

【讨论】:

  • FakeFunc 可以省略,然后这个答案符合我的需要。谢谢:p
【解决方案3】:

作为对这个伟大的 answer 的补充。

要让你的函数只接受这些类型的实例,你可以将它们定义为:

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

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

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

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

playground link

【讨论】:

  • 我希望两个参数都是实例而不是类型,也就是第一个参数应该是 new A() 而不是 A
【解决方案4】:

你不能。

虽然我确实有一个解决方法。

注意:我将我的答案复制到your duplicate question,因为我花了很多时间来整理它。我在下面给出的示例是基于该问题的,因此与上面的 完全相同 不一样,但它很接近并且完全涵盖了问题。

Typescript 不包含静态成员作为类表示的类型的一部分。 The following is in TS Playground 这样您就可以看到错误消息。我添加ParamClass 是因为我们需要一些 方法来引用泛型函数中Param 的类型。

class ParamClass {
    foo: number = 0
}

class A1 {
    Param: typeof ParamClass = class {
        foo: number = 0
    }
}

class A2 {
    static Param: typeof ParamClass = class {
        foo: number = 0
    }
}

const a1: A1 = {}  //fails type check: Property 'Param' is missing in type '{}' but required in type 'A1'.(2741)

const a2: A2 = {}  //passes type check, static class member is not part of type A2


function test1<T extends {Param: typeof ParamClass}>(c: T): T {
  return c
}

test1(new A1())  //passes

test1(new A2())  //fails: Property 'Param' is missing in type 'A2' but required in type '{ Param: typeof ParamClass; }'.(2345)


// But what if we define T to have a static member? 
// You can't. It's not legal Typescript:
//
//      'static' modifier cannot appear on a type member.(1070)
//
function test2<T extends {static Param: typeof ParamClass}>(c: T): T {
  return c
}

我的解决方法

in playground

class ParamClass {
    foo: number = 0
}

class A3 {
    static Param: typeof ParamClass = class {
        foo: number = 0
    }
    get Param() { 
        return A3.Param
    } 
}

class B {
}


function test1<T extends {Param: typeof ParamClass}>(c: T): T {
  const param = c.Param
  // now you can do whatever you want with class Param, e.g. 
  const paramInstance = new param() 
  return c
}

test1(new A3())  // passes type check

test1(new B())  // fails type check

【讨论】:

  • 感谢您的解释。我想要的是限制将传递给函数的argument,而不是在函数内部创建一个新的。换句话说,我想让测试功能在这种模式下工作test(new A3(), new A3.Param())。我想在调用之前准备好论点。目标是:当用户传递第一个参数时,第二个参数应该被第一个限制。
猜你喜欢
  • 2020-11-06
  • 1970-01-01
  • 1970-01-01
  • 2020-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多