我有2个接口(interface)声明:

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

还有两个实现每个的类:
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的返回类型?

Online demo

最佳答案

问题是Typescript使用结构化类型来确定类型兼容性,因此,空的IStore接口(interface)与任何其他类型(包括SomethingElse)兼容

模拟名义类型(C#/Java等中的类型)的唯一方法是添加一个使接口(interface)与其他接口(interface)不兼容的字段。实际上,您不必使用该字段,只需声明它以确保不兼容:

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可能会有更多的方法,因此这种偶然的兼容性应该很少见。

只是附带说明,私有(private)字段可确保不相关的类100%不兼容,因此,如果有可能使IStore成为带有私有(private)字段的抽象类。这可以确保没有其他类意外兼容:
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

10-06 04:05