推断泛型时避免加宽 [英] Avoid widening while inferring generic types

查看:29
本文介绍了推断泛型时避免加宽的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以这个函数为例

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;

这与您的版本具有相同的推理问题,但谈论类型要容易一些.无论如何,编译器需要从您想要推断 Aoptions 值 passend 推断 ABcode>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
    }
)

那些推断完全按照您的意愿工作,如果必须,您可以使用 DReturnType 重写它们.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