【问题标题】:why does my type not satisfy the constraint为什么我的类型不满足约束
【发布时间】:2021-10-19 18:01:17
【问题描述】:

我想知道为什么我收到了

Type 'typeof TestBody' does not satisfy the constraint 'typeof AbstractBodyWithTabs'.
  Construct signature return types 'TestBody' and 'AbstractBodyWithTabs<T, U>' are incompatible.
    The types of 'contents' are incompatible between these types.
      Type 'TestContent' is not assignable to type 'T'.
        'TestContent' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'AbstractSelector'.

class TestPage extends PageWithTabs<typeof TestBody> {

“T”约束是正确类型的扩展类。

export class AbstractSelector {
    protected readonly myRoot : Selector
    
    constructor(inSelector : Selector) {
        this.myRoot = inSelector;
    }
}
export abstract class AbstractBodyWithTabs<
    T extends AbstractSelector, 
    U extends Tabs
> {
    abstract get contents () : T
    abstract get tabs () : U
    get wrapper () : Selector {
        return Selector(".app-component");
    }
}
export abstract class Tabs extends AbstractSelector {
    get active () {
        return this.wrapper.find(":scope > button[contains(@class, 'focus')]");
    }
    get all () {
        return this.wrapper.find(":scope > button");
    }
    abstract get named_tab () : Record<string, Selector>;
    get wrapper () {
        return this.myRoot.find("div.btn-group");
    }
}
export abstract class PageWithTabs<V extends typeof AbstractBodyWithTabs> {
    abstract get body () : InstanceType<V>
    get footer () {
        return new SaveAndCancelFooter();
    }
    get header () {
        return new Header();
    }
}

class TestContent extends AbstractSelector {

}
class TestTabs extends Tabs {
    get named_tab () {
        return { get country () { return Selector("div"); } };
    }
}
class TestBody extends AbstractBodyWithTabs<TestContent, TestTabs> {
    get contents () {
        return new TestContent(this.wrapper);
    }
    get tabs () {
        return new TestTabs(this.wrapper);
    }
}
class TestPage extends PageWithTabs<typeof TestBody> {
    get body () {
        return new TestBody();
    }
}

当我进行这些更改时:


export abstract class PageWithTabs<V extends typeof AbstractBodyWithTabs> {
    abstract get body () : InstanceType<V>
    get footer () {
        return new SaveAndCancelFooter();
    }
    get header () {
        return new Header();
    }
}

export abstract class PageWithTabs<T extends AbstractSelector, U extends Tabs, V extends AbstractBodyWithTabs<T, U>> {
    abstract get body () : V
    get footer () {
        return new SaveAndCancelFooter();
    }
    get header () {
        return new Header();
    }
}

class TestPage extends PageWithTabs<typeof TestBody> {
    get body () {
        return new TestBody();
    }
}

class TestPage extends PageWithTabs<TestContent, TestTabs, TestBody> {
    get body () {
        return new TestBody();
    }
}

它有效,但我不喜欢我必须传递已声明的多个类型。

打字稿playground

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    错误

    typeof AbstractBodyWithTabs 大致相当于*

    type TypeofAbstractBodyWithTabs =
       new <T extends AbstractSelector, U extends Tabs>() => AbstractBodyWithTabs<T, U>;
    

    这意味着如果我有typeof AbstractBodyWithTabs,我可以选择我想要的任何TU(只要它们扩展AbstractSelectorTabs): p>

    class NotTestContent extends AbstractSelector {
        foo = "foo";
    }
    
    class NotTestTabs extends Tabs {
        bar = "bar";
    
        get named_tab() {
            return {};
        }
    }
    
    function f(AbstractBodyWithTabsCtor: typeof AbstractBodyWithTabs): void {
        const bodyWithTabs = new AbstractBodyWithTabsCtor<NotTestContent, NotTestTabs>();
        bodyWithTabs.contents.foo;
        bodyWithTabs.tabs.bar;
    }
    

    注意TNotTestContentUNotTestTabs

    如果我们尝试使用typeof TestBody 执行此操作会怎样?

    function g(TestBodyCtor: typeof TestBody): void {
        const bodyWithTabs = new TestBodyCtor();
        bodyWithTabs.contents.foo;
    //                        ~~~
    // Property 'foo' does not exist on type 'TestContent'.
        bodyWithTabs.tabs.bar;
    //                    ~~~
    // Property 'bar' does not exist on type 'TestTabs'.
    }
    

    bodyWithTabs.contentsTestContent,不像在f 中,我们可以使用AbstractBodyWithTabsCtor 的类型参数使其成为NotTestContent

    这从错误消息中解释了'TestContent' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'AbstractSelector'.T 可能是AbstractSelector 的另一个子类型,例如NotTestContent,不一定是TestContent

    解决方案

    abstract class PageWithTabs<V extends AbstractBodyWithTabs<AbstractSelector, Tabs>> {
        abstract get body (): V
        // ...
    }
    

