『understanding-typescript』のメモ
tsconfig.json
tsc --init
で作ることができる。
これを実行すると、実行したカレントディレクトリがプロジェクトのルートディレクトリであることをTypeScriptに伝えることができる。
sourceMap
デバッグを効率的にできる機能。
true
にすると、開発者コンソールのsourceで.tsファイルの内容が見える。加えて、ブレークポイントを設置してdebuggerも利用できる。
仕組み
これは、.tsファイルをコンパイルしたときに.js.map
というファイルができ、jsファイルとtsファイルの何行目と何行目が一致しているかの対応付けを作ってくれる。
この対応付けファイルをブラウザが理解してくれる。
outDir
TypeScriptからコンパイルされたjavascriptファイルの出力先ディレクトリ。
入力先ディレクトリ構造がそのまま反映される。
これによるメリットはTypeScriptのインポートと同じ構造でJavascriptもインポートできる。
rootDir
コンパイル対象のTypeScriptコードを格納する場所。
includeとの違いは、rootDirに指定したディレクトリ以外にtsファイルが存在する場合、コンパイル時にエラーを出してくれる。
動作確認
"rootDir": "./src"
"outDir": "./dist"
├── src
├── analytics.ts
└── app.ts
↓
dist
配下にapp.js
とanalytics.js
ができる
"rootDir": "./"
"outDir": "./dist"
├── src
├── analytics.ts
└── app.ts
↓
dist/src
配下にapp.js
とanalytics.js
ができる
removeComment
TypeScriptで描いたコメントはコンパイルされたJSに含まれない。JSファイルのサイズを縮小できる。
noEmitOnError
コンパイルエラーが起きたときにJavascriptファイルを出力しないようにできる
ES6から導入されたモダンJavaScript機能のメモ
let/const
従来のvarとの違いとして、スコープの違いがある。
varはグローバルスコープまたは関数スコープのどちらか。
letはブロックスコープ
アロー関数
スプレッド演算子
レストパラメータ
任意の数の引数を配列で受け取れる
配列とオブジェクトの分割代入
const items = ["hoge", "fuga"];
const [item1, item2, ...remainingItems] = items;
const person = {
name: "hoge",
age: 30
}
const {name: userName, age} = person;
コンパイルターゲットを意識する
tsconfig.jsonのtarget
に指定したバージョンによってコンパイルされるJavascriptの構文は異なる。
なのでES5とか古めのバージョンがtargetだとモダンなJavascriptの構文は使われない。
クラス
protected修飾子
継承したクラスからも参照できる
高度な型
交差型
Union型を交差型で定義する場合、結合対象の型が持つ共通の型になる。以下の場合はNumber型になる
type Combinable = string | number;
type Numeric = number | boolean;
type Universal = Combinable & Numeric;
// OK
const a: Universal = 1;
// NG
const b: Universal = "test";
型ガード
実行時エラーを防止するために、型ガードで何かをする前にその値の型をチェックすることができる。
in
を使う
オブジェクトの場合はtype Admin = {
name: string;
privileges: string[];
};
type Employee = {
name: string;
startDate: Date;
};
type UnknownEmployee = Employee | Admin;
function printEmployeeInformation(emp: UnknownEmployee) {
console.log(emp.name);
// 型ガード
if ('privileges' in emp) {
console.log("Privileges: " + emp.privileges);
}
// 型ガード
if ('startDate' in emp) {
console.log("StartDate: " + emp.startDate);
}
}
printEmployeeInformation({ name: "manu", startDate: new Date() });
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;
const v1 = new Car();
const v2 = new Truck();
function useVehicle(vehicle: Vehicle) {
vehicle.drive();
// if ('loadCargo' in vehicle) {
// クラスでの型ガード
if (vehicle instanceof Truck) {
vehicle.loadCargo(1000);
}
}
useVehicle(v1);
useVehicle(v2);
ただし、interfaceの場合はinstanceofで判別することはできない。interfaceはJavascriptにコンパイルされないから。interfaceの場合はin
を使う。
Discriminated Unions
// Discriminated Unions
interface Bird {
// 共通の識別可能なプロパティを定義する(これはリテラル)
hoge: "bird";
flyingSpeed: number;
}
interface Horse {
// 共通の識別可能なプロパティを定義する(これはリテラル)
hoge: "horse";
runningSpeed: number;
}
type Animal = Bird | Horse;
function moveAnimal(animal: Animal) {
let speed;
// switch文で判別する
switch (animal.hoge) {
case "bird":
speed = animal.flyingSpeed;
break;
case "horse":
speed = animal.runningSpeed;
}
console.log("Moving at speed: " + speed);
}
console.log(moveAnimal({ hoge: "bird", flyingSpeed: 10 }));
型キャスト
その値が特定の型であることをTypeScriptに伝えたい場合に使用する
// 型キャスト
// 1. キャスト対象の前に<>をつける
// const userInputElement = <HTMLInputElement>document.getElementById("user-input")!;
// 2. asをつける(JSXなどで使用する場合、競合してしまうので)
// エクスクラメーションマーク(!)は、返す値がNullではないことをTypeScriptに伝える仕組み
// const userInputElement = document.getElementById("user-input")!;
const userInputElement = document.getElementById("user-input");
// userInputElement.value = 'こんにちは';
// 3. nullかどうかを確認した後にキャストする
if (userInputElement) {
// キャスト式を()で囲む
(userInputElement as HTMLInputElement).value = 'こんにちは';
}
インデックス型
オブジェクトのフィールド名をあえて指定せず、プロパティのみを指定したい場合に使えるのがインデックス型(index signature)
// index型
interface ErrorContainer { // { email: '正しいメールアドレスを入力してください', username: 'ユーザー名を入力してください' }
[prop: string]: string;
}
const errorBag: ErrorContainer = {
// stringに変換できるので、numberでもOK
1: "正しいメールアドレスを入力してください",
username: "ユーザー名を入力してください",
};
関数オーバーロード
// 関数オーバーロード
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: string, b: number): string;
function add(a: number, 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("'Hello'", "World");
result.split(" ");
Optional Chainig
// Optional chaining
const fetchedUserData = {
id: "u1",
name: "Max",
// job: {
// title: "CEO",
// description: "My own company"
// },
};
// 先頭から存在しているかどうかを判断する
console.log(fetchedUserData?.job?.title);
Null合体演算子
// Null合体演算子
// const userInput = null;
const userInput = '';
// null or undefined の場合、??の右側の値を返す
const storedData = userInput ?? "DEFAULT";
console.log(storedData);