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

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

问题描述

我试验了一种对我来说似乎很陌生的行为。



让我们考虑以下示例(

 抽象类FooAbstract {
抽象bar(){}
}
$ b $ class Foo extends FooAbstract {
bar(){
return {bar:'bar'};
}
}

class FooMaker< FOO扩展FooAbstract> {
构造函数(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; //输入Foo,OK
let result1 = foomaker.foo.bar(); //输入{bar:string},OK
let result2 = foomaker.bar(); //键入{},?
let result3 = foomaker.baz(); //我见过有关使用lambda表达式的评论...不是更好

result2 result3 类似于抽象 bar 函数( {} )。看来这个没有被解析为具体的类 Foo ,而是作为抽象类 FooAbstract 。然而, foo2 类型显示类 foo 属性已正确解析。



发生了什么事?我做错了什么?






更新



作为事后的想法,这种情况可以像这样重新表达(在Typescript playground中测试):

  class Foo {
bar(){
return {bar:'bar };


getThis():这个{
返回这个
}
}

类包装器{
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 定义,而不是其显式类型

更新2



好吧,在与打字打架的时候,我想我平了。这个概念确实是即使在推导出类型时, Typescript 中的隐式类型也不遵循任何继承模型。那么,我仍然怀疑为什么或会改变,但我必须应对这就是这样。所以在这种情况下,类型必须是显式的。



我找到了一个更简单的方式来编写他的示例(尝试它打字稿游乐场):

 抽象类FooAbstract {
抽象条():{}
}

class Foo扩展FooAbstract {
bar(){
return {bar:'bar'};
}
}

class FooMaker< FOO扩展FooAbstract,BAR> {
构造函数(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 参数类型中的内容可以变得更简洁。

解决方案

下面是一个干净的答案,以及传递方法返回类型所需的示例。



问题



嵌入另一个对象的对象使用其内部声明的类型(在本例中为抽象类型)来确定其函数的返回类型。即使当对象类型已知(或显式声明)时也是如此。



换句话说,Typescript类型推断不会在对象方法内部寻找推断类型。 p>

解决方案



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



基于我的问题 update 2 在Typescript游乐场中测试):

 接口TestInterface< ASNUM,ASSTRING,ASOBJECT> {
asNum:()=> ASNUM
asString:()=> ASSTRING
asObject:()=> ASOBJECT
}

接口BaseInterface扩展了TestInterface< any,any,any> {}

class Obj implements BaseInterface {
构造函数(private n:number){
}

asNum(){
return this .N;
}

asString(){
return this.n.toString();
}

asObject(){
return {value:this.n};
}
}

class Wrapper< T扩展BaseInterface,ASNUM,ASSTRING,ASOBJECT> {
constructor(private obj:T& TestInterface< ASNUM,ASSTRING,ASOBJECT>){
}

asNum(){
return this.obj.asNum ()as ASNUM;
}

asString(){
返回this.obj.asString()as ASSTRING;
}

asObject(){
返回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}

类型正常!



替代品



我没有发现很多关于这方面的信息,或者可能对 2.3 。关于那些可能有助于形成更好解决方案的事情: 这里有一篇关于 variadic 的文章 ,也许这可能有助于改善这样一个示例(不知道): https:// github。 com / Microsoft / TypeScript / issues / 5453

  • 关于这个当使用 - noImplicitThis 编译选项时,以及 ThisType< T> 函数来明确地声明它。但显然它更多的是关于一个函数意识到它的嵌入结构类型,而不是遵循对象模型流程。这对我的情况没有帮助。


  • I experiment a behavior that seems strange to me.

    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
    

    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?


    update

    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 has the type {bar:string}
    result2 has the type {} (from the interface).
    wrapper.bar has the type Wrapper.bar<Foo>(foo: Foo): {}

    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.


    update 2

    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.

    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 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.

    the issue

    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).

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

    the solution

    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.

    Based on my question update 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}
    

    The types are OK!

    alternatives

    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:

    • 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.

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

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