    如果AbstractBodyWithTabs 的类型参数没有任何约束,您可以使用unknown 而不是AbstractSelectorTabs。这里使用AbstractSelectorTabs的唯一原因是由于TU的约束;如果你尝试使用unknown,你会得到Type 'unknown' does not satisfy the constraint 'AbstractSelector'.

    现在这可以正常工作了,因为 TestBody 可以分配给 AbstractBodyWithTabs&lt;AbstractSelector, Tabs&gt;

    class TestPage extends PageWithTabs<TestBody> { /* ... */ }
    

    请注意,V extends AbstractBodyWithTabs&lt;AbstractSelector, Tabs&gt; 并不意味着AbstractBodyWithTabs 中的T 正好是AbstractSelectorU 正好是Tabs。这意味着TU 类型只需要分别分配给AbstractSelectorTabs

    TestContent 扩展AbstractSelectorTestTabs 扩展Tabs,因此扩展AbstractBodyWithTabs&lt;TestContent, TestTabs&gt;TestBody 可分配给AbstractBodyWithTabs&lt;AbstractSelector, Tabs&gt;

    Playground link


    *typeof AbstractBodyWithTabs 更像abstract new &lt;T extends AbstractSelector, U extends Tabs&gt;() =&gt; AbstractBodyWithTabs&lt;T, U&gt;(注意abstract)。这意味着f 实际上并没有编译,因为你不能构造一个抽象类。但是,我认为我上面描述的原理仍然适用于抽象构造函数。

    不管怎样,这里有一个 typeof AbstractBodyWithTabstypeof TestBody 不工作的地方工作的示例:

    function f(AbstractBodyWithTabsCtor: typeof AbstractBodyWithTabs): void {
        class MyBodyWithTabs extends AbstractBodyWithTabsCtor<NotTestContent, NotTestTabs> {
            get contents(): NotTestContent {
                return new NotTestContent(new Selector(""));
            }
    
            get tabs(): NotTestTabs {
                return new NotTestTabs(new Selector(""));
            }
        }
    }
    
    function g(TestBodyCtor: typeof TestBody): void {
        class MyBodyWithTabs extends TestBodyCtor {
            get contents(): NotTestContent {
                return new NotTestContent(new Selector(""));
            }
    
            get tabs(): NotTestTabs {
    //          ~~~~
    // Property 'tabs' in type 'MyBodyWithTabs' is not assignable to the same property in base type 'TestBody'.
    //  Type 'NotTestTabs' is not assignable to type 'TestTabs'.
    //    Types of property 'named_tab' are incompatible.
    //      Property 'country' is missing in type '{}' but required in type '{ readonly country: Selector; }'.
                return new NotTestTabs(new Selector(""));
            }
        }
    }
    

    【讨论】:

    • 感谢您的解释。我觉得我同时理解和不理解,哈哈。我认为奇怪的是PageWithTabs 需要本质上重新声明AbstractBodyWithTabs 类型为AbstractSelectorTabs,它们已经在原始类中定义。但是T 可能是另一个子类型的解释也是有道理的。我会再考虑一下。
    【解决方案2】:

    重温cherryblossom的回答后,似乎在这种情况下不需要泛型

    重写:

    class Selector {
      constructor (inValue : string) {
        console.log(inValue);
      }
    }
    
    export class AbstractSelector {
        protected readonly myRoot : Selector
        
        constructor(inSelector : Selector) {
            this.myRoot = inSelector;
        }
    }
    export abstract class AbstractBodyWithTabs {
        abstract get contents () : AbstractSelector
        abstract get tabs () : Tabs
        get wrapper () : Selector {
            return new Selector(".app-component");
        }
    }
    export abstract class Tabs extends AbstractSelector {
        get active () {
            return;
        }
        get all () {
            return;
        }
        abstract get named_tab () : Record<string, Selector>;
        get wrapper () {
            return;
        }
    }
    export abstract class PageWithTabs {
        abstract get body () : AbstractBodyWithTabs
        get footer () {
            return;
        }
        get header () {
            return;
        }
    }
    
    class TestContent extends AbstractSelector {
        get has_content () {
            return "has ocntent";
        }
    }
    class TestTabs extends Tabs {
        get named_tab () {
            return { get country () { return new Selector("div"); } };
        }
    }
    class TestBody extends AbstractBodyWithTabs {
        get contents () {
            return new TestContent(this.wrapper);
        } 
        get tabs () {
            return new TestTabs(this.wrapper);
        }
    }
    class TestPage extends PageWithTabs {
        get body () {
            return new TestBody();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-09-12
      • 2019-09-19
      • 2021-03-16
      • 2021-01-28
      • 1970-01-01
      • 1970-01-01
      • 2020-10-25
      • 2021-05-13
      • 2021-04-15
      相关资源
      最近更新 更多