请考虑以下代码:
meteor --version
为什么最后一行是语法错误?
提供的参数与呼叫目标的任何签名都不匹配。
为什么p推断为function ensure<TModel, TValue>(accessor: { (obj: TModel): TValue; }) {
}
interface Person {
firstName: string;
lastName: string;
}
ensure((p: Person) => p.firstName); // <-- this works
ensure<Person>(p => p.firstName); // <-- this does not work
而不是any
?
这里是TypeScript游乐场中代码的 link 。
答案 0 :(得分:4)
编辑:TypeScript 3.1的即将推出的功能将允许部分类型参数推断(使您引用的示例有效),有关详细信息,请参阅the pull request。
原始回答(适用于TypeScript&lt; 3.1):
第一个例子起作用的原因是因为编译器根据传入的匿名lambda函数的类型推断出两个泛型。
不幸的是,在使用TypeScript中的通用函数时,无论是全部还是全无 - 您必须提供:
请注意,如果无法推断出类型,则默认情况下假定其类型为:Object
,例如:
function example<T>(a: any): T {
return a as T;
}
let test = example(123);
above example中的变量test
将属于{}
类型。
指定泛型类型或指定方法中参数的类型都是处理此问题的正确方法:
ensure<Person, string>(p => p.firstName);
ensure((p: string) => p.firstName);
您引用的错误是正确的,因为:函数ensure
中不存在仅包含一个通用的签名。
原因是您可以使用具有不同数量的泛型类型参数的替代签名的函数:
interface Example {
ensure<TModel, TValue>(accessor: { (obj: TModel): TValue; }): TValue;
ensure<TModel>(accessor: { (obj: TModel): any; }): any;
}
interface Person {
firstName: string;
lastName: string;
}
let test: Example;
// the method 'ensure' has now 2 overloads:
// one that takes in two generics:
test.ensure<Person, string>((p: Person) => p.firstName);
// one that takes only one generic:
test.ensure<Person>(p => p.firstName);
// when not specified, TypeScript tries to infer which one to use,
// and ends up using the first one:
test.ensure((p: Person) => p.firstName);
Playground上述内容。
如果TypeScript没有强制执行签名匹配,它就不知道应该选择哪个签名。
现在回答你问题的另一部分:为什么p
在调用函数时被假定为any
而没有明确说明泛型:
一个原因是编译器不能对其可能的类型做出任何假设,TModel
是不受约束的,并且字面上可以是任何类型,因此p
的类型是any
。
您可以将泛型方法约束到接口,如下所示:
ensure<TModel extends Person, TValue>(accessor: { (obj: TModel): TValue; });
现在,如果您在未指定参数类型或泛型类型的情况下调用该函数,则会将其正确推断为Person
:
ensure(p => p.firstName); // p is now Person
希望这完全回答你的问题。
答案 1 :(得分:0)
由于我遇到了同样的问题,实际上确实需要一个解决方案,而不仅仅是一个解释,这就是我最终如何做到这一点:
function NewTag<Attributes>(): { <Prototype>(P: Prototype): Prototype & { attributes: Attributes, view(attributes: Attributes): any } } {
return NewTag_Internal;
}
function NewTag_Internal(p) { // simplified to make it fit
return <any>{ attributes: Object.create(null), view() { }, __proto__: p }
}
var elm = NewTag<{ id:string }>()({
log(str: string) {
console.log(str);
}
})
elm.log(elm.attributes.id)
每次要使用该函数时都需要一对括号,但在我的情况下,因为它只是为了声明事物(因此在代码中不是很普遍)并且启用完全自动完成=&gt;这是值得的。