我有一种TypeScript无法推断出的类型。
interface Foo<A> {
[name: string] : {
foo : A
}
}
function makeFoo<A>(foo: Foo<A>) : Foo<A>{
return foo
}
// Works fine when manually specifying the types
const manuallyTyped : Foo<string | number> = {
a: {
foo: '1'
},
b: {
foo: 3
}
}
// ERROR, Can't infer type as Foo<string | number>
makeFoo({
a: {
foo: '1'
},
b: {
foo: 3
}
})
最初,我使用了下面的类型,但我希望使对象对象本身的值。当索引签名是平坦的时,推理工作得很好。
interface FlatFoo<B> {
[name: string] : B
}
function makeFlatFoo<B>(bar: FlatFoo<B>): FlatFoo<B>{
return bar
}
// Correctly has type FlatFoo<string | number>
const inferred = makeBar({
a: 'a',
b: 2
})
是否有人对此有解释和/或建议?
发布于 2018-09-14 21:28:40
这是一个类似于this question和this question的问题。当TypeScript为相同类型的参数(在第一个示例中,number
和string
用于A
)进行多个协变推理时,它尝试选择其中一个是其他参数的超级类型;它不会推断出一个联合,除非在特殊情况下,推理是相同原语类型的文字类型。如果TypeScript在其他情况下似乎推断出一个联合类型,那是因为其他一些语言特性起作用了。在makeFlatFoo
中,该特性是对象文字类型的隐式索引签名生成,它将属性a
和b
的类型(即string | number
)的类型结合在一起。string | number
与B
相匹配,您可以得到B
的string | number
的一个推论,一切都正常。但是,在makeFoo
中,隐式索引签名的返回类型是Foo<string> | Foo<number>
。当这与Foo<A>
相匹配时,联合就会被分解,您将得到string
和number
对于A
的两个不同的推论。
虽然以下基于您的答案的示例编译时没有错误:
function makeFoo<A, F extends Foo<A>>(foo: F) : F{
return foo
}
const result = makeFoo({
a: {
foo: '1'
},
b: {
foo: 3
}
});
您将看到A
是{}
,result
的类型是{ a: { foo: string; }; b: { foo: number; }; }
,所以还没有成功地将对象转换为Foo<T>
类型。相反,可以使用类型参数FA
来捕获隐式索引签名的返回类型,然后使用分布式条件类型提取foo
属性的实际类型,如this answer中的那样。
interface FlatFoo<FA> {
[name: string]: FA;
}
type FooPropTypes<FA> = FA extends { foo: infer A } ? A : never;
function makeFoo<FA extends {foo: unknown}>(foo: FlatFoo<FA>) : Foo<FooPropTypes<FA>> {
return <any>foo
}
发布于 2018-09-13 15:21:02
看起来您可以通过更新makeFoo
来将整个输入捕获为泛型来修复这个问题。
function makeFoo<A, F extends Foo<A>>(foo: F) : F{
return foo
}
这与我所期望的不完全一样,但结构类型的效果很好。
https://stackoverflow.com/questions/52322803
复制