我有一个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
}
})
Run Code Online (Sandbox Code Playgroud)
最初,我使用下面的类型,但是我想自己创建对象对象的值。当索引签名是平坦的时,推理就可以正常工作。
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
})
Run Code Online (Sandbox Code Playgroud)
是否有人对此有解释和/或建议?
这是一个与this question和this question类似的问题。当 TypeScript 对同一个类型参数(在第一个示例中,number以及stringfor A)进行多个协变推断时,它会尝试选择其中一个是其他类型参数的超类型;它不会推断联合,除非在推断是相同基本类型的文字类型的特殊情况下。如果 TypeScript 在其他情况下似乎推断出联合类型,那是因为某些其他语言功能在起作用。在 的情况下makeFlatFoo,该功能是对象文字类型的隐式索引签名生成,它采用属性a和的类型的并集b,即string | number。 string | number匹配B你会得到一个单一的推论string | number为B,一切工作。但是,在 中makeFoo,隐式索引签名的返回类型是Foo<string> | Foo<number>。当它匹配时Foo<A>,联合被打破,你得到两个不同的推论string和numberfor A。
虽然以下基于您的答案的示例编译没有错误:
function makeFoo<A, F extends Foo<A>>(foo: F) : F{
return foo
}
const result = makeFoo({
a: {
foo: '1'
},
b: {
foo: 3
}
});
Run Code Online (Sandbox Code Playgroud)
你会看到,A是{}和类型resultIS { a: { foo: string; }; b: { foo: number; }; },所以你还没有在对象转换为一个成功Foo<T>的类型。相反,您可以使用类型参数FA来捕获隐式索引签名的返回类型,然后使用分布式条件类型来提取foo属性的实际类型,如以下答案所示:
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
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
825 次 |
| 最近记录: |