Open35

ブルーベリー本読書メモ

t3yamotot3yamoto

1.2.1
関数オーバーローディングの機能(名前が同じでシグニチャが異なる関数を定義)はない。「ランタイムの挙動が型情報に依存しない」ため。

t3yamotot3yamoto

1.2.2
TS -> JS へのコンパイル時、やっていること

  • 型注釈を取り除く
  • 実行環境に応じて古い構文 (ES5 etc) に変換
t3yamotot3yamoto

コラム1
TS 独自の機能である enum や namespace は使うべきでない(TS の思想にそぐわない)という意見があるらしい。
enum 好きなんだがなあ... 使わない場合はどう実装すれば良いのか確認。

t3yamotot3yamoto

1.3.4
tsconfig.json を色々いじらないといけないの萎えるな
tsc --init でいい感じに雛形出してくれるのは良い

t3yamotot3yamoto

Node.js 向けの TS プログラムをサクッと実行するやつ。
tsc -> node をまとめてやってくれる
https://github.com/TypeStrong/ts-node

t3yamotot3yamoto

試してみる。なんか拡張子がおかしいと怒られる...

❯ 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'
}
t3yamotot3yamoto

2.4.4
等価演算子は基本 ===で良いが、foo == null とすると null または undefined であることを確認できるので例外的に使う。

t3yamotot3yamoto

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";
t3yamotot3yamoto

2.5.4
switch 文の break の書き忘れを防ぐコンパイラオプション

tsconfig.json
{
     "noFallthroughCasesInSwitch": true,               /* Enable error reporting for fallthrough cases in switch statements. */
}
t3yamotot3yamoto

3.1.3
Computed property name

const propName = "foo";
const obj = {
    [propName]: 123
}

console.log(obj[propName]);
t3yamotot3yamoto

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 の コピー が展開される

t3yamotot3yamoto

3.2.3
type 文

type FooBarObj = {
    foo: number,
    bar: string
};

const obj: FooBarObj = {
    foo: 123,
    bar: "hoge"
};
t3yamotot3yamoto

既存の type の別名を付けられる

type UserId = string;
const id: UserId = "t3yamoto";
t3yamotot3yamoto

3.2.4
interface 宣言
オブジェクト型だけに使用できる型名の宣言方法
ほとんどの場合 type 文で代用できる

interface FooBarObj {
    foo: number,
    bar: string
};

const obj: FooBarObj = {
    foo: 123,
    bar: "Hello"
};
t3yamotot3yamoto

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
};
t3yamotot3yamoto

3.3.1
部分型
fooBarBazObjFooBar であるための条件(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;
t3yamotot3yamoto

3.4.1
型引数

type User<T, V> = {
    name: string;
    child: T;
    mother: V;
};

const me: User<number, string> = {
    name: "aaa",
    child: 1,
    mother: "bbb"
}
t3yamotot3yamoto

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];
t3yamotot3yamoto

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"}
t3yamotot3yamoto

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));
t3yamotot3yamoto

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
t3yamotot3yamoto

4.2 関数の型

メソッドのシグニチャも type で表現できる

type F = (repeatNum: number) => string;

const xRepeat: F = (num: number): string => "x".repeat(num);
t3yamotot3yamoto

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);