【问题标题】:TypeScript type not inferred as expectedTypeScript 类型未按预期推断
【发布时间】:2021-12-21 15:10:09
【问题描述】:

我有一个用例,我想将一组过滤器定义为一个对象。我想知道究竟有哪些过滤器并为其自动完成,但在过滤器的通用处理中,只要它的值符合某些类型,它就可以是任何键。我想使用泛型和扩展,这在一定程度上有效,但在某些情况下,它并没有像我期望的那样根据它扩展的类型进行统一。这是我尝试过的以及出了什么问题的简化示例:

type Foo = { [key: string]: boolean | string[] }
class Bar<F extends Foo> {
  constructor(public arg: F) {}
}
const bar = new Bar({ foo: false, bar: [] })
// Reported as always being false.
console.log(bar.arg.foo)
for (const n of bar.arg.bar) {
  // Property 'includes' does not exist on type 'never'. ts(2339)
  n.includes("x")
}

我希望它推断出boolean 而不是false(对于foo),以及string[] 而不是never[](对于bar)。我假设 extends 并没有像我预期的那样统一类型。有没有办法在 TypeScript 中实现这一点?这意味着我将充分了解Bar 的实例具有foobar 属性,但在类本身内部,它只知道泛型类型(例如,某个对象,而不是知道它的特定键) .

【问题讨论】:

  • TypeScript 需要符合 ms/TS#10676 的窄类型(无论如何对于 boolean)。您可以强制编译器计算F 的扩展版本,例如this,但使用起来有些不符合人体工程学,至少对于Bar 的实现者而言。这是否满足您的需求,或者您是否正在寻找其他东西(如果是,您能否详细说明)?
  • 还有this approach 描述了Bar 类构造函数的所需类型,然后只是断言给定的类构造函数值可以分配给它。对于Bar 的用户来说,这个可能更好,但对于实施者来说,它又不符合人体工程学。
  • @jcalz 我有点希望这是一个足够常见的用例,不必像那样拼写出来。但是,它确实运作良好,所以如果您提出它作为答案,我会接受它。谢谢!

标签: typescript types typescript-generics


【解决方案1】:

@jcalz 在 cmets 中对我的问题给出的答案是我一直在寻找的答案。我最终以类似于他给出的示例的方式实现它:

type Foo = { [key: string]: boolean | string[] }

type WidenFoo<T extends Foo> = { [K in keyof T]: T[K] extends boolean ? boolean : string[] };

class Bar<F extends Foo> {
    arg: WidenFoo<F>
    constructor(arg: F) {
        this.arg = arg as WidenFoo<F>;
    }
}
const bar = new Bar({ foo: false, bar: [] })

bar.arg.foo // boolean
bar.arg.bar // string[]

console.log(bar.arg.foo)
for (const n of bar.arg.bar) {    
    n.includes("x")
} 

正如他所提到的,根据 PR ms/TS#10676,将在适用的情况下使用文字类型,并将尽可能具体(窄)。 AFAIK 没有办法让 TypeScript 自动扩展文字类型,所以我们必须通过 T extends Type ? Type : ... 技巧自己做。我试过了,它也适用于更复杂的类型,比如我的用例(有递归类型等)。

【讨论】:

    猜你喜欢
    • 2019-09-04
    • 2018-07-03
    • 2023-03-28
    • 2019-09-17
    • 1970-01-01
    • 2021-06-13
    • 1970-01-01
    • 2020-07-06
    • 2017-10-04
    相关资源
    最近更新 更多