blog

タイプスクリプト - インターフェース (III)

インターフェイスの役割:オブジェクト指向のOOPプログラミングでは、インターフェイスは定義の仕様であり、仕様の動作と作用を定義します。 インターフェイスは、クラスの特定のグループは、仕様に準拠する必要...

Jan 11, 2021 · 13 min. read
シェア

インターフェイスの役割:オブジェクト指向のOOPプログラミングでは、インターフェイスは規範的な定義であり、プログラミングの内部では、インターフェイスは制限と仕様の役割を果たします。

インターフェイスは、特定のクラスのグループが仕様に準拠する必要があることを定義します。インターフェイスは、これらのクラスの内部状態データについては気にしませんし、これらのクラスのメソッドの実装の詳細についても気にしません。 typescripのインターフェースはjavaに似ていますが、プロパティ、関数、indexable、クラスなど、より柔軟なインターフェース型が追加されています。

OOP : オブジェクト指向プログラミング

基準の定義

  • 属性 クラス インタフェース
  • 関数 型 インタフェース
  • インデックス可能なインターフェース
  • クラス 型 インタフェース
  • インターフェイスの拡張
    1. インターフェースのクラス実装
    2. インターフェイスはインターフェイスを継承できます。
    3. クラスはクラスを継承し、クラスはインタフェースを実装できます。
    4. インターフェースの継承 クラス
    5. まとめ
  • インターフェースと型の区別

. 属性 クラス インタフェース

未使用のインターフェース

printパラメータはオブジェクトですが、label属性を持たなければなりません。 tsカスタムメソッドはパラメータを渡し、jsonを制約します。


function print(labelObj:{ label:string }){
 console.log( labelObj.label );
}
// これらのチェックをスキップする方法は、オブジェクトを別の変数に代入することだ:
// myObj 追加のプロパティ・チェックはないので、コンパイラーがエラーを報告することはない。
print({name:' '}); //間違った書き方
print({label:' '}); //正しい書き方
print({
 label:"Size test",
 name:'sss'
} as { label:string });

次のように書くとエラーになるので注意してください:

print({ label: "Size test", name:'laney' });

オブジェクト・リテラルをパラメータとして渡す場合、オブジェクト・リテラルは特別に扱われ、「特別なプロパティ・チェック」が行われます。オブジェクト・リテラルに、"ターゲット・タイプ" が含んでいない属性があると、エラーになります。

このチェックをスキップする方法

公式サイトには3つの方法が掲載されています:

  • 2つ目は、文字列インデックスのシグネチャを追加します:

  • 3番目は、リテラルを別の変数に代入します:

これはオブジェクトを別の変数 myObj に代入しているだけです。追加の属性がチェックされないので、コンパイラーはエラーを報告しません。

これは、printを呼び出す際に入力される引数の型をチェックし、必要なプロパティが存在することと型が一致することだけをチェックするシンプルな関数です。以下は、インターフェイスを使用して上記の例を書き直したものです。

インターフェースの使用

// 
interface labelValue{
 label:string;
 size:number;
}
//type  
type labelObj = {
 label?:string;
 size:number;
}
 function print(labelObj:labelValue){
 console.log( 'labelObj.label' );
 console.log( labelObj );
 }
 let myObj2={size:10,label:"Size 10 Object",name:'Alice' };
 print(myObj2);

パラメータ・タイプをチェックすると、そのパラメータがインターフェイス labelValue の仕様に従っ ているかどうかがわかります。インターフェースは、誰もが従うべき制約に相当します。

//interface: 動作とアクションの指定、バルクメソッドに対する制約 //つまり、入力オブジェクトに対する制約 プロパティ・インターフェース

型のアサーション

型アサーション 型アサーションには2つの形式があります。 ひとつは "山括弧" 構文です:

let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

もうひとつはas構文です:

let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;

この2つの形式は同等です。 どちらを使うかは個人の好みの問題です。

