ウェブサイト検索

抽象化 - 関数を持つ TypeScript インターフェース。インターフェースを実装する際に、パラメータとしてのサブタイプは受け入れられない


私は以下のように別のクラスを拡張するクラスを持っています

abstract class FooAbstract{
    constructor(someProp:any){
        this.someProp = someProp;
    }
    someProp:any;
}

class Foo extends FooAbstract{
    constructor(prop:any){
        super(prop);
    }
    someRandomFunction(){
        console.log("Something")
    }
}

私は以下のような関数を持つインターフェースを持っています

interface ExampleInterface{
    someFunction: (foo:FooAbstract)=>any;
}

次にインターフェースを実装したいのですが、以下のようにインターフェースの実装内で関数someFunctionのパラメータとしてサブタイプを渡したいです

class Example implements ExampleInterface{
    someFunction = (foo:Foo)=>{
        console.log("Hello World");
    }
}

TypeScriptは、someFunction の実装が正しくないこと、そして型 Foo と FooAbstract が互換性がないことを警告しています。なぜ FooAbstract のサブタイプをパラメーターとして要求することで someFunction を実装できないのか、理解したいです。

回答 #1

実際、これは意味があります。なぜなら、これを行うのは安全ではないからです。次のシナリオを考えてみてください:

class Example implements ExampleInterface{
    someFunction = (foo:Foo)=>{
        console.log("Hello World");
        foo.someRandomFunction() // we can call this since foo is of type Foo
    }
}
class Boo extends FooAbstract{
    constructor(prop:any){
        super(prop);
    }
    // no someRandomFunction method
}
var ex: ExampleInterface = new Example();
ex.someFunction(new Boo({})) // ok, Boo is derived from FooAbstract

もしコンパイラがあなたの質問のシナリオを許可するなら、上記のコードはコンパイルされますが、someRandomFunction が Boo に存在しないため、実行時に失敗します。

インターフェースをジェネリックにすることで、使用する派生FooAbsrtactの種類を指定できるようにできます:

interface ExampleInterface< T extends FooAbstract >{
    someFunction: (foo:T)=>any;
}
// now ok
class Example implements ExampleInterface<Foo>{
    someFunction = (foo:Foo)=>{
        console.log("Hello World");
        foo.someRandomFunction() 
    }
}
class Boo extends FooAbstract{
    constructor(prop:any){
        super(prop);
    }
    // no someRandomFunction method
}
var ex: ExampleInterface<Foo> = new Example();
ex.someFunction(new Boo({})) // compile error as it should be