【问题标题】:Generic types in higher order functions高阶函数中的泛型
【发布时间】:2017-08-01 15:00:34
【问题描述】:

我想在高阶函数的内部函数中使用泛型类型作为参数。但是,当我这样做时,外部函数上的参数会丢失它们的类型(变为any)。

这是我正在尝试做的一个示例。我预计第 2 行会出错。

type TestFunc = <T>(outerArg: string) => (innerArg: T) => number
const test: TestFunc = <T>(outerArg) => (innerArg: T) => innerArg ? outerArg * 3 : 0 // no error because `outerArg` is `any` (unexpected)

innerArg 不是通用的时,我会收到预期的错误:

type TestFunc = (outerArg: string) => (innerArg: boolean) => number
const test: TestFunc = (outerArg) => (innerArg: boolean) => innerArg ? outerArg * 3 : 0 // error because `outerArg` is `string` (expected)

问题

在第一个示例的第 2 行,outerArg 的类型为 any。在第二个示例的第 2 行,outerArg 的类型为 string。为什么它们不同?


更多详情

我的目标是在评估外部函数时允许指定T。例如:

type TestFunc = <T>(outerArg: string) => (innerArg: T) => number
const test: TestFunc = (outerArg) => (innerArg) => innerArg ? outerArg.length * 3 : 0 
const test2: TestFunc = <T>(outerArg) => (innerArg: T) => innerArg ? outerArg * 3 : 0 // want error


const fourBool = test<boolean>('str')
console.log(fourBool(true))
console.log(fourBool(1)) // want error

const fourNum = test<number>('str')
console.log(fourNum(true)) // want error
console.log(fourNum(1))

这里是typescript playground

【问题讨论】:

  • 您的 outerArg 在顶部有类型字符串,在底部有数字。可能有点混乱。
  • 谢谢。我将进行编辑以使它们保持一致。

标签: typescript


【解决方案1】:

你有两个功能:

1) 具有返回数字的泛型类型的函数,

2) 和一个接受数字并返回泛型的函数。

正如您在 cmets 中提到的,您正在使用带有类型参数的箭头函数,但这对这两个函数都不起作用。您可以将内部函数声明为泛型,而将外部函数声明为类型参数:

type InnerFunc<T> = (inner: T) => number;
type TestFunc = <T>(outer: number) => InnerFunc<T>;

const test: TestFunc = <T>(outerArg: number) => (innerArg: T) => innerArg ? outerArg * 3 : 0;

const fourBool = test<boolean>(4);
fourBool(true); // OK
fourBool(1);    // error

【讨论】:

  • 谢谢!您能否详细说明为什么 outerArg 在第一个示例中隐含 any 而在第二个示例中没有。
  • noImplicitAny 在第一个示例中肯定会给我一个错误,但我将如何解决该错误?是在示例 1 的第 2 行上说 (outerArg: string) 的唯一方法吗?这似乎是多余的,因为 outerArg: string 在 TestFunc 类型定义中。
  • 我同意编译器应该说outerArgstring,即使你还没有调用它。这有助于您编写代码,以便您提前知道它应该是什么。我仍然认为这是一些编译器错误。我不知道这是否是设计使然,但这对我来说也没有意义。
  • 抱歉,其实我没有仔细看你的问题。我的回答并没有真正回答它,但我认为这种思路无论如何都不会解决您的问题。我相信这只是因为你的语法对于你想要做的事情是错误的。 type TestFunc&lt;T&gt; = (x: T) =&gt; ...type TestFunc = &lt;T&gt;(x: T) =&gt; ... 不同。前者是泛型,后者是类型断言。
  • 我不能做type TestFunc&lt;T&gt; = (outerArg: string) =&gt; (innerArg: T) =&gt; ...,因为我想在评估外部函数时指定T。相反,TestFunc&lt;T&gt; 强制我在声明外部函数时指定T。我很难找到任何关于在这样的函数之前使用类型断言的文档。我在断言什么类型为T
【解决方案2】:

这似乎是编译器或设计中的错误。

但现在这可能是一种解决方法,您可以将泛型类型附加到使用泛型类型的函数。

type TestFunc = (outerArg: string) => <T>(innerArg: T) => number

好的,这就是你想要接近它来做你想做的事情的方式:

type TestFunc = (arg1: string) => <T>(a: T) => number
type Number1 = (a: number) => number
const test: TestFunc = (arg1) => (a) => a ? (arg1 ? 1 : 2) : 0
const num1: Number1 = test("blah")
// OR
const newNum1 = <Number1>test("bleh")

如果你像以前一样放置arg1,上面会给你错误,它会得到你以前想要的通用形式。你会注意到,如果你错误地定义了“子类型”,它也会产生一个编译错误。

为了完整起见,这里有一个更简单的解决方案(几乎是 cdbajorin 在他的解决方案中所做的):

type TestFunc1 = <T>(arg1: string) => (a: T) => number
const test1: TestFunc1 = (arg1: string) => a => a ? (arg1 ? 1 : 2) : 0
const newNum2 = test1<number>("bluh")

【讨论】:

  • 谢谢@Jon49。这种方法似乎在正确的轨道上,因为它确实在我的示例的第 2 行给出了错误。但是,当我评估外部函数时,我看不到如何指定 T。我会用一个例子来更新原来的问题。
  • 具体来说,您的建议正确地在“更多详细信息”的代码块的第 3 行给出了错误,但在如下行给出了不需要的错误:const fourBool = test&lt;boolean&gt;(4)
  • 谢谢乔恩。在这种方法中,看起来我必须为每个内部函数创建一个单独的类型?例如,如果我希望内部函数的参数是布尔值,我必须这样做:type BoolFunc = (innarArg: boolean) =&gt; number?我知道这将如何工作,但它似乎很冗长。使用泛型不是为了避免冗长吗?
  • 理想情况下,TypeScript 中似乎存在一个错误,就像我之前所说的那样,所以你不会得到理想的解决方案。基本上,如果你想要一些简单的东西,你必须做 cdbajorin 在他的解决方案中所做的事情。在哪里定义 Arg1 两次。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-03-11
  • 2020-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-15
  • 1970-01-01
相关资源
最近更新 更多