型アサーションは特別なデータチェックや分解を行わないため、以下のような記述では型変換はできませんが、コード保護としては機能します。

let a: any = 6; //変数aは型によって数値であると推測される
console.log(a as string); // 出力6:数値型
console.log((a as string).length); //出力は未定義

定義:値の型を手動で指定するために使用できます

  • モード I: <类型> 値
function getLength(x:number|string):number{
 if((<string>x).length) {
 return (<string>x).length
 } else {
 return x.toString().length
 }
}
  • 第一の方法< >
function getLength(x:number|string):number{
 if((x as string).length){
 return (x as string).length
 } else {
 return x.toString().length
 }
}

同等

function getLength(x:number|string):number{
 if(typeof x =='string') {
 return x.length
 } else {
 return x.toString().length
 }
}

型アサーションは型変換ではありません。ユニオン型に存在しない型をアサーションするとエラーになります!


function wrong(x:number|string):boolean{
 return <boolean>x //  
}

追加属性の検出

interface labelValue{
 label:string;
 size:number;
}
 function print(labelObj:labelValue){
 console.log( 'labelObj.label' );
 console.log( labelObj );
 }

上の「1.2 インターフェイスの使用」の例では、このように値を渡すとエラーになります print({ label:'hello', size:30, name:'laney' });

余分な属性チェックを回避する公式の方法は3つあります。

解決策

// 型アサーションの最初の使い方
print({
 label1:'hello',
 size:30
} as labelValue);
// 文字列インデックスのシグネチャを追加する
/* interface labelValue{
 label?:string;
 size:number;
 [propName: string]: any; //+
} */
//リテラル量を別の変数に代入する
 var labK = {
 label1:'hello',
 size:30
 };
print(labK);

オプション属性

interface FullName{
 firstName:string; // ; 
 secondName:string;
 // secondName?:string; //オプション属性:渡せるか渡せないか
 hello(str:string):string
}
function printName(name:FullName){
 // オブジェクトに渡さなければならない firstName secondName
 console.log(name.firstName+'--'+name.secondName);
 console.log(name)
}
// printName('1213'); // 
/*入力されるパラメーターは、firstName secondNameを含んでいなければならない。*/
var obj={ 
 firstName:' ',
 secondName:' ',
 hello(str:string):string{
 return obj.firstName+obj.secondName;
 }
};
printName(obj)
//secondNameがオプションのパラメーターの場合は、パラメーターを渡すときに省略できる。
// printName({
// firstName:'firstName'
// })

ajaxの例によるプロパティ・クラス・インターフェースのデモンストレーション

// ES5でajaxをラップする方法

/*
 $.ajax({
 type: "GET",
 url: "test.json",
 data: {username:$("#username").val(), content:$("#content").val()},
 dataType: "json" 
 }); 
*/
// config : {
// type,
// url,
// data,
// dataType
// }
function ajax(config){
 var xhr=new XMLHttpRequest();
 xhr.open(config.type,config.url,true);
 xhr.send(config.data);
 xhr.onreadystatechange=function(){
 if(xhr.readyState==4 && xhr.status==200){
 console.log('chengong');
 if(config.dataType=='json'){
 console.log(JSON.parse(xhr.responseText));
 }else{
 console.log(xhr.responseText)
 }
 }
 }
 }

interface Config{
 type:string;
 url:string;
 data?:string;
 dataType:string;
}
//TSカプセル化されたajax
function ajax(config:Config){
 var xhr=new XMLHttpRequest();
 xhr.open(config.type,config.url,true);
 xhr.send(config.data);
 xhr.onreadystatechange=function(){
 if(xhr.readyState==4 && xhr.status==200){
 console.log('chengong');
 if(config.dataType=='json'){
 console.log(JSON.parse(xhr.responseText));
 }else{
 console.log(xhr.responseText)
 }
 }
 }
}
ajax({
 type:'get',
 data:'name=tanakasan',
 url:'http://:/st', //api
 dataType:'json'
})

