使用Typescript强制泛型约束接口的方法?

7

我有两个接口声明:

interface IStore    { }
interface SomethingElse     { a: number;}

以下是实现了 each 方法的 2 个类:

class AppStoreImplemetion implements IStore 
 { }

class SomethingImplementation  implements SomethingElse
 {
    a: 4;
 }

我希望能够将我的方法的返回类型限制为“必须是 IStore”,因此我这样做了:
class Foo {

    selectSync<T extends IStore>( ):  T
        {
        return <T>{/* omitted*/ };    // I set the return type(`T`) when invoking
        }
}

好的

测试:

这个按预期工作:

new Foo().selectSync<AppStoreImplemetion>();

但这个也可以工作 - 不是像预期的那样:
new Foo().selectSync<SomethingImplementation>();

问题:

如何强制我的方法接受一个必须实现 IStore 接口的返回类型?

在线演示


1
你不能这样做。TypeScript采用鸭子类型。不过,我认为TypeScript会从非鸭子类型中受益,所以如果你请求这个功能,我会支持你的 :) - Jonas Wilms
1个回答

4
问题在于Typescript使用结构类型来确定类型的兼容性,因此空的接口IStore与包括SomethingElse在内的任何其他类型都是兼容的。
模拟名义类型(C#/Java等中使用的类型)的唯一方法是添加一个使接口与其他接口不兼容的字段。您实际上不必使用该字段,只需声明它以确保不兼容性:
interface IStore { 
    __isStore: true // Field to ensure incompatibility
}
interface SomethingElse { a: number; }

class AppStoreImplemetion implements IStore { 
    __isStore!: true // not used, not assigned just there to implement IStore
}

class SomethingImplementation implements SomethingElse {
    a = 4;
}

class Foo {

    selectSync<T extends IStore>(): T {
        return <T>{/* omitted*/ };   
    }
}

new Foo().selectSync<AppStoreImplemetion>();
new Foo().selectSync<SomethingImplementation>(); // This will be an error

请注意,任何具有__isStore的类都是兼容的,无论它是否明确实现了IStore,这是由于Typescript使用结构来确定兼容性,所以以下代码也是有效的:
class SomethingImplementation implements SomethingElse {
    a = 4;
    __isStore!: true 
}
new Foo().selectSync<SomethingImplementation>(); // now ok

实际上,IStore可能会有更多的方法,因此这种偶然的兼容性应该很少见。

顺便说一下,私有字段确保了不相关类之间的100%不兼容性,因此如果可以将IStore作为带有私有字段的抽象类。这可以确保没有其他类意外地兼容:

abstract class IStore { 
    private __isStore!: true // Field to ensure incompatibility
}
interface SomethingElse { a: number; }

class AppStoreImplemetion extends IStore { 

}
class Foo {

    selectSync<T extends IStore>(): T {
        return <T>{/* omitted*/ };   
    }
}

new Foo().selectSync<AppStoreImplemetion>(); // ok 

class SomethingImplementation implements SomethingElse {
    private __isStore!: true;
    a = 10;
}
new Foo().selectSync<SomethingImplementation>(); // an error even though we have the same private since it does not extend IStore

1
抱歉,!: 是什么意思? - Royi Namir
@RoyiNamir 我在编译时使用了 strict,所以我得到了一个错误,说这个字段没有初始化。! 告诉编译器不要担心未初始化的字段。 - Titian Cernicova-Dragomir
哦,原来如此。谢谢。 - Royi Namir
@RoyiNamir 还添加了与抽象类100%不兼容和一个私有字段的选项。希望能对你有所帮助,祝你好运。 - Titian Cernicova-Dragomir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接