TypeScriptでconstとreadonlyを使い分ける

TypeScriptで、読み込み専用・変更不可にするにはconstやreadonlyを使用します。

constとreadonlyは使う場面が異なり、

プロパティにはreadonly
変数にはconst

を使用します。

プロパティにconstを使うと文法エラーになります。

interface Point {
    const x: number;  // error TS1005: ';' expected.
    const y: number;  // error TS1005: ';' expected.
}

変数にreadonlyを使ってもエラーになります。

readonly greeting: string = "hello"; //error TS1128: Declaration or statement expected.

正しくは、プロパティにはreadonly

interface Point {
    readonly x: number;
    readonly y: number;
}

let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error TS2540: Cannot assign to 'x' because it is a constant or a read-only property.

変数にはconstを使います。

const greeting: string = "hello";
greeting = ""; // error TS2540: Cannot assign to 'greeting' because it is a constant or a read-only property.

TypeScriptでthisの型を指定する

JavaScriptでは、thisの型が変わることがあります。

class Sample {
    hello() { console.log(this.constructor.toString()); }
}

const sample = new Sample();
sample.hello(); //(1) => function Sample() {}

let hello = sample.hello; 
hello(); //(2) => function Object() { [native code] }

(1)ではthisはSample型でしたが、(2)ではObject型になりました。

関数の使い方を誤ってthisが想定外の型になり、思わぬエラーになることがあります。

TypeScriptでは、関数の1番目の引数をthisにすることで、thisの型を指定できます。

class Sample {
    hello(this: Sample) { console.log(this.constructor.toString()); }
}

thisは仮の引数であり、実際には存在しません。
関数を呼び出すときは、引数thisを指定する必要はありません。

先ほどのコードをコンパイルすると(2)のところで、コンパイルエラーになります。

const sample = new Sample();
sample.hello();

let hello = sample.hello;
hello(); //(2) error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'Sample'.

コンパイラに「–noImplicitThis」オプションを渡すと、関数内でthisの型を指定せずにthisを使うとエラーになります。

class Sample {
    name: string = "hello";
}
function hello() { console.log(this.name); }

hello.bind(new Sample())(); //=> error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.

JavaScriptではthisにまつわるトラブルが起こりがちです。

TypeScriptで「–noImplicitThis」をつけてthisの型を忘れずに指定すれば、安全なプログラミングができそうです。

TypeScriptの–strictNullChecksでnullやundefinedの代入をチェックする

TypeScriptでは、すべての型の変数にnullやundefinedを代入できます。

let str: string = "Hello";
str = null; // OK
str = undefined; // OK

strictNullChecksモードを有効にすると、nullとundefinedはそれぞれの型とvoid以外の変数に代入できなくなります。

strictNullChecksモードを有効にします。

tsc index.ts --strictNullChecks

今度はstring型の変数にnullやundefinedを代入すると、エラーになります。

let str: string = "Hello";
str = null; // error TS2322: Type 'null' is not assignable to type 'string'.
str = undefined; // error TS2322: Type 'undefined' is not assignable to type 'string'.

strictNullChecksモードを有効にすることで、nullやundefinedによるエラーを減らすことができます。

TypeScriptでサーバーからファイルをダウンロードして、ローカルに保存するサンプルコード

TypeScriptでサーバーからファイルをダウンロードして、ローカルに保存するサンプルコード。

imagesディレクトリを作成して、ファイルを保存します。
ダウンロードするファイルと保存するファイルのファイル名は、download()関数の引数で設定します。

事前準備

npm install node-fetch --save
npm install @types/node-fetch --save

ソースコード

import * as fs from "fs";
import * as path from "path";
import fetch from "node-fetch";

// ダウンロードしたファイルを保存するディレクトリ
const download_dir = path.join(process.cwd(), "images");

// ディレクトリがなければ作成する
if (!fs.existsSync(download_dir)) {
    fs.mkdirSync(download_dir);
}

/**
* URLを受け取り、該当するファイルをダウンロードして保存する
* @param url ダウンロードするファイルのURL
* @param filename 保存するファイルのファイル名
*/
function download(url: string, filename: string) {
    fetch(url, { method: "GET" }).then((response) => {
        console.log(`OK: ${filename}`);
        response.body.pipe(fs.createWriteStream(filename));
    }).catch((error) => console.log(`error:${url} ${error}`));
};

download(
    "http://~/example.jpg",
    path.join(download_dir, "sample.jpg"));