III.関数型インターフェイス

tsの関数型インターフェースは、javaやc#のラムダ式を使った無名関数の受け渡しによく似ています。オブジェクトには関数が1つしか含まれていないため、このオブジェクトの全体的な意味は、その関数が外部から呼び出せるということだけなので、関数型インターフェースと呼ばれます。

暗号化された関数型インターフェース

関数型のインターフェース:メソッドに渡されるパラメータと戻り値に対する制約 バッチ制約

interface encrypt{
 (key:string,value:string):string;
}
var md5:encrypt=function(key:string,value:string):string{
 //シミュレーション
 return key+value;
}
console.log(md5('name','tanakasan'));
var sha1:encrypt=function(key:string,value:string):string{
 //シミュレーション
 return key+'----'+value;
}
console.log(sha1('name','lisi'));
//2つの数値を演算して別の数値を得る 関数インターフェース
interface CalcTwo{
 (a:number,b:number):number;
}
/**
 * 配列の結果をあるアルゴリズムで計算する。
 * @param {number[]} arr  
 * @param {CalcTwo} calc ユーザー指定のアルゴリズム関数
 * @param {number} initVal 初期値を渡す
 * @returns {number} 最終結果を得る
**/
function calcArr(arr:number[],calc:CalcTwo,initVal:number):number{
 var result=initVal;
 arr.forEach((value)=>{
 result = calc(result,value);
 });
 return result;
}

ご利用ください:


 var arr:number[]=[1,3,5,7,9];
 var add=function(a:number,b:number):number{
 return a+b;
 };
 
 var multiply=function(a:number,b:number):number{
 return a*b;
 };
