ブルーベリー本読書メモ
1.2.1
関数オーバーローディングの機能(名前が同じでシグニチャが異なる関数を定義)はない。「ランタイムの挙動が型情報に依存しない」ため。
1.2.2
TS -> JS へのコンパイル時、やっていること
- 型注釈を取り除く
- 実行環境に応じて古い構文 (ES5 etc) に変換
コラム1
TS 独自の機能である enum や namespace は使うべきでない(TS の思想にそぐわない)という意見があるらしい。
enum 好きなんだがなあ... 使わない場合はどう実装すれば良いのか確認。
1.3.3
package.json の "type"
.js ファイルをスクリプトとして解釈するか、モジュールとして解釈するか。後者がより現代的らしい。
もうちょい調べる
@types/node は TS で Node.js 向けにプログラムを書くときの型情報って感じ?
1.3.4
tsconfig.json を色々いじらないといけないの萎えるな
tsc --init でいい感じに雛形出してくれるのは良い
Node.js 向けの TS プログラムをサクッと実行するやつ。
tsc -> node をまとめてやってくれる
試してみる。なんか拡張子がおかしいと怒られる...
❯ npx ts-node src/index.ts
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /Users/t3yamoto/dev/src/github.com/t3yamoto/ts-blueberry-book/practice/src/index.ts
at new NodeError (node:internal/errors:387:5)
at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:76:11)
at defaultGetFormat (node:internal/modules/esm/get_format:118:38)
at defaultLoad (node:internal/modules/esm/load:81:20)
at nextLoad (node:internal/modules/esm/loader:165:28)
at ESMLoader.load (node:internal/modules/esm/loader:608:26)
at ESMLoader.moduleProvider (node:internal/modules/esm/loader:464:22)
at new ModuleJob (node:internal/modules/esm/module_job:63:26)
at ESMLoader.#createModuleJob (node:internal/modules/esm/loader:483:17)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:441:34) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
ほほう...?
"type": "module"をpackage.jsonから削除することで実行することができるようになる
確かにうまく行った
この辺が参考かな(よく分からんが
コラム4
TS の number はIEEE754倍精度浮動小数点数
他の言語の double 相当
Node.js のライブラリに型情報を付けるためのプロジェクト
大抵は @types/ライブラリ名
でインストールできそう。
npm install --save big.js @types/big.js
ここでも探せる
2.4.4
等価演算子は基本 ===
で良いが、foo == null
とすると null または undefined であることを確認できるので例外的に使う。
2.4.6
??
は null, undefined のときだけ 1 を返す。環境変数のデフォルト値設定に使える。
||
は "" や 0 など真偽値にした際に false になる値の場合も 1 を返す。
> "" || 1
1
> "" ?? 1
''
> 0 || 1
1
> 0 ?? 1
0
> null || 1
1
> null ?? 1
1
> undefined || 1
1
> undefined ?? 1
1
// 環境変数 e が定義されていなかったらデフォルト値を設定
const e: string = process.env.e ?? "defaultValue";
2.5.4
switch 文の break の書き忘れを防ぐコンパイラオプション
{
"noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
}
3.1.3
Computed property name
const propName = "foo";
const obj = {
[propName]: 123
}
console.log(obj[propName]);
3.1.5
Spread syntax
const obj1 = {
bar: 456,
baz: 789
};
const obj2 = {
foo: 123,
...obj1
};
console.log(obj2); // { foo: 123, bar: 456, baz: 789 }
obj2 には obj1 の コピー が展開される
重複するキーがある場合は後勝ち
3.2.3
type 文
type FooBarObj = {
foo: number,
bar: string
};
const obj: FooBarObj = {
foo: 123,
bar: "hoge"
};
既存の type の別名を付けられる
type UserId = string;
const id: UserId = "t3yamoto";
3.2.4
interface 宣言
オブジェクト型だけに使用できる型名の宣言方法
ほとんどの場合 type 文で代用できる
interface FooBarObj {
foo: number,
bar: string
};
const obj: FooBarObj = {
foo: 123,
bar: "Hello"
};
3.2.6
Optional property
?
をつけるとオプショナルに
type MyObj = {
foo: boolean,
bar: boolean,
baz?: number
};
const obj: MyObj = {
foo: true,
bar: false,
};
const obj2: MyObj = {
foo: true,
bar: false,
baz: 1
};
この時の baz の型は number | undefined
3.2.7
Readonly property
type MyObj = {
readonly foo: number
}
3.3.1
部分型
fooBarBazObj
は FooBar
であるための条件(foo:string, bar:number をもつ)を満たしているので、FooBar
型としても振る舞える
type FooBar = {
foo: string;
bar: number;
}
type FooBarBaz = {
foo: string;
bar: number;
baz: boolean;
}
const fooBarBazObj: FooBarBaz = {
foo: "foo",
bar: 123,
baz: true
}
const fooBarObj: FooBar = fooBarBazObj;
3.4.1
型引数
type User<T, V> = {
name: string;
child: T;
mother: V;
};
const me: User<number, string> = {
name: "aaa",
child: 1,
mother: "bbb"
}
3.5.3
配列の型
type Human = {
name: string;
age: number;
};
const people: Human[] = [{name: "Taro", age:20}, {name: "Hanako", age:23}];
type HasName = {
name: string;
};
type Pet = {
name: string;
age: number;
owner: Human;
};
const taro: Human = {name: "Taro", age: 20};
const choco: Pet = {name: "Choco", age: 7, owner: {name: "Goro", age: 44}};
// Human, Pet は HasName の部分型なので HasName 配列に格納できる
const family: Array<HasName> = [taro, choco];
3.6 分割代入
const numbers: number[] = [1,2,3,4,5,6,7];
const [first, second, ...rest] = numbers;
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3,4,5,6,7]
const fooBarBaz = {
foo: "foo",
bar: "bar",
baz: "baz",
};
const {foo, ...restObj} = fooBarBaz;
console.log(foo); // "foo"
console.log(restObj); // {"bar": "bar", "baz": "baz"}
4.1.5 アロー関数式
type Human = {
height: number;
weight: number;
};
// 関数の引数で分割代入できる
const calcBMI = ({height, weight}: Human): number => {
return weight / height ** 2;
};
const me: Human = {
height: 1.76,
weight: 67,
}
console.log(calcBMI(me));
4.1.6
メソッド記法
前者の double
がメソッド記法、double2
は通常のプロパティ宣言 + アロー関数
const obj = {
double(num: number): number {
return num * 2;
},
double2: (num: number): number => num * 2,
};
console.log(obj.double(100)); // 200
console.log(obj.double2(-50)); // -100
4.2 関数の型
メソッドのシグニチャも type で表現できる
type F = (repeatNum: number) => string;
const xRepeat: F = (num: number): string => "x".repeat(num);
4.2.5 コールシグニチャ
type MyFunc = {
isUsed?: boolean;
(arg: number): void;
};
const double: MyFunc = (arg: number) => {
console.log(arg * 2);
}
double.isUsed = true;
console.log(double.isUsed);
double(1000);