推断泛型时避免加宽 [英] Avoid widening while inferring generic types
问题描述
以这个函数为例
type Decoder<A, B> = (v: A) => B
declare function test<Values, D extends Decoder<Values, unknown>>(options: {
values: Values,
decoder: D,
onDecoded: (decodedValue: ReturnType<D>) => unknown
}): void;
这个想法是 onDecoded
输入由 decoder
计算的值.但是:
The idea is that onDecoded
gets in input the value computed by decoder
. However:
test({
values: { a: "" },
decoder: values => values.a.length,
onDecoded: decodedValue => {
decodedValue // unknown
}
})
奇怪的是,如果我在 decoder
的定义中不使用 values
那么 decodedValue
具有正确的类型
Strangely enough, if I don't use values
in the definition of decoder
then decodedValue
has the correct type
test({
values: { a: "" },
decoder: () => 42,
onDecoded: decodedValue => {
decodedValue // number
}
})
下面是用相同的例子的操场链接
有没有办法让原始示例工作?
Is there a way to make the original example work?
推荐答案
这里的问题是编译器在它可以推断出一切之前就放弃了.您有一个对象,编译器需要从中推断出两个类型参数,但它不能一次全部完成.
The problem here is that the compiler gives up before it can infer everything. You've got a single object from which the compiler needs to infer two type parameters, but it can't do it all at once.
首先让我将您的签名重构为一个可能更易于分析的几乎等效的版本:
First let me refactor your signature to a nearly equivalent version that might be more straightforward to analyze:
declare function test<A, B>(options: {
values: A,
decoder: (a: A) => B,
onDecoded: (b: B) => unknown
}): void;
这与您的版本具有相同的推理问题,但谈论类型要容易一些.无论如何,编译器需要从您想要推断 A
和 options
值 passend 推断 A
和 B
code>B 来自它.它可以从values
的类型推断出A
,但除非decoder
的实现,否则它可能无法推断出B
> 碰巧不依赖于 A
,所以它失败了.
This has the same inference issue as your version, but it's a little easier to talk about the types. Anyway, the compiler needs to infer A
and B
from the options
value passend you want to infer A
and B
from it. It can infer A
from the type of values
, but it probably can't infer B
unless the implementation of decoder
happens not to depend on A
, so it fails.
类型推断的细节不是我的专家.但是如果这个问题有一个规范的答案,它在 microsoft/TypeScript#38872它使用非常相似的数据结构并遇到相同的问题.这被归类为 TypeScript 中的设计限制,因此如果不更改 test
函数或调用它的方式,可能无法解决此问题.
The details of type inference aren't something I'm an expert on. But if there is a canonical answer to this question, it's at microsoft/TypeScript#38872 which uses a very similar data structure and runs into the same problem. This is classified as a design limitation in TypeScript, so there's probably no way to fix this without altering your test
function or the way you call it.
改变你调用它的方式需要向编译器提供足够的类型信息以允许它工作.例如,如果你在调用 decoder
的输入参数时注释它的类型,你就可以了:
Altering the way you call it would involve giving enough type info to the compiler to allow it to work. For example, If you annotate the type of decoder
's input argument when you call it, you're fine:
test({
values: { a: "" },
decoder: (values: { a: string }) => values.a.length, // annotate
onDecoded: decodedValues => {
decodedValues // number
}
})
或者您可以更改 test()
的定义方式.我的一个建议是将 options
对象拆分为单独的参数.与单个参数相比,编译器更愿意为不同的函数参数花费多次推理.也许是这样的:
Or you can change how test()
is defined. One suggestion I have is to split the options
object out into separate parameters. The compiler is a little more willing to spend multiple inference passes for different function parameters than it is for a single parameter. Maybe like this:
declare function test2<A, B>(values: A,
decoder: (a: A) => B,
onDecoded: (b: B) => unknown
): void;
test2(
{ a: "" },
values => values.a.length,
decodedValues => {
decodedValues // number
}
)
test2({ a: "" },
() => 42,
decodedValues => {
decodedValues // number
}
)
那些推断完全按照您的意愿工作,如果必须,您可以使用 D
和 ReturnType
重写它们.
Those inferences work exactly as you want, and you can probably rewrite them using D
and ReturnType
if you must.
我想你走哪条路取决于你.无论如何,希望有所帮助;祝你好运!
Which way you go is up to you I guess. Anyway, hope that helps; good luck!
这篇关于推断泛型时避免加宽的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!