2016-07-31 95 views
1

考虑下面的代码:打字稿泛型:参数类型推断

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 

为什么是最后一行语法错误?

提供的参数不匹配呼叫目标的任何签名。

为什么推断p是any而不是Person

这里有一个link到TypeScript操场中的代码。

+0

附注:您可以对函数类型执行此操作:'访问者:(obj:TModel)=> TValue' –

回答

2

第一个示例工作的原因是因为这两种泛型都是由编译器根据匿名lambda函数中传入的类型推断的。

不幸的是,在打字稿消费类功能时,它的全有或全无 - 你必须提供两种:

  • 类型匹配函数的签名的所有仿制药,或
  • 没有仿制药,如果你希望编译器“猜测”最适合您的呼叫匹配函数签名,同时自动推断类型(如果这样的推断是在所有可能的)

注意,如果一个类型不能推断它是默认的假设是的类型: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 

希望能够完全回答你的问题。

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) 

有每次一对括号中的成本想要使用函数,但在我的情况,因为它只是声明的东西(所以不是很普遍的代码),并启用全自动完成=>交易是值得的。