TypeScript インターフェースでオブジェクト型の定義

TypeScript インターフェースでオブジェクト型の定義

いまさらながらTypeScriptに入門

2022-01-10TypeScript

定義できる内容色々

プロパティ、メソッドの定義

オブジェクトに存在すべきプロパティ、メソッドを定義する

// 定義内容
interface Person{
    name:string;//プロパティの定義
    age?:number;//プロパティの定義(?をつけて任意にする)
    show():string;//メソッドの定義
}

let p:Person = {
    name:'テス男',
    // age:10,//ageは任意プロパティなのでなくてもエラーにならない
    show(){
        return `[${this.name}]`;
    },
}
console.log(p.show());
//[テス男]

関数の定義

特定の引数、戻り値を持つ関数の型を作成する(コールシグネチャ)。

// 関数の引数と戻り値の型を定義
interface Method1{
    (x: number, y:number):number;
}
// 同じ引数、戻り値の関数を作成する
let test:Method1 = function add(x:number, y:number) {
    return x + y;
}
console.log(test(2, 3));


/** 複数のメソッドを定義しオーバロード */
interface Method2{
    (x: number, y:number):number;
    (x: string):string;
}

let test2:Method2 = (x:any, y:any=3):any => {
    if(typeof x === 'number'){
        console.log( x + y);
    }else if(typeof x === 'string'){
        console.log('引数:' + x);
    }
}

test2(5,2);
test2('TEST');

プロパティを動的に作成する(インデックスシグネチャ)

連想配列的な、、、

[key 名:key の型]:value の型」で自由な値を指定できるようになる。

interface Profile {
    // 内部のオブジェクトの型も定義が必要
    [index: string]: string | number | boolean;
    specialFlg: boolean;//特定のプロパティを定義
  }

let profile: Profile = { specialFlg: true };

// 動的にプロパティを設定可能
profile['name'] = 'test';
profile.name2 = 'test';
profile.age = 2;
// profile.array = [2,3,4];//定義していない型を代入するとエラーになる

プロパティに文字列型、数値型を混同させる

// 文字列型、数値型のキーの混同
interface Profile2 {
    // keyの型はnumberまたはstring、valueの型は合わせる必要あり
    [index: string]: string;
    [index: number]: string;
  }

  let profile2: Profile2 = {
    test: '文字列型',
    22: 'number型',
  };

console.log(profile2.test);
console.log(profile2[22]);//数値型のプロパティにアクセスる際は添え字でアクセス

型の互換性

オブジェクトを別の型に代入するには同じプロパティを持っている必要がある(任意プロパティならなくても OK)⇒ 構造的部分型

// x,yを持つ型IntA
interface IntA{
    x:number;
    y:number;
}

// xを持つ型IntB
interface IntB{
    x:number;
}

let valA:IntA={
    x:3,
    y:4
};
let valB:IntB ={
    x:3,
};

valB = valA;//両方ともxプロパティを持っているので代入できる。ただ、valBはyにアクセスできない
// console.log(valB.y);//yに参照しようとするとエラーとなる
console.log(valB);//情報としては持っている({ x: 3, y: 4 })
// valA = valB;//vaB(IntB)はyプロパティを持っていないのでエラー

valB = {
    x:3,
//    y:4//ただ、直接いれようとするとエラー
}

全てのプロパティが省略可能なオブジェクトの場合、1つでもプロパティが一致していれば代入できる。

// 全てのプロパティが省略可能な型
interface IntA{
    name ?: string;
    age ?: number;
}

let objA = {test:'AAA'}
let objB = {name:'AAA', test:'AAA'}
let objC = {}

// let valA:IntA = objA; //testプロパティは定義されていないのでエラー
let valB:IntA = objB;//nameが存在しているのでOK
let valC:IntA = objC;//全て省略の場合はOK

keyofでプロパティ名を取得

keyofを仕様すると指定されたオブジェクトのプロパティ名を文字列リテラル型で取得できる。

interface IntA {
    name:string;
    age:number;
}
// IntAからプロパティ名を抽出し型として定義
// "name"|"age"と同じ
type typeA = keyof IntA;

//渡されたobj内のpropを返すメソッド
// propに何を渡されるかわからないのでtypeA型で定義すればnameかageのみ受け付けることができる
function getProp(obj:IntA, prop:typeA) {
    return obj[prop];
}

let p:IntA = {name: 'よしお' , age:30};

console.log(getProp(p, 'name'));
console.log(getProp(p, 'age'));
//console.log(getProp(p, 'test'));//testはtypeAで定義されていないのエラー

数値型のプロパティを持つオブジェクトにkeyofを使用した場合は、文字列リテラルとnumberの共用型になる。

// name,age,数値型をプロパティに持つ型
interface IntA {
    name:string;
    age:number;
    [index: number]: string;
}
// IntAからプロパティ名を抽出し型として定義
// number | "name" | "age"となる
type typeA = keyof IntA;

typeofとkeyofを組み合わせて指定したオブジェクトのプロパティを抽出できる

const obj = {
    name:"YOSHIO",
    age:30,
    weight:170,
    height:66,

}
type typeA = typeof obj;//オブジェクト型を抽出{    name: string;    age: number;    weight: number;    height: number;}
type typeB = keyof typeA;// "name" | "age" | "weight" | "height"型を抽出
// keyof typeof obj;//1文でも書いてもOK

LookupTypes 指定のプロパティの型を取得する

指定の型から配下のプロパティ型を取得する仕組み(LookupTypes)

interface IntA {
    name:string;
    age:number;
}

// intAのプロパティに紐づく型を取得して定義
type NameType = IntA['name'];//string型で定義
type AllType = IntA['name'|'age'];//string|number型で定義
type AgeType = IntA['age'];//number型で定義

MappedTypes 与えられた型から新しい型を作成

元々あるインターフェース型から新しい型を作成する

// name、ageをプロパティに持つインターフェスIntAを定義
interface IntA {
    name:string;
    age:number
}

/**
 * IntAのプロパティをすべてreadonlyにした型を作成
 */
type ReadonlyIntA ={
    // IntA型のプロパティセットからプロパティ名を取得し、Kに割り当てる
    readonly [K in keyof IntA] : IntA[K]
}

const test:ReadonlyIntA = {
    name:"aaa",
    age:2,
};
//test.age=4;//リードオンリーのためエラー


/**
 * 型引数を使用して、型の定義時にIntAを渡せるようにする
 * IntAのプロパティをすべて任意にした型を作成
 */
type Optional<T> = {
  // 与えられた型引数TのプロパティPに対して繰り返し定義する
  [P in keyof T]?: T[P] | null;
  // [P in keyof T] ⇒ Tからキーを文字列で取得
};
type OptionalProfile = Optional<IntA>;

// 新しく作成された型
// type OptionalProfile = {
//   name?: string | null | undefined;
//   age?: number | null | undefined;
// }

ConditionalTypes 条件に応じて型を振り分ける

与えられた型引数の内容を判定し、定義する型を振り分ける。

「T extends U ? X : Y」→型Tが型Uに代入できる場合には、型Xまたは型Y

//TがUに代入できるならT型、できないならnever
type Intersection<T,U> = T extends U ? T : never;

// 型Tに「string|boolean|number」を渡す。
// 型Uに「boolean|string[]| string」を渡す。
// stringとbooleanは代入できる、numbaerは代入できないため、CommonTypeはstring|boolean型となる
type CondType = Intersection<string|boolean|number , boolean|string[]| string>

let test:CondType;
test="3";//stringはOK
test= true;//booleanはOK
//test=2;//numberはエラー