使用此示例功能
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
计算得到的值。但是:
test({
values: { a: "" },
decoder: values => values.a.length,
onDecoded: decodedValue => {
decodedValue // unknown
}
})
奇怪的是,如果我在values
的定义中没有使用decoder
,那么decodedValue
的类型正确
test({
values: { a: "" },
decoder: () => 42,
onDecoded: decodedValue => {
decodedValue // number
}
})
这是一个带有相同示例的playground link
有没有办法使原始示例正常工作?
答案 0 :(得分:2)
这里的问题是编译器在推断所有内容之前就放弃了。您只有一个对象,编译器需要从该对象推断两个类型参数,但无法一次完成所有操作。
首先,让我将您的签名重构为几乎等效的版本,以使其更易于分析:
declare function test<A, B>(options: {
values: A,
decoder: (a: A) => B,
onDecoded: (b: B) => unknown
}): void;
与您的版本具有相同的推断问题,但是谈论类型要容易一些。无论如何,编译器需要从您想从中推断出A
和B
的{{1}}值密码中推断出options
和A
。它可以从B
的类型推断出A
,但是除非values
的实现不依赖于B
,否则可能无法推断出decoder
,所以失败了。
类型推断的细节不是我的专家。但是,如果对此问题有一个规范的答案,那就是microsoft/TypeScript#38872,它使用非常相似的数据结构并遇到相同的问题。这在TypeScript中被归类为设计限制,因此在不更改A
函数或调用方式的情况下,可能无法解决此问题。
更改您的调用方式将涉及向编译器提供足够的类型信息,以使其能够工作。例如,如果在调用时注释test
的输入参数的类型,就可以了:
decoder
或者您可以更改test({
values: { a: "" },
decoder: (values: { a: string }) => values.a.length, // annotate
onDecoded: decodedValues => {
decodedValues // number
}
})
的定义方式。我的一个建议是将test()
对象拆分为单独的参数。相比于单个参数,编译器更愿意为不同的函数参数花费多个推理过程。也许像这样:
options
这些推论完全按照您想要的方式工作,您可以根据需要使用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
重写它们。
我猜你想走哪条路。无论如何,希望能有所帮助;祝你好运!