console.log(" ,calcArr(arr, add, 0));//25
console.log(" ,calcArr(arr, multiply, 1));//945
//あるいは無名関数を渡す。
var s1=calcArr(arr,function(a,b){
 return a*b;
},1);
console.log("s1",s1);//945
 
 var s2=calcArr(arr,function (a,b) {
 return (a+1)*(b+1);
 },1);
console.log("s2",s2);//10170

インデックス可能なインターフェース

インデックス可能なインタフェース:配列、オブジェクト制約

ts 配列の定義方法 /* var arr:number[]=[2342,235325] var arr1:Array=['111','222'] */.

インデックスの付けられるインターフェイス 配列の制約


 interface UserArr{
 [index:number]:string
 }
 var arr:UserArr=['aaa','bbb'];
 console.log(arr[0]);
 var arr:UserArr=[123,'bbb']; /* */
 console.log(arr[0]);

インデクサブル・インターフェイス オブジェクトの制約

 interface UserObj{
 [index:string]:string
 }
 var arr:UserObj={name:' '};

第五に、クラス型インターフェイス、インターフェイスの実装の使用

クラス・タイプ・インターフェースは、クラスの内容を標準化するために使用します。サンプル・コードは以下の通りです。

より使用され、抽象的なクラスは、本質的にインターフェイスのクラスの実装は少し似ている、つまり、インターフェイスの制約に従うようにクラスは、インターフェイスは、関数、パラメータ、クラスの実装の数は、同じ関数、パラメータを記述する内部に書かれています。

interface FullName { 
 username: string; 
} 
//Personこのクラスは、インターフェースFullNameの制約に従う必要がある。
class Person implements FullName { 
 username: string; 
 constructor(n: string) { 
 this.username = n;
 } 
} 

以下のsetNameメソッドのように、インターフェイスでメソッドを記述し、クラスでその機能を具体的に実装することも可能です:

interface FullName { 
 username: string; 
 setName(name: string): void; 
 getName():void;
} 
class Person implements FullName { 
 username: string; 
 constructor(name: string) {
 this.username = name;
 } 
 setName(str: string) { 
 this.username = str;
 } 
 getName(){
 console.log(this.username)
 } 
} 
var p1 = new Person('Alice');
p1.setName('tony');
p1.getName(); 

以下のものは実演していませんので、各自で学習してください。


interface Animal{
 name:string;
 eat(str:string):void;
 }
class Dog implements Animal{
 name:string;
 constructor(name:string){
 this.name=name;
 }
 eat(){
 console.log(this.name+' ')
 }
}
var d=new Dog(' ');
d.eat();
class Cat implements Animal{
 name:string;
 constructor(name:string){
 this.name=name;
 }
 eat(food:string){
 console.log(this.name+' '+food);
 }
}
var c=new Cat(' ');
c.eat(' ');
 

第六に、インターフェイス拡張

クラス実装インタフェース - クラス型インタフェースの前の部分


interface ClockInterface{
 currentTime:Date;
 getTime(d:Date):any;
}
class Clock implements ClockInterface{
 currentTime:Date;
 constructor(){
 this.currentTime = new Date()
 }
 getTime(){
 console.log("123");
 console.log(this.currentTime)
 }
 
}
let clock1=new Clock();
clock1.getTime();

インターフェイスのクラス実装は基本的に同じです。つまり、クラスはインターフェイスの制約に従います。インターフェイスの内部に記述されている関数やパラメータと同じ数の関数やパラメータを、実装クラスの内部にも記述しなければなりません。

インターフェイス継承とは、インターフェイスが他のインターフェイスを通して自分自身を拡張できることを意味します。

Typescript では、インターフェースを複数のインターフェースから継承することができます。継承は extends というキーワードを使って行います。

インターフェースはインターフェースを継承可能

interface Animal {
 eat(): void;
}
// Person interfaceはinterface Animalを継承する。
// 単一インターフェース継承: 1つのインターフェースだけを継承する。
interface Person extends Animal {
 work(): void;
}
//クラス実装インターフェース WebFront クラス実装インターフェース Person
class WebFront implements Person {
 public name: string;
 constructor(name: string) {
 this.name = name;
 }
 eat() {
 console.log(this.name + "饅頭を食べるのが好きだ");
 }
 work() {
 console.log(this.name + "コードを書く");
 }
}
var w = new WebFront("李");
w.eat();

インターフェイスの拡張は、複数のインターフェイスに制約を追加することです。インターフェイスは複数のインターフェイスを拡張することができ、インターフェイスが他のインターフェイスを拡張すると、そのインターフェイスの制約も継承します。

クラスはクラスを継承でき、クラスはインターフェースを実装できます。


interface Animal {
 eat(): void;
}
interface Person extends Animal {
 work(): void;
}
class Programmer {
 public name: string;
 constructor(name: string) {
 this.name = name;
 }
 coding(code: string) {
 console.log(this.name + code);
 }
}
//クラスはクラスを継承し、インターフェースを実装する: WebFrontはクラスProgrammerを継承し、インターフェースPersonを実装する。
class WebFront extends Programmer implements Person {
 constructor(name: string) {
 super(name);
 }
 eat() {
 console.log(this.name + "饅頭が好きだ");
 }
 work() {
 console.log(this.name + "コードを書く");
 }
}
var w = new WebFront("李");
w.eat();
w.coding("tsコードを書く");

インターフェースの継承 クラス


class Point{
 x:number;
 y:number;
 constructor(x:number,y:number){
 this.x = x;
 this.y = y;
 }
 log(){
 console.log('');
 }
 }
 interface Point3d extends Point{
 z:number;
 }
 
var point3d:Point3d={
 x:1,
 y:2,
 z:3,
 log(){
 console.log('7777');
 }
 }
point3d.log();//7777

公式説明: インターフェースがクラス型を継承する場合、クラスのメンバは継承しますが、実装は継承しません。つまり、インターフェイスはクラス値を継承し、その制約を継承しますが、具体的な値は継承しません。

概要

インターフェイスはインターフェイスを拡張します interfaceA extends interfaceB

クラスは interfaceA を実装します。

インターフェースの継承 クラス interfaceB extends classB

クラスの拡張 classA extends classB

ケンは、いつエクステンドを使うべきか、いつインプラントを使うべきかを見極めることができません。

相続に関しては、延長されることを忘れないでください。

インターフェース型と型宣言型の違い


namespace InterfaceAndType {
 // 1. Objects / Functions
 // どちらもオブジェクトや関数の型を記述するのに使えるが、構文が異なる。
 // Interface
 // interface Point {
 // x: number;
 // y: number;
 // }
 
 // interface SetPoint {
 // (x: number, y: number): void;
 // }
 // Type alias
 type Point = {
 x: number;
 y: number;
 };
 
 type SetPoint = (x: number, y: number) => void;
 // 2. Other Types 型のエイリアスは、基本型、ユニオン型、タプルなど他の型にも使用できるが、interfaceには使用できない。
 // primitive 基本的な型
 type Name = string;
 // object
 type PartialPointX = { x: number; };
 type PartialPointY = { y: number; };
 // union
 type PartialPoint = PartialPointX | PartialPointY;
 // tuple
 type Data = [number, string];
 // dom
 let div = document.createElement('div');
 type B = typeof div;
 // 3. Extend
 // どちらも拡張できるが、構文が異なる。また、インターフェースと型エイリアスは相互に排他的ではないことに注意。インターフェースは型エイリアスを拡張することができるし、その逆もしかりである。
 
 // Interface extends interface
 interface PointX { x: number; }
 interface PointY extends PointX { y: number; }
 // Type alias extends type alias
 type PointA = { x: number; };
 type PointB = PointA & { y: number; };
 // Interface extends type alias
 type SizeA = { x: number; };
 interface SizeB extends SizeA { y: number; }
 // Type alias extends interface
 interface SizeX { x: number; }
 type SizeY = SizeX & { y: number; };
 // 4. class Implements
 // クラスはインターフェイスや型のエイリアスを同じように実装できる。ただし、クラスとインターフェースは静的なものとみなされる。したがって/名前付きユニオン型のための拡張型エイリアス。
 interface PointV {
 x: number;
 y: number;
 }
 
 class SomePoint implements PointV {
 x:number;
 y:number;
 constructor(x:number,y:number){
 this.x = x;
 this.y = y;
 }
 }
 
 type PointV2 = {
 x: number;
 y: number;
 };
 
 class SomePoint2 implements PointV2 {
 x:number;
 y:number;
 constructor(x:number,y:number){
 this.x = x;
 this.y = y;
 }
 }
 
 type PointXX = { x: number; } | { y: number; };
 
 // FIXME: can not implement a union type
 // class SomePoint3 implements PointXX {
 // x:number;
 // y:number;
 // constructor(x:number,y:number){
 // this.x = x;
 // this.y = y;
 // }
 // }
}

要約すると、インターフェースとタイプは非常に似ており、両方が使える場面はたくさんあります。しかし、微妙な違いがあります:

型:オブジェクト、関数の両方が適用されますが、型は基底型、ユニオン型、メタ・アンカーに使用できます。同じ名前でのマージ:インターフェースはサポートしていますが、型はサポートしていません。計算された属性:型はサポートしていますが、インターフェースはサポートしていません。一般的に、publicなものはinterfaceで実装され、interfaceで実装できないものはtypeで実装されます。主に、プロジェクト内の一貫性を保つためです。

Read next

JS学習シリーズ 7-配列オブジェクト、ラッパー・オブジェクト、ブール値オブジェクト

前書き\n\n配列オブジェクト\nArray はネイティブオブジェクトであり、新しい配列を生成するために使用できるコンストラクタです。\nvar c = new Array;\nvar c = Array(0x2

Jan 11, 2021 · 12 min read