我尝试了一种对我来说很奇怪的行为。
让我们考虑以下示例(test it in Typescript playground):
abstract class FooAbstract {
abstract bar() {}
}
class Foo extends FooAbstract {
bar() {
return { bar: 'bar' };
}
}
class FooMaker<FOO extends FooAbstract> {
constructor(public foo: FOO) {}
bar() {
return this.foo.bar();
}
baz = () => {
return this.foo.bar();
}
}
let foo = new Foo();
let result = foo.bar();
let foomaker = new FooMaker(new Foo);
let foo2 = foomaker.foo; // Type "Foo", OK
let result1 = foomaker.foo.bar(); // Type "{bar: string}", OK
let result2 = foomaker.bar(); // Type "{}", ???
let result3 = foomaker.baz(); // I've seen comments about using a lambda... Not better
result2
和result3
的输入类似于抽象bar
函数({}
)。似乎this
未被解析为具体类Foo
,而是作为抽象类FooAbstract
。而foo2
的类型显示正确解析了类foo
属性。
发生了什么事?我做错了吗?
作为事后的想法,这个案例可以像(Test it in Typescript playground)那样重新制定:
class Foo {
bar() {
return { bar: 'bar' };
}
getThis(): this {
return this
}
}
class Wrapper {
bar<FOO extends { bar(): {} }>(foo:FOO) {
return foo.bar();
}
}
let wrapper = new Wrapper();
let result = (new Foo()).bar();
let result2 = wrapper.bar(new Foo());
result
的类型为{bar:string}
result2
的类型为{}
(来自界面)
wrapper.bar
的类型为Wrapper.bar<Foo>(foo: Foo): {}
通过此示例,更清楚的是,即使知道FOO
被键入为Foo
, Typescript 也会使用FOO
定义而不是其显式类型bar
返回类型。
好的,在打字时打架,我觉得我平了。确实,即使推断出类型, Typescript 中的隐式输入也不遵循任何继承模型。好吧,我仍然想知道为什么或它会改变,但我必须应对“它就像那样”。所以在这种情况下,类型必须是明确的。
我找到了一种更简单的方法来编写他的例子(try it in Typescript playground):
abstract class FooAbstract {
abstract bar(): {}
}
class Foo extends FooAbstract {
bar() {
return { bar: 'bar' };
}
}
class FooMaker<FOO extends FooAbstract, BAR> {
constructor(public foo: FOO & { bar: () => BAR } ) {
}
bar():BAR {
return this.foo.bar() as BAR;
}
}
let foomaker = new FooMaker(new Foo());
let result = foomaker.bar();
result
获取类型{bar:string}
,无需在任何地方放置泛型。通过引用具有泛型的接口,FooMaker.constructor
参数类型中的内容可以变得更清晰。
答案 0 :(得分:1)
这就是类型分辨率如何适用于条形函数:
bar() {
return this.foo.bar();
}
什么是this.foo
? FOO
或更确切地说,是一个扩展FooAbstract
的类,因为与属性foo
不同,bar
不会公开FOO
。必须在定义实际类型FOO之前确定打字。
如果你真的想输入它,你必须做这样的事情:
abstract class FooAbstract<T extends {}> {
abstract bar(): T
}
class Foo extends FooAbstract<{ bar: string }> {
bar() {
return { bar: 'bar' };
}
}
class FooMaker<FOO extends FooAbstract<BAR>, BAR> {
constructor(public foo: FOO) {}
bar():BAR {
return this.foo.bar();
}
baz = (): BAR => {
return this.foo.bar();
}
}
let foo = new Foo();
let result = foo.bar();
let foomaker = new FooMaker<Foo, { bar: string}>(new Foo);
let foo2 = foomaker.foo; // Type "Foo", OK
let result1 = foomaker.foo.bar(); // Type "{bar: string}", OK
let result2 = foomaker.bar(); // Type "{bar: string}", OK
let result3 = foomaker.baz(); // Type "{bar: string}", OK
不幸的是,你必须明确定义FooMaker的类型,但你确实阻止了这样的事情:
let foomaker = new FooMaker<Foo, { bar: number}>(new Foo);
答案 1 :(得分:0)
以下是传递方法返回类型所需内容的简洁答案和示例。
嵌入另一个对象的对象使用其内部声明的类型(在本例中为抽象类型)来确定其函数返回类型。即使已知(或明确声明)该对象类型。
换句话说,Typescript类型推断不会在对象方法中查看推断类型。
我发现处理这种情况的唯一解决方案是将泛型与方法/函数返回类型相关联,并将对象结构与它们相匹配。
根据我的问题更新2 (test it in Typescript playground):
interface TestInterface<ASNUM, ASSTRING, ASOBJECT> {
asNum: () => ASNUM
asString: () => ASSTRING
asObject: () => ASOBJECT
}
interface BaseInterface extends TestInterface<any, any, any> { }
class Obj implements BaseInterface {
constructor(private n: number) {
}
asNum() {
return this.n;
}
asString() {
return this.n.toString();
}
asObject() {
return {value: this.n};
}
}
class Wrapper<T extends BaseInterface, ASNUM, ASSTRING, ASOBJECT> {
constructor(private obj: T & TestInterface<ASNUM, ASSTRING, ASOBJECT>) {
}
asNum() {
return this.obj.asNum() as ASNUM;
}
asString() {
return this.obj.asString() as ASSTRING;
}
asObject() {
return this.obj.asObject() as ASOBJECT;
}
}
let w = new Wrapper(new Obj(5));
let myNum = w.asNum(); // type: number
let myString = w.asString(); // type: string
let myObject = w.asObject(); // type: {value: number}
类型还可以!
我没有找到很多关于这个或那些可能有助于 Typescript 2.3 的文档/即将推出的功能的内容。关于可能有助于形成更好解决方案的事情:
this
引用,在使用--noImplicitThis
编译选项和ThisType<T>
函数明确声明时,会提到强类型。但显然它更多的是一个函数意识到它的嵌入结构类型,而不是跟随对象模型流。在我的情况下它没有用。