はじめに
普段はC#を使っている者が、JavaScriptとTypeScriptを学んで今後実装する上で気をつけたいことをまとめました。
以下の技術同人誌から学んだことを中心に追加で調べたことなどをまとめています。
りあクト! TypeScriptで始めるつらくないReact開発 第3版【Ⅰ. 言語・環境編】 - くるみ割り書房 ft. React - BOOTH
JavaScript
もともとはブラウザ上で動く言語だったが、Node.jsなどを使えばサーバー上にアクセスできるようになりサーバーサイドでも動くようになった。
JSX(独自のタグ技術)は、JavaScriptの拡張構文であり、コンパイルするとJavaScriptに変換される。
JavaScript | MDN
ブラウザ上で試したい時
アカウント登録なしで手軽に試せる。
JSFiddle - Code Playground
他にもいろいろあるみたいです。
ブラウザ上でJavaScriptの実行ができるサービスまとめ - Qiita
関数
// 関数宣言文
// 非推奨: 同じ関数名だと再宣言出来てしまうこと、宣言する前に呼び出し可能であることから
function multiply(x, y) {
return x * y;
}
// Functionコンストラクタで定義した関数
// 非推奨: 常にグローバルスコープで実行されることから
var multiply = new Function('x', 'y', 'return x * y');
// 関数式
// ES2015以降はアロー関数式が推奨
const multiply = function(x, y) {
return x * y;
};
// アロー関数式
// ES2015で追加。
// 推奨: 関数外のスコープのthisが使われるためクラス構文の時に分かりやすいから
// 再宣言や宣言する前に代入ができないようにconstにしておくこと
const multiply = (x, y) => {
return x * y;
};
残余引数(Rest Parameters)
引数の名前に ... のプレフィックスを付けることで不定数の引数を配列として表す。
Array インスタンスなので、sort, map, forEach, pop などのメソッドを直接適用できる。
function myFun(a, b, ...manyMoreArgs) {
console.log("a", a)
console.log("b", b)
console.log("manyMoreArgs", manyMoreArgs)
}
myFun("one", "two", "three", "four", "five", "six")
// console output:
// a, one
// b, two
// manyMoreArgs, [three, four, five, six]
スプレッド構文
引数や配列リテラルの要素の名前に ... のプレフィックスを付けることで配列式や文字列などを展開する。
オブジェクトリテラルを展開することもできる。
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
const obj = { red: 1, blue: 2, yellow: 3 };
const obj2 = { ...obj, green: 4 };
console.log(sum(...numbers));
console.log(obj2);
// console output:
// 6
// Object { red: 1, blue: 2, yellow: 3, green: 4 }
プロトタイプベース
どのオブジェクトもプロトタイプと呼ばれる、他のオブジェクトへの内部的な繋がりを持っています。
そのプロトタイプオブジェクトも自身のプロトタイプを持っており、あるオブジェクトのプロトタイプが null に到達するまでそれが続きます。
継承とプロトタイプチェーン - JavaScript | MDN から引用。
Javaなどのクラスベースと違い、実態のあるオブジェクトを継承していることになる。
var o = new Foo();
これを呼び出したとき、 JavaScript は、
var o = new Object();
o.[[Prototype]] = Foo.prototype;
Foo.call(o);
実際にはこれ(あるいはこのような何か)を行っており、
o.someProp;
後にこうすると、 o が someProp プロパティを持っているかどうかを調べ、もし持っていなければ Object.getPrototypeOf(o).someProp を、そこにも存在しなければ Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp を…と調べていきます。
null は、プロトタイプを持たず、プロトタイプチェーンの最終リンクとなります。
継承とプロトタイプチェーン - JavaScript | MDN から引用。
コンストラクタ関数は、プロトタイプオブジェクトを継承して新しくオブジェクトインスタンスを生成するための関数ということになる。
オブジェクトプロパティのショートハンド
ES6では、オブジェクトリテラルの定義するときにオブジェクトのプロパティ値の記載を省略できる。
プロパティ名がキーと一致しているときのみショートハンドが使える。
// 通常
const obj = { x:x, y:y };
// ショートハンド
const obj = { x, y };
あなたが知らないJavaScriptの便利すぎるショートハンド19選 – WPJ
分割代入
配列から値を取り出す、またはオブジェクトからプロパティを取り出して別の変数に代入ができるようになる。
Reactの開発でも良く見かける。
const user = {
id: 42,
is_verified: true
};
const {id, is_verified} = user;
console.log(id); // 42
console.log(is_verified); // true
this
メソッドとして実行した時はアクセス演算子.の前のオブジェクトが、new演算子をつけた時newによって新規に生成されたオブジェクトがthisになる。
関数内にないトップレベルのコードや関数をオブジェクトのメソッドでなくオブジェクトとして呼ぶと、thisはグローバルオブジェクトになる。
Global object (グローバルオブジェクト) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN
JavaScript の this を理解する多分一番分かりやすい説明 - Qiita
👻globalThis👻と🌏global🌏と🌝this🌝 - Qiita
Strict モード
ES5で追加。
ファイルの先頭行や関数の最初に 'use strict' を記述することで有効になる。
任意のオブジェクトのコンテキストがない場合(関数をオブジェクトのメソッドでなくオブジェクトとして呼ぶ)、this が undefined になる。クラス構文内の関数でのthisもグローバルオブジェクトではなくundefinedになる。
Strict モード - JavaScript | MDN
クロージャ
クロージャは、組み合わされた(囲まれた)関数と、その周囲の状態(レキシカル環境)への参照の組み合わせです。言い換えれば、クロージャは内側の関数から外側の関数スコープへのアクセスを提供します。JavaScript では、関数が作成されるたびにクロージャが作成されます。
関数の中で定義された関数をクロージャ、外側の関数をエンクロージャと呼ぶ。
function makeFunc() {
const name = 'Mozilla';
function displayName() {
alert(name);
}
return displayName;
}
const myFunc = makeFunc();
myFunc();
myFuncを定義時にエンクロージャmakeFuncによってクロージャdisplayNameのインスタンスの参照が返されている。
そのため、変数nameがGCに回収されず残っている。
TypeScript
AltJSであり、ビルド時にはJavaScriptに変換される。独自のコンパイラを所持している。
TypeScriptは、JavaScriptで型定義、インターフェイス、クラス、null/undefined safeに出来る・使えるようになるなどJSの上位互換といわれている。
特に型定義によって、実行前のビルド時にエラーやバグを検知することが出来ることが大きい。
MicroSoft産。
TypeScript: Typed JavaScript at Any Scale.
Introduction - TypeScript Deep Dive
Reactでは、公式で大きな規模での開発時はTypeScriptの使用を薦めている。
静的型チェック – React
ブラウザ上で試したい時
アカウント登録なしで手軽に試せる。
TypeScript Playground
npmパッケージをインストールせずrequireして使える。
Runkit + npm
型について
きちんと型のnull安全性を保障するには、tsconfig.json ファイルにコンパイラオプション strictNullChecks を設定する必要がある。
strictNullChecksの他にも便利なオプションを含んだstrictオプションを有効にすると便利。
型の互換性は、構造的サブタイピング(Structural Subtyping)に基づいており、下記のように型の構造が合致していればエラーにならない。
interface Named {
name: string;
}
class Person {
name: string;
}
let p: Named;
// OK, because of structural typing
p = new Person();
TypeScript: Handbook - Type Compatibility
クラス
クラスを定義すると、「クラスインスタンスのインターフェース型宣言」と「コンストラクタ関数の宣言」が同時に実行される。
よって、TypeScriptでインポート/エクスポートするときもデフォルトでは型とオブジェクトの両方を同時にインポート/エクスポートする。
詳しくはTypeScript: Handbook - Classes を参照。
クラスの型は、インスタンスではなく下記の型エイリアスで定義する。
type Tree<T> = {
value: T;
left?: Tree<T>;
right?: Tree<T>;
};
型エイリアスは、任意の型に別名を与えて再利用できるようになっている。
TypeScript: Handbook - Advanced Types
インターフェースとType
インターフェースはクラスやオブジェクトの型を宣言するが、Typeは無名の型に別名を付けるといったコンパイルの解釈の違いがある。
機能にも違いがあり、インターフェースは、同じ名前のインターフェースに新しいプロパティがある場合、型定義が追加される。一方、Typeは同じ名前のTypeは宣言できない。
他の違いなど詳しくは、TypeScriptのInterfaceとTypeの比較 - Qiita を参照。
ユニオン型
演算子|を挟んで型を並べるといずれかの型が適用される複合的な型が定義できる。
function padLeft(value: string, padding: string | number) {
// ...
}
const str = padLeft("Hello world", '1'); // OK
const num = padLeft("Hello world", 1); // OK
TypeScript: Handbook - Unions and Intersection Types
リテラル型
JavaScriptのプリミティブのように任意の値(文字列または数値、boolean)以外代入させない型。
ユニオン型と組み合わせ演算子|を挟んで型を並べるといずれかの型が適用される複合的な型が定義できる。
enumよりコンパイル後のコードが短いためこちらが使われることが多い。
type CardinalDirection =
| "North"
| "East"
| "South"
| "West";
function move(distance: number, direction: CardinalDirection) {
// ...
}
move(1,"North"); // Okay
move(1,"Nurth"); // Error!
リテラル型 - TypeScript Deep Dive 日本語版
Const アサーション
定数に型注釈を付与してくれる。
const Colors = {
red: "RED",
blue: "BLUE",
green: "GREEN"
} as const;