JavaScriptからType Script-インターフェースまで


前に汎型を話した時、インターフェースに言及しました.一般的なタイプと同じで、インターフェースも現在のJavaScriptには存在しない文法です.
汎型文法は常に類または関数文法に付加されるので、Type ScriptからJavaScriptに変換した後、少なくとも類と関数が存在します.しかしながら、ある.tsファイルにインターフェースのみが定義されている場合、変換された.jsファイルは空のファイルである.インターフェースは完全に消去される.
では、Type Scriptにはなぜインターフェース文法が登場しますか?強いタイプの文法に触れたことがないJSerにとって、インターフェースとは一体どんなものですか?
インターフェースとは何ですか
実際の生活では、海外旅行の前に、目的地の電源コンセントの状況を把握する必要があります.
  • はどんな形ですか?三本差しですか?それとも二本差しですか?平差しですか?それとも丸挿しですか?
  • 形が同じなら、電圧は110 Vですか?それとも220 Vですか?それとも380 Vですか?
  • 直流ですか?それとも交流ですか?
  • ご存知のように、国内の電源プラグにはよくあるものが二つあります.三平プラグ(例えば、多くのノートパソコンの電源プラグ)と平滑プラグ(例えば、多くの携帯電話の電源プラグ)、家庭用電圧は全部220 Vです.しかし、近年の電子製品は国際的に接続されています.電源アダプターと充電器は普通100~220 Vの電圧をサポートしています.
    上に二つの基準があります.一つはコンセントの基準です.もう一つはプラグの基準です.もしこの二つの標準が同じなら、私達はバッグを持って出発できます.場所に着いたら携帯は充電できなくなります.パソコンは適当な電源の問題が見つけられません.しかし、もし標準が違ったら、変換プラグを買いに行かなければなりません.
    ここで言及した変換プラグは、ソフトウェア開発において「アダプターモード」に属しており、ここでは深く研究していない.私たちが研究したいのはコンセントとプラグの基準です.コンセントは壁の上にあるインターフェースです.自分の基準があります.プラグはこのコンセントを使えるように、その基準に合わせなければなりません.産業上のコンセントのような標準は成文、審査、公布、実行しなければならないが、プログラミング上のインターフェースも同様であり、インターフェース、タイプチェック(コンパイラ)を定義し、ドキュメントを公開し、インターフェースを実現する必要がある.
    したがって、Type Scriptに戻ります.interface宣言と似たような文法でインターフェースを定義します.だから一つのインターフェースはこのように見えるかもしれません.
    interface INamedLogable {
        name: string;
        log(...args: any[]);
    }
    実例を通してインターフェースを説明する.
    私たちの業務にはこのような一部JavaScriptコードがあると仮定します.
    function doWith(logger) {
        console.log(`[Logger] ${logger.name}`);
        logger.log("begin to do");
        // ...
        logger.log("all done");
    }
    
    doWith({
        name: "jsLogger",
        log(...args) {
            console.log(...args);
        }
    })
    Type Scriptに翻訳します.
    私たちはまだインターフェースを知らないので、class属性とname方法を含むクラスを先に定義します.このクラスがあれば、タイプ制約(チェック)を行うためにlog()および他の定義においてそれを使用することができる.
    class JsLogger {
        name: string;
    
        constructor(name: string) {
            this.name = name;
        }
    
        log(...args: any[]) {
            console.log(...args);
        }
    }
    次に、doWith()を定義する.
    function doWith(logger: JsLogger) {
        console.log(`[Logger] ${logger.name}`);
        logger.log("begin to do");
        // ...
        logger.log("all done");
    }
    呼び出しの例:
    const logger = new JsLogger("jsLogger");
    doWith(logger);
    log()の方法に材料を追加します.
    上記の例では、出力したログはログの内容自体だけですが、ログ情報の各行の前にログ名を付けたいです.例えば、このような出力があります.
    [jsLogger] begin to do
    したがって、doWithからJsLoggerを継承して使用します.
    class PoweredJsLogger extends JsLogger {
        log(...args: any[]) {
            console.log(`[${this.name}]`, ...args);
        }
    }
    
    const logger = new PoweredJsLogger("jsLogger");
    doWith(logger);
    第三者のロギングに換える
    さらに、私達は第三者のロギングに換えることができます.PoweredJsLoggerとは無関係ですが、メンバー定義は同じです.
    function doWith(logger: JsLogger) {
        console.log(`[Logger] ${logger.name}`);
        logger.log("begin to do");
        // ...
        logger.log("all done");
    }
    
    const logger = new AnotherLogger("oops");
    doWith(logger);
    それは間違いを報告すると思いますか?ありません.正常に翻訳され、正常に動作し、出力されます.
    [Logger] oops
    [Another(oops)] begin to do
    [Another(oops)] all done
    この結果を見て、JavaとC〓プログラマーが狂いそうです.でも、JSerさんはこれは大丈夫だと思います.私たちはいつもこのようにします.
    クラスからインターフェースを宣言します.
    理論的には、インターフェースは抽象的な概念であり、クラスはより具体的な抽象的な概念である.一般的には、私たちの設計過程は具体的から抽象的までですが、開発(プログラミング)過程は正反対で、抽象から具体的までです.だから、一般的に開発過程では、まずインターフェースを定義し、このインターフェースを実現するクラスを定義します.
    もちろん例外があります.多くの開発者は逆の体験があると信じています.特に設計しながら開発する時:まず業務の必要に応じて種類を定義してから、このような抽象的なインターフェースからインターフェースを定義して、前の種類がこのインターフェースを実現すると宣言します.インタフェースの要素(例えば、方法)が変化したら、まずクラスの中で実現して、抽象的にインターフェースの定義に補充します.このような状況では、直接にクラスからインターフェースを生成したいです.もちろん、このプロセスを実現できるツールがありますが、多くの言語自体はサポートしていません.
    でも、Type Scriptは違った体験を持ってきました.このような種類の声明インターフェースからもいいです.
    interface ILogger extends JsLogger {
        //            
    }
    ここで定義されたJsLoggerと、最も前に定義されたILoggerとは同じインターフェース要素を有しており、同じ効果である.
    なぜType Scriptはこの逆の定義を支持していますか?しかし、大規模なアプリケーション開発にとっては、いいことではない.今後、何らかの理由で公共方法を追加する必要があるならば、悲劇的なことになる.INamedLogableインターフェースを実現したすべての種類は、この新たな方法を実現しなければならない.今後、あるバージョンのType Scriptはこの問題を処理するかもしれません.少なくとも今はJavaで方法を見つけました.これはJava 8が持ってきたデフォルトの方法です.また、C啣嗳もすぐにこの特性を実現します.
    上の問題に戻ります.
    今は上の問題に戻りますが、なぜJsLoggerILoggerオブジェクトが入ってきたのかに違和感がなく、警告さえもありません.
    前に述べましたが、「アヒル弁別法」はdoWith()にとって必要なのは本当にAnotherLoggerではなく、doWith(logger: JsLogger)です.入ってきたこのパラメータがこのインターフェース制約に符合する限り、方法の中のいかなる語句も文法エラーを発生しません.文法的には絶対大丈夫です.したがって、着信JsLoggerには問題がなく、暗黙のインターフェース定義は完全にinterface extends JsLogger {}インターフェースの定義に一致している.
    しかし、意味的には問題があるかもしれません.これも十年以上の経験を持つ静的言語の使用者としては十分に理解できません.これはType ScriptがダイナミックなJavaScriptに適応するために作った譲歩かもしれません.Type Scriptがわざわざ導入した特性かもしれません.多くの動的言語と関数的言語についてはよく分かりませんが、Type Scriptが初めて作ったのではないと信じています.
    Type Scriptインターフェース詳細
    上記の多くの内容は、AnotherLoggerの定義を通じてILoggerの理解に導入するためだけである.しかし、インターフェースはどのように定義されますか?
    一般インターフェース
    従来のインターフェースの定義とクラスの定義はほとんど区別されていません.上にはすでに例があります.要約すると、いくつかの点に注意が必要です.
  • classキーワードを使用する.
  • インターフェース名は、一般的に、規範プレフィックスinterfaceに従っている.
  • インターフェースには実装が含まれていません.
  • メンバ変数に初期値を割り当てない
  • 構築関数がない
  • メソッドがない
  • インタフェースの実装は、例えばinterfaceキーを介してもよい.
    class MyLogger implements INamedLogable {
        name: string;
        log(...args: any[]) {
            console.log(...args);
        }
    }
    これは明示的に実現するものであり,また陰的なものである.
    const myLogger: INamedLogable = {
        name: "my-loader",
        log(...args: any[]) {
            console.log(...args);
        }
    };
    また、すべての宣言インターフェースタイプのところで値や割り当てが行われ、Type Scriptは、比来に入ってきたオブジェクトをインタフェース要素ごとにチェックします.
    関数タイプインターフェース
    以前、関数タイプを定義しましたが、Iキーワードを使ってLambandのような文法で定義します.例えば、1つのパラメータを定義する必要があるのはimplemnetsであり、戻り値はtypeの関数タイプである.
    //     
    type NumberToStringFunc = (n: number) => string;
    
    //           hex
    const hex: NumberToStringFunc = n => n.toString(16);
    今はインターフェース文法で定義できます.
    // tslint:disable-next-line:interface-name
    interface NumberToStringFunc {
        (n: number): string;
    }
    
    const hex: NumberToStringFunc = n => n.toString(16);
    このような定義は、Java 8の関数式インターフェースシンタックスと類似しており、関数タイプを表しているので、一般にプレフィクスnumberではなく、接尾辞string(有参)またはI(無参)である.ただし、TSLintはこのセットを食べないことができますので、ここではコメントを通じてTSLintの接続先の名前をチェックします.
    このようなインターフェースはクラスでは実現できません.上記の例のFuncは、1つのLambanを通じて直接実現される.関数、関数式によっても達成できます.さらに,混合型のインターフェースに拡張できる.
    ハイブリッドインターフェース
    JSerたちは常に一つの技術を使って、一つの関数を定義し、またこの関数にいくつかの属性を割り当てるべきです.これは問題ないです.JavaScriptの関数自体が対象です.JavaScriptのオブジェクトは動的に修正できます.よくある例はjQueryとLodashです.
    このようなタイプはType Scriptではハイブリッドタイプインターフェースで定義されていますが、今回は公式文書の例を直接引用します.
    interface Counter {
        (start: number): string;
        interval: number;
        reset(): void;
    }
    
    function getCounter(): Counter {
        let counter = function (start: number) { };
        counter.interval = 123;
        counter.reset = function () { };
        return counter;
    }
    
    let c = getCounter();
    c(10);
    c.reset();
    c.interval = 5.0;
    インターフェース継承
    前に述べたように、クラス宣言インターフェースから、文法はActionキーワードを採用しています.だから、継承と言ってもいいです.
    また、インターフェースは他のインターフェースから継承されてもよい.
    interface INewLogger: ILogger {
        suplier: string;
    }
    インターフェースはまた、上記hexなどの複数のインターフェースからの継承を可能にする.
    interface INamed {
        name: string;
    }
    
    interface ILogable {
        log(...args: any[]);
    }
    
    interface INamedLogable extends INamed, ILogable {}
    このようにextendsを定義するのはもっと合理的ですか?
    後記
    どの言語であれ、インタフェースの主な目的は供給者と消費者の前に契約書を作ることです.その意味はプログラム自体ではなく設計する傾向があります.ですから、インタフェースは様々な設計モードにおいて非常に広く使われています.インターフェースのためにインターフェースをしないでください.デザインが必要な時に使います.複雑なアプリケーションには良いインターフェースを定義する必要がありますが、いくつかの小さなプログラムには必要がないようです.
    関連記事
  • JavaScriptからType Script-モジュール化と構築
  • JavaScriptからType Script-ステートメントタイプ
  • JavaScriptからType Script-汎型
  • まで
    作者の公衆番号「辺境城旅館」→