简体   繁体   English

未按预期推断 TypeScript 类型

[英]TypeScript type not inferred as expected

I have a use case where I want to define a set of filters as an object.我有一个用例,我想将一组过滤器定义为一个对象。 I want to know which filters there are exactly and have auto completion for it, but in the generic handling of the filters, it can be just be any key as long as it adheres to certain types for their values.我想知道究竟有哪些过滤器并为其自动完成,但在过滤器的通用处理中,它可以是任何键,只要它遵循某些类型的值即可。 I thought to use generics and extends, which works to a certain degree, but there are a few situations where it does not unify based on the type it is extending like I would expect it too.我想使用泛型和扩展,这在一定程度上是有效的,但在某些情况下,它不会像我期望的那样基于它扩展的类型进行统一。 Here is a simplified example of what I attempted and what goes wrong:这是我尝试过的操作和出错的简化示例:

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")
}

I expected it to infer boolean rather than false (for foo ), and string[] rather than never[] (for bar ).我希望它推断出boolean而不是false (对于foo ),和string[]而不是never[] (对于bar )。 I assume extends does not unify the types quite as I expected.我认为 extends 并没有像我预期的那样统一类型。 Is there a way to achieve this in TypeScript?有没有办法在 TypeScript 中实现这一点? Meaning I would get full knowledge that the instance of Bar has the properties foo and bar , but inside the class itself, it only knows the generic type (as in, some object, rather than knowing its specific keys).这意味着我将完全了解Bar的实例具有属性foobar ,但在类本身内部,它只知道泛型类型(例如,某些对象,而不知道其特定键)。

The answer given by @jcalz in the comments to my question was the answer I was looking for. @jcalz 在对我的问题的评论中给出的答案就是我正在寻找的答案。 I ended up implementing it similarly to the example given by him:我最终以类似于他给出的例子来实现它:

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")
} 

As he mentioned, per PR ms/TS#10676 , literal types will be used whenever applicable and will be as specific (narrow) as possible.正如他所提到的,根据 PR ms/TS#10676 ,文字类型将在适用时使用,并且尽可能具体(窄)。 AFAIK there is no way to have TypeScript widen literal types automatically, so we have to do it ourselves via T extends Type ? Type : ... AFAIK 没有办法让 TypeScript 自动T extends Type ? Type : ...文字类型,所以我们必须通过T extends Type ? Type : ...自己做T extends Type ? Type : ... T extends Type ? Type : ... tricks. T extends Type ? Type : ...技巧。 I tried it and it works well for more complex types too, such as my use case (has recursive types and such).我试过了,它也适用于更复杂的类型,例如我的用例(具有递归类型等)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM