Typescriptで様々な型を扱う

Typescriptでは型を定義できます。プリミティブ型だけでなく、classやinterface、配列、連想配列等を定義できます。

また、nullableにしていないのにnullundefinedが代入できてしまう問題も解決しましょう。

型を付ける

シンプルに変数や引数に型をつける場合、変数: 型と書きます。

const i: number = 100;
const s: string = "hoge";
const arr: number[] = [1, 2, 3];

// any型は何でも受け付けるというもの
const arr: any[] = [1, "hoge", null, () => {}];

型の書き方はJSDocと同じです。

複数の型を持つ可能性がある場合、|で区切ります。

そのように複数のいずれかの型を受け入れる型をunion型と言います。

let a: number | string = 100;
a = "hoge";

let b: Array<number | string> = [100, "fuga"];

const c: {[key: string]: number} = {hoge: 100, fuga: 200};

// (引数) => 戻り値 とすると関数そのものの型を定義できる
function f(cb: (a: number) => boolean): boolean {
    return cb(100);
}

関数に型を付ける

関数の引数や戻り値に型をつける場合も、同じように: 型を書きます。

引数には引数名: 型を、戻り値にはfunction f(...): 型と書きます。

function f(a: number, b: string): boolean {
    return true;
}

アロー関数の場合、(...): 型 => {}のように書きます。

const f = () : boolean => {
    return true;
}

classの型

classでは、Typescriptでclassを定義した場合は特に指定する必要はありません。普通にclassを作るだけで入力補完等が効くようになります。

typescriptのclassで補完が効く例

しかし、最初はnullを入れておいて、後からインスタンスを代入するなどの場合は、let hoge: Hoge = null;のように型を指定する必要があります。

importする場合

classやinterfaceが外部にある場合、それらをimportする必要があります。

hoge.ts
export interface Fuga {
    f(): boolean;
}

export class Hoge implements Fuga {
    private aa: string = "hoge";
    public bb: number = 100;

    private internalF() {
        return 100;
    }

    f() {
        return true;
    }
}
app.ts
import { Hoge, Fuga } from "./hoge";

function f(hoge: Hoge, fuga: Fuga): void {
    // ....
}

ジェネリクスの指定

ジェネリクスの場合、<>の中に型を指定します。

function f(): Promise<boolean> {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(true);
        }, 1000);
    })
}

特定の値だけ受け付ける

型だけでなく、特定の値だけを受け付けるようにしたい場合、そのままその値を書きます。

const hoge: "foo" | 100 = 100;
const hige: "foo" | 100 = "foo";
const fuga: "foo" | 100 = 200; // Type '200' is not assignable to type '100 | "foo"'.

関数の引数

関数の引数にはデフォルト値や、省略可能化などを設定できます。

もし実引数を省略しても良い場合 (つまりundefinedとなっても良い場合) は、引数名?とします。

function f(hoge: number, fuga?: string) {
    if (fuga != undefined) {
        // ...
    }
    else {
        // ...
    }
}

// fuga? となっているため、エラーではない
f(100);

もしデフォルト値がある場合は呼び出し側で実引数を省略できますが、その場合は?を付けることはできません。省略できることが自明であるためです。

// fuga? とはしない
function f(hoge: number, fuga: string = "abc") {
    console.log(fuga);
}

f(100);

ここで、undefinedでも良いということはfuga: string | undefinedで良いと思われそうですが、この2つは明確に異なります。

省略するには?を明示する必要があります。

function f(fuga?: string) {
    // ...
}

f(); // OK


function g(fuga: string | undefined) {
    // ...
}

g(); // NG fugaは省略できない
g(undefined); // OK


function h(fuga: string) {
    // ...
}

h(undefined); // OK!
h(null); // OK!
h(100); // NG 仮引数はstring

nullとundefinedの扱い

上の例でも出てきましたが、string | nullのような書き方をせずともnullは代入できます。

const s: string = null; // OK
const s: string = undefined; // OK

しかし、これは多くの場合で期待されるものではないと思います。いちいちnullかどうかをチェックしてから動かす必要が出てくるためです。

これを回避するには、tsconfig.jsonに"strictNullChecks": trueを書き加えます。 (無い場合はpackage.jsonがあるフォルダに作成してください。)

tsconfig.json
{
    "compilerOptions": {
        // nullやundefinedを厳密にチェックするオプション
        "strictNullChecks": true
    }
}

これを設定すると、const s: string = null;などが想定通りエラーとなります。

function h(hige: number) {
    console.log(hige);
}

h(undefined); // Argument of type 'undefined' is not assignable to parameter of type 'number'.
h(null); // ほぼ同上

const s: string = null; // Type 'null' is not assignable to type 'string'.
const ss: string = undefined; // ほぼ同上
const n: string | null = undefined; // ほぼ同上

型に別名をつける

C言語のtypedefように、typeを利用することで型に別名をつけることができます。

type 新しい型名 = 型と書きます。

type MyString = string | null;

const s: MyString = null;

// 関数の型定義
type MyCallback = (a: number) => boolean;

function f(cb: MyCallback): boolean {
    return cb(100);
}

// 連想配列の定義
type MyObj = {
    name: string,
    age: number,
}

const person: MyObj = {
    name: "Taro",
    age: 18,
}

まとめ

Typescriptで指定する型は、JSDocで指定した型と同じです。型を指定することで、コンパイル時にエラーとすることができ、より安全なコードを書くことができます。

null関連の扱いがデフォルトでは甘いため、ミスを防ぐために必ずnullを厳密にチェックする設定しておきましょう。

ts type thumb

役に立ったらシェアしよう!