LoginSignup
56
60

More than 5 years have passed since last update.

TypeScriptで定数を定める

Last updated at Posted at 2017-03-13

TypeScriptでは、書き替えのできない定数を定める構文がいくつかあります。それらの違いを、簡単なサンプルコードで比べてみましょう。

[追記: 2018年9月2日] オブジェクトのプロパティの書き替え、およびReadonlyArray<T>型についての説明を補いました。

01 const宣言で定数を定める

変数をconst宣言すると、あとから値が書き替えられません。つまり、定数が定められます(「TypeScript: 変数の宣言」03「const宣言」参照)。つぎの関数(getAngle())は、引数のxy座標がx軸正方向となす角度をラジアンで返します。定数(RAD_TO_DEG)には、ラジアンから度数への変換比率を定めました。座標(1, $\sqrt{3}$)とx軸との角度は60度になります(三角定規の2辺の比率です)。

const RAD_TO_DEG: number = 180 / Math.PI;
// RAD_TO_DEG = 1;  // const宣言した値は書き替えられない
function getAngle(x: number, y: number): number {
    return Math.atan2(y, x);
}
console.log(getAngle(1, Math.sqrt(3)) * RAD_TO_DEG);  // 59.99999999999999

ただし、気をつけなければならないのは、const定数は代入した値が上書きできないだけだということです。オブジェクトを変更しなけければ、プロパティ値は書き替えられます。

const ZERO_POINT: {x: number, y: number} = {x: 0, y: 0};
// ZERO_POINT = {x: 100, y: 0};  // const宣言した値は上書きできない
ZERO_POINT.x = 100;  // プロパティ値は変えられる
console.log(ZERO_POINT);  // {x: 100, y: 0}

02 クラスに読み取り専用のプロパティを定める

const宣言は、クラスのプロパティには使えません。代わりに、readonly修飾子が備わりました(「TypeScript 2.0のreadonly修飾子」参照)。プロパティが読み取り専用に定められます。

class Point {
    static readonly RAD_TO_DEG: number = 180 / Math.PI;
    constructor(public x: number, public y: number) {}
    getAngle(): number {
        return Math.atan2(this.y, this.x);
    }
}
const point: Point = new Point(1, Math.sqrt(3));
// Point.RAD_TO_DEG = 1;  // 読み取り専用なので書き替えられないというエラー
console.log(point.getAngle() * Point.RAD_TO_DEG);  // 59.99999999999999

ただし、readonly修飾子についても、const宣言と同じく、オブジェクトのプロパティ値は変えられます。

class Point {

    static readonly ORIGIN = {x: 0, y: 0} as Point;

}
// Point.ORIGIN = new Point(0, 100);  // 読み取り専用なので書き替えられないというエラー
Point.ORIGIN.x = 100;  // プロパティ値は変えられる
console.log(Point.ORIGIN);  // {x: 100, y: 0}

配列については、ReadonlyArray<T>という書き替えのできない型が備わっています(「Readonly properties」参照)。この型づけがされると配列アクセス演算子[]による要素の書き替えも、インスタンス自身を変更するメソッドの呼び出しもできません。インスタンスを書き替えるメソッドは除かれてしまうからです。

class Point {

    static ZERO_ARRAY: ReadonlyArray<number> = [0, 0];

}
// Point.ZERO_ARRAY[0] = 100;  // 読み取り専用なので書き替えられないというエラー
// Point.ZERO_ARRAY.push(100);  // メソッドが存在しないというエラー

03 getアクセサで読み取りのみ定める

TypeScriptは、get/setアクセサでメソッドをプロパティのように扱えます(「TypeScript: クラス」06「アクセサでプロパティのようにメソッドを扱う」参照)。getアクセサのみ定めてsetアクセサを加えなければ、読み取り専用プロパティになります。

class Point {
    constructor(public x: number, public y: number) {}
    static get RAD_TO_DEG(): number {
        return 180 / Math.PI;
    }
    getAngle(): number {
        return Math.atan2(this.y, this.x);
    }
}
const point: Point = new Point(1, Math.sqrt(3));
// Point.RAD_TO_DEG = 1;  // 読み取り専用なので書き替えられないというエラー
console.log(point.getAngle() * Point.RAD_TO_DEG);  // 59.99999999999999

04 readonly修飾子とgetアクセサの違い

readonly修飾子でもgetアクセサでも、同じように定数が定められました。けれど、コンパイルされるJavaScriptコードの実装は違います。readonly修飾子の場合、JavaScriptには読み取り専用の修飾子がないので、プロパティはただクラスに加えられるだけです。したがって、実行時であれば値が変更できます(図001)。ReadonlyArray<T>型も、実行時には書き替えが可能です。

Point.RAD_TO_DEG = 180 / Math.PI;

図001■ readonly修飾子を与えたプロパティは実行時に書き替えできる

図001

getアクセサはECMAScript 5.1から備わったget構文にコンパイルされます。そのため、返す値は実行時も書き替えできません(図002)。なお、get/setアクセサは、ECMAScript 5以降の設定にしないと使えないことにご注意ください。

図002■getアクセサの返す値は実行時に書き替えできない

図002

コンパイルされるJavaScriptコードは、ECMAScript 5とECMAScript 2015(ECMAScript 6)とではつぎのように異なります。

// ECMAScript 5
Object.defineProperty(Point, "RAD_TO_DEG", {
    get: function () {
        return 180 / Math.PI;
    },
    enumerable: true,
    configurable: true
});
// ECMAScript 2015
class Point {

    static get RAD_TO_DEG() {
        return 180 / Math.PI;
    }

}
56
60
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
56
60