打字稿类型、泛型和抽象类 [英] Typescript typings, generics and abstract classes

查看:30
本文介绍了打字稿类型、泛型和抽象类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试了一种我觉得很奇怪的行为.

I experiment a behavior that seems strange to me.

让我们考虑以下示例(在 Typescript playground 测试):

Let's consider the following sample (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

result2result3 的类型类似于抽象的 bar 函数 ({}).似乎this 没有被解析为具体类Foo,而是作为抽象类FooAbstract.而 foo2 的类型表明类 foo 属性被正确解析.

result2 and result3 are typed like the abstract bar function ({}). It seems that this isn't resolved as the concrete class Foo but as the abstract class FooAbstract. Whereas the type of foo2 shows that the class foo property is resolved correctly.

这是怎么回事?我做错了什么吗?

What is going on? Do I do something the wrong way?

事后想来,这个案例可以这样重新表述(在Typescript playground中测试):

As an afterthought, this case can be reformulated like that (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): {}

result has the type {bar:string}
result2 has the type {} (from the interface).
wrapper.bar has the type Wrapper.bar<Foo>(foo: Foo): {}

通过这个示例,更清楚的是,即使知道 FOO 的类型为 FooTypescript 使用 FOO 定义而不是它的显式类型作为 bar 返回类型.

With this sample, it's clearer that, even when knowing that FOO is typed as Foo, Typescript uses FOO definition and not its explicit type as bar return type.

好的,在与打字斗争的同时,我想我已经升级了.这个概念确实是 Typescript 中的隐式类型不遵循任何继承模型,即使在推导类型时也是如此.好吧,我仍然想知道为什么它会改变,但我必须应付就是这样".所以在这种情况下,类型必须是显式的.

Ok, while fighting with typings, I think I leveled up. The concept is indeed that implicit typings in Typescript don't follow any inheritance model even when a type is deduced. Well, I still wonder why or is it going to change, but I'll have to cope with "it's like that". So in this case the type has to be explicit.

我找到了一种更简单的方法来编写他的示例(在 Typescript 游乐场尝试):

I found a simpler way to write his example (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 参数类型中的内容可以通过引用具有泛型的接口变得更清晰.

result gets the type {bar:string} and no need to put generics everywhere. The stuff in the FooMaker.constructor parameter type could get cleaner by referring an interface with a generic.

推荐答案

这里有一个清晰的答案以及传递方法返回类型所需的示例.

Here is a clean answer and example of what is needed to pass method return types.

嵌入另一个对象的对象使用其内部声明的类型(在本例中为抽象类型)来确定其函数的返回类型.即使该对象类型已知(或明确声明).

An object that embeds another object uses its internally declared type (in this case the abstract type) to determine its functions return type. Even when that object type is known (or explicitly declared).

换句话说,Typescript 类型推断不会查看对象方法内部来推断类型.

In other words, Typescript type inference doesn't look inside the object methods to deduce a type.

我发现处理这种情况的唯一解决方案是将泛型与方法/函数的返回类型相关联,并将对象结构与它们相匹配.

The only solution I found to handle that case is to associate generics to the methods/functions return types, and to match the object structure with them.

基于我的问题更新 2(在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 的文档/即将推出的功能中没有找到很多关于此或可以帮助的内容.关于可能有助于形成更好解决方案的事情:

I didn't find a lot of things about that or that could help in the docs/upcoming features of Typescript 2.3. Concerning the things that could possibly help to shape a better solution:

  • 这里有一篇关于可变类型的帖子,也许这可以帮助改进这样的示例(虽然不确定):https://github.com/Microsoft/TypeScript/issues/5453
  • 关于 this 引用,这里提到了在使用 --noImplicitThis 编译选项和 ThisType 时强输入 this代码> 函数来明确声明这一点.但显然,与遵循对象模型流程相比,它更多地是关于了解其嵌入结构类型的函数.这对我来说没有帮助.
  • There is a post about variadic types here, maybe this could help to improve such a sample (not sure though): https://github.com/Microsoft/TypeScript/issues/5453
  • Concerning the this references, there's a mention about strongly typing this here when using the --noImplicitThis compilation option, and the ThisType<T> function to declare this explicitely. But apparently it's more about a function being aware of its embedding structure type, than following the object model flow. And it doesn't help in my case.

这篇关于打字稿类型、泛型和抽象类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