Advanced Types(Typescriptを学んでいこう #5)

Advanced Types(Typescriptを学んでいこう #5)

第5回は高度な型についてです。

交差型

交差型(intersection type)は複数の型を1つに連結したものです。 これは既存の型を統合し、必要とする機能を全て備えた1つの型を得ることを可能にしてくれます。

type Admin = {
    name: string,
    privileges: string[]
}

type Employee = {
    name: string,
    startDate: Date
}

type ElevatedEmployee = Admin & Employee;

// AdminとEmployeeの両方の型を満たす必要がある
const e1: ElevatedEmployee = {
    name: 'Max',
    privileges: ['create-server'],
    startDate: new Date()
};

交差型はどんな型に対しても使うことができるので、プリミティブ型を使うこともできます。

type Combinable = string | number;
type Numeric = number | boolean;

type Universal = Combinable & Numeric;

型ガード

型ガードは、特定のスコープ内で変数の型を絞り込むための機能です。型ガードの方法はいくつか種類があります。

typeof

変数が特定の型であるかどうかをチェックします。

type Combinable = string | number;

function add (a: Combinable, b: Combinable) {
    if (typeof a === 'string' || typeof b === 'string') {
        return a.toString() + b.toString();
    }
    return a + b;
}

instanceof

オブジェクトが特定のクラスのインスタンスであるかどうかをチェックします。

class Car {
    drive() {
        console.log('Driving...');
    }
}

class Truck {
    drive() {
        console.log('Driving a truck...');
    }
    loadCargo(amount: number) {
        console.log(`Loading cargo ... ${amount}`);
    }
}

type Vehicle = Car | Truck;
function useVehicle(vehicle: Vehicle) {
    vehicle.drive();
    // インスタンスがTruckであることを判定
    if (vehicle instanceof Truck) {
        vehicle.loadCargo(1000);
    }
}

in

オブジェクト上のプロパティの存在をチェックします。

type Admin = {
    name: string,
    privileges: string[]
}

type Employee = {
    name: string,
    startDate: Date
}

type UnknownEmployee = Employee | Admin;
function printEmployeeInformation(emp: UnknownEmployee) {
    console.log(`Name: ${emp.name}`);
    if ('privileges' in emp) {
        console.log(`Privileges: ${emp.privileges}`);
    }
    if ('startDate' in emp) {
        console.log(`Start Date: ${emp.startDate}`);
    }
}

Union型の判別(Discriminated Union)

クラスのプロパティを利用して、Union型のうちどの型なのかを判別することができます。

interface Bird {
    // typeを追加することで判別できるようになる
    type: 'bird';
    flyingSpeed: number;
}

interface Horse {
    type: 'horse';
    runningSpeed: number;
}

type Animal = Bird | Horse;

function moveAnimal(animal: Animal) {
    let speed;
    // if文やswitch文などで判別
    switch (animal.type) {
        case 'bird':
            speed = animal.flyingSpeed;
            break;
        case 'horse':
            speed = animal.runningSpeed;
            break;
    }
    console.log(`Moving at speed ${speed}`);
}

moveAnimal({ type: 'horse', runningSpeed: 10});

Type Casting(Type Assertion)

型推論を明示的に上書きすることができます。2通りの書き方があります。

asを使った書き方

const userInputElement = document.getElementById('user-input')! as HTMLInputElement;

<>を使った書き方

const userInputElement = <HTMLInputElement>document.getElementById('user-input')!;

インデックス型(Index Signature)

TypeScriptのインデックスシグネチャは、オブジェクトがインデックス付きアクセス(obj[key])をサポートすることを示す特殊なタイプです。これにより、オブジェクトが任意の数のプロパティを持つことができ、それぞれが特定の型を持つことが保証されます。

interface StringDictionary {
    [index: string]: string;
}

let dict: StringDictionary = {};

// 'hello'はstring型のため、これは有効。
dict["hello"] = "world";

// 123はstring型ではないため、これは無効。
// dict["hello"] = 123;  // Error: Type '123' is not assignable to type 'string'.

オーバーロード関数(Function Overload)

1つの関数に対して、複数の関数シグネチャを定義できる機能です。メイン関数のすぐ上に、同じ名前の関数を記述します。オーバーロード関数の最後にはセミコロンをつけるのが推奨されています。

オーバーロード関数の構文が用意されているのは関数宣言だけのため、アロー関数にはオーバーロードの構文がありません。

また、TypeScriptは関数シグネチャを上から順に試していき、最初にマッチしたシグネチャを採用するため、引数の型の範囲が狭い関数シグネチャを上に書くことが推奨されています。

type Combinable = string | number;
type Numeric = number | boolean;
type Universal = Combinable & Numeric;

// 同じ関数名で引数によって戻り値が違う場合に使える
function add(a: number, b: number): number; // セミコロンをつける
function add(a: string, b: number): string;
function add(a: number, b: string): string;
function add(a: string, b: string): string;
function add(a: Combinable, b: Combinable) {
    if (typeof a === 'string' || typeof b === 'string') {
        return a.toString() + b.toString();
    }
    return a + b;
}

const result = add('Max', 'Schwarz');
result.split(' ');

オプショナルチェーン(Optional Chaining)

オブジェクトのプロパティが存在しない場合でも、エラーを起こさずにプロパティを参照できる安全な方法です。データベースから値が必ず取得できるかわからない場合などに使用します。オプショナルチェーンはネストして使うことができます。

const fetchUserData = {
    id: 1,
    name: 'Max',
    Job: { title: 'CEO', description: 'My own company'}
}

// fetchUserDataやJobがundefinedやnullでもエラーにならない
console.log(fetchUserData?.Job?.title);

また関数や配列に対してもオプショナルチェーンを使うことができます。

// 関数のオプショナルチェーン
const increment = undefined;
const result = increment?.(1);
console.log(result);

const increment = (n) => n + 1;
const result = increment?.(1);
console.log(result);

// 配列のオプショナルチェーン
const books = undefined;
const title = books?.[0];
console.log(title);

const books = ["サバイバルTypeScript"];
const title = books?.[0];
console.log(title);

Null合体演算子(Nullish Coalescing)

変数がnullやundefinedの場合、デフォルト値を返すことができる機能です。nullやundefined以外であれば、変数の値をそのまま返します。

const userInput = undefined;
const storeData = userInput ?? 'DEFAULT';

console.log(storeData); // DEFAULT

以上です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です