Typescript不从外部函数推断类型

Typescript不从外部函数推断类型,typescript,type-inference,Typescript,Type Inference,有没有一种方法可以帮助编译器推断出如下内容: class Base<T> { children(... children: (A<any> | B<T>)[]) { return this } onVisit(handler:(context: T)=>void) { return this } } class A<T> extends Base<T> { constructor( public cont

有没有一种方法可以帮助编译器推断出如下内容:

class Base<T> {
    children(... children: (A<any> | B<T>)[]) { return this }
    onVisit(handler:(context: T)=>void) { return this }
}

class A<T> extends Base<T> {
    constructor( public context: T ) { super() }
}

class B<T> extends Base<T> {}

const foo = new A({ bar: 1 })
    .children(
        new A({baz:2}).onVisit(({baz})=>{}),
        new B().onVisit(({bar})=>{}) // Fails here because it infers that the instance as type B<unknown> instead of B<{bar:number}>
    )
类基{
儿童(…儿童:(A | B)[]){返回此}
onVisit(handler:(context:T)=>void){returnthis}
}
A类扩展基础{
构造函数(公共上下文:T){super()}
}
类B扩展了基{}
const foo=新的A({bar:1})
.儿童(
新的A({baz:2}).onVisit({baz})=>{}),
new B().onVisit(({bar})=>{})在这里失败,因为它推断实例的类型是B而不是B
)
这似乎不是因为编译器无法从调用函数中提取某些上下文,因为这是可行的:

function f1<T>(p: T, ...h: ((p:T) => void)[]) { }
function f2<T>(h: (p: T) => void) { return h }

f1({ a: 1 }, 
    f2((p) => { console.log(p.a) }),
)
函数f1(p:T,…h:((p:T)=>void)[]){
函数f2(h:(p:T)=>void){returnh}
f1({a:1}),
f2((p)=>{console.log(p.a)}),
)
我可能会完全困惑(很可能),但如果后者有效,那么前者也应该如此。

当编译器检查表达式的内容以确定它是什么类型时,TypeScript中的“正常”类型推断就会发生。它会在表达式内部执行此操作。这往往与运行时发出的程序的控制流的方向相同。如果我有这样的代码

let x = ""; 
let y = x.length;
[""].map(z => z.length)
,编译器确定
“”
(将是
“”
)的类型,并使用该类型确定
x
(将是
字符串
)的类型。然后,编译器通过检查
string
(将是
number
)上
length
属性的类型来确定
x.length
的类型,然后使用它来确定
y
(将是
number
)的类型。早期表达式决定了后期表达式的类型

但是,在某些情况下编译器会这样做。在上下文类型中,当编译器检查表达式的上下文以确定它是什么类型时,就会发生推理。这样做看起来超出了表达式的范围。在运行时,这往往与所发出程序的控制流的方向相反。如果我有这样的代码

let x = ""; 
let y = x.length;
[""].map(z => z.length)
,回调中的变量
z
没有显式类型,但无法通过检查回调的内容来确定其类型。但是,由于
string
值数组的
map()
方法期望其参数是接受
string
属性的回调,因此编译器使用此上下文为
z
指定
string
的类型。在这里,从某种意义上讲,后面的表达式用于确定前面表达式的类型

但是上下文类型是脆弱的。它只发生在少数特殊情况下,通过将必要的上下文信息“父亲远离”需要推断其类型的表达式,很容易被打破:

let cb = z => z.length; // error, z is implicitly any
[""].map(cb); // oops, now we have an any[]
这里正在创建相同的回调,
z=>z.length
。但它本身正在发生。它唯一使用的地方是作为
string[]
map()
方法的回调。因此,从技术上讲,编译器可以说,“那么,
cb
应该是一个类似
(val:string)类型的回调=>任何
,因此我们向上一行给出该类型的cb,然后从该上下文中我们可以推断z必须是一个
字符串
。但这不会发生。推断失败


上下文类型化的一个地方是通过使用上下文的预期返回类型来推断被调用泛型函数的类型参数。您的
f2()
情况会这样做,类似于此:

declare function f<T>(): T;
const numGood: number = f(); // infers number for T
declare function g<T>(): { a: T };
const numBad: number = g().a; // error!
但是,在您的
new B().onVisit
情况下,您试图让编译器根据返回值的属性(或方法)类型,在上下文中推断函数(或构造函数调用)的类型参数。这显然破坏了上下文推断。类似于:

declare function f<T>(): T;
const numGood: number = f(); // infers number for T
declare function g<T>(): { a: T };
const numBad: number = g().a; // error!
声明函数g():{a:T};
const numBad:number=g().a;//错误!
函数
g()
。推理失败。
T
默认为
unknown
,您会得到一个错误


我不能指出一个特定的GitHub问题或文档,它概述了您可以和不能期望上下文类型工作的时间。有一些未遂事件,比如它描述了编译器执行上下文类型能力有限的一些情况。但是我还没有看到一个规范的答案,它说“
f2
yes,
new B”()在线访问
no”

除非你遇到了一个类型推断失败的情况,你可以考虑手动注释或指定这些类型。如果你不想让<代码>新的B-()/代码>产生一个<代码> B<代码>,那么写<代码>新的()(代码)>它应该开始工作。乏味。当然。但是至少它是有效的!


当编译器检查表达式的内容以确定它是什么类型时,TypeScript中的“正常”类型推断会发生。它会在表达式内部执行此操作。这往往与运行时发出的程序的控制流的方向相同。如果我有

let x = ""; 
let y = x.length;
[""].map(z => z.length)
,编译器确定
“”
(将是
“”
)的类型,并使用该类型确定
x
(将是
string
)的类型。然后,编译器通过检查
string
上的
length
属性的类型来确定
x.length
的类型(将是
number
),然后使用它来确定
y
(将是
number
)的类型。前面的表达式确定后面表达式的类型

然而,也有一些情况