typescript学習メモ

代入型定義がないとany型になる。 let hoge;

enumよりunion型でenumを表現するのが今時らしい

jsのStringはUTF-16エンコードされる。サロゲートペア文字を扱う場合、一部だけを拾ってしまう可能性があることに注意する。

文字列を正規化するString.normalizeメソッドが存在する。 同様の意味だが、コードポイントが異なる文字を正規化する。 例) 平成㍻ 正規化には4つオプションが存在する。 normalize("NFKC")を使っておけばとりあえずおk

undefined値が入っていない状態。null明示的な値が存在しない場合。 union型で、 let hoge: string | null = "test"; みたいに定義することで、この値はnullになりうるよ。と伝える必要がある。

タプル... const movie:[string, number] = ['Gozila', 1954]; 異なる型を持つことができる。 movie[0]//Gozila indexアクセスしかできない。

まとめて配列要素の取り出し

const smalls = [
"hoge",
"fuga",
"hyoga"
];

const [A, B, C] = samlls;

残余構文

const [,...other] = smalls; console.log(other);//['fuga', 'hyoge']

const others = ["gyoe", "gyopi"]; const newSmalls = [...smalls.slice(0,2), "P", ...others] //"hoge", "fuga", "P", "fuga", "hyoge"

配列ソート hoge.sort中の文字列を辞書順でソートする。

hogeの中身が数値の場合、注意が必要。 const number = [30, 1, 200];

元の配列を破壊せずにソート指定場合は、コピーを作成してソートする。 const sorted = [...hoge].sort((a,b) => a - b));

for

indexが欲しい場合

for(const [i, value] of iterable.entries()) {
 console.log(i, value);
}

index不要の場合

for(const value of iterable)

イテレータを配列に変換

const names = Array.from(iterable);
const names = [...iterable];

読み込み専用の配列

constは再代入を禁止するが、配列をimmutableにはできない。 readonly or as constを使用する。

const a: readonly number[] = [1,2,3];
const b = [1,2,3] as const;

readonlyな配列は制約が強い。mutableな配列を引数に持つ関数・メソッドにreadonlyは渡せない。mutableな配列にreadonlyな配列を代入することはできない。readonlyを使用するなら徹底的に使用するか、使用しないか全体で決めた方が良い。

配列っぽいオブジェクト

HTMLのDOM操作時に取得されるオブジェクト(HTMLCollection/NodeList)これらはlengthで長さが取得でき、indexアクセスできるが、配列が持っているメソッドをほとんど持っていない。 どちらもイテレータではあるので、下記コードは利用可能。 - for ... ofループ - スプレッド構文 - Array.from()で配列に変換後、配列メソッド利用

オブジェクト

値の取り出し const smallAnimal = { name: "小動物", favorite: "小籠包" };

const {name, favorite, age=3} = smallAnimals; const {name, ...others} = smallAnimals;

オブジェクト自身がnullであったり、オブジェクトの要素がnull/undefinedだったりする場合がある。 このような場合、オブジェクト/要素のチェックが必要になる。 これを簡便に行うためにオプショナルチェイニングという方法が存在する。 const favorite = smallAnimals?.favorite?.toUppercase(); nullがundefinedな値があると評価を停止する。

オブジェクトとMap Key オブジェクト 文字列固定、ユニーク Map 可変の連想配列

値 オブジェクト 複数の型 Map 一定

配列のループにfor..inを使用すると劇遅い

=== 厳密な比較。配列やオブジェクトの比較時は同一インスタンスか確認する。 == 文字列に変換後、比較

[条件] && 真の場合の値 👆新出ない場合はfalseが変える

タグ付き合併型

typeof {変数}で型名を文字列で返す。プリミティブ型のみ有効 nullは"object"になるので、nullチェックには使えない。

interface と Typeどうちがうん?

拡張方法が違う

interface A {
 hoge: string;
}

interface A {
 fuga: string;
}

///最終的にこーなる。
inteface A {
    hoge: string;
    fuga: string;
}

意図しない型になっている可能性がある。 typeは重複定義となりエラー。typeで拡張子たければ別の型を宣言する。

type A = {
 hoge: string;
}


type B = {
 fuga: string;
}

type Aextends = A & B;

Typescriptでオーバーロードは使えない

returnの返り値が一つなら、返り値の型は推定される。

getter/setter

{
    _favorite: string
    get favorite() {
        return this._favorite;
    },

    set favorite(v) {
        this._favorite = v;
    },

    function c() {

    }
}

アロー関数

無明関数をコールバック関数に渡すとthisが分からなくなる。 アロー関数を使用した場合、アロー関数が定義された時点でのthisを保持することができる。

時刻

サーバー側ではエポック時刻で時刻を扱い、クライアント側でローカル時刻に変換するのが良い。

JSON

//any型
const a = JSON.parse('{"name": "Jhon Cleese"}');

//asで型情報を付与できる。
const p = JSON.parse('{"name": "Jhon Cleese"}') as Person;

コンストラクタの引数でプロパティ宣言

class SmallAnimal {
    
    constructor(private animaltype: string = "ポメラニアン") {
    }

    say() {
        console.log("hoge");
    }
}

//👆とだいたい同じ
class SmallAnimal {

    private animalType:string;
    
    constructor() {
        this.animalType = "ポメラニアン"
    }

    say() {
        console.log("hoge");
    }
}

非同期処理

import fetch from 'node-fetch';

async function test() {
    console.log("Start");
    const resp = await fetch("https://google.com")
    const json = await resp.text();
    console.log(json);
    console.log("End");    
    const v = await sleep(1000);
    //Hoge 1
    console.log(`Hoge ${v}`); 
}

test();

const sleep = async (time: number) : Promise<number> => {
    //resolver 成功時に呼ぶ関数。引数に渡した値が返る。
    //rejector 省略可能で、処理失敗時に呼ぶ関数
    return new Promise<number>((resolver, rejector ) => {
        setTimeout(() => {
            resolver(1);
        }, time)
    });
};

以下のforEach内のawaitは待たない。

iterable.forEach(async value => {
await hogehgoe();
}

for/of/if/while/switchは待つ。

    for (const value of urlList) {
        const resp = await fetch(value)
        const json = await resp.text();
        console.log(json);
    }    

Promise all

Promiseをforでawaitすると実行時間×要素数の時間がかかるため、思いがけないボトルネックになる可能性がある。 Promise.allを使用すると同時に実行されるため、このような問題が回避できる。 同時実行数が多すぎると負荷がかかるのでは?→かかる。ヒープを食いつぶす可能性もある? 以下のように書くと上限値を設けながら効率よく処理を行うことができる

async function verySlowAsync(num: number , index: number) {
    console.log(`${num} ${index}`);
    return new Promise((resolve) => setTimeout(resolve, Math.random() * 1000));
}

async function execute() {

    console.time('execute');

    const INIT = 0;
    const MAX = 100;
    const CONCURRENCY = 10; // 同時実行できる数を定義

    //cntは並列タスク内で共通のindex
    let cnt = INIT;
    let promises = [];

    for (let i = 0; i < CONCURRENCY; i++) {
        //executor(loop)はPromise生成時に実行を開始している。Promiseのexecutor自体は同期的に実行される。
        let p = new Promise((resolve) => {
            //以下は並列に実行される。
            (async function loop(index) {
                if (index < MAX) {
                    await verySlowAsync(i, index);
                    loop(cnt++); //indexだと並列タスクの足並みがそろわない。
                    return;
                }
                resolve(0);
            })(cnt++);
        });
        // ここではPromiseで定義されたタスクをpushしているだけ。
        promises.push(p);
        console.log("End Push",i);
    }

    console.log("Promise.all");
    await Promise.all(promises);

    console.timeEnd('execute');

}

execute();

https://qiita.com/tonio0720/items/6f9319e4cce53256b4c9

例外処理

基本Javaと一緒。ただ、型情報がないので、catch後にinstanceを調べて処理を分岐する。

try {
    // 何かしらの処理
} catch (e) {
    // instanceof を使ってエラーの種類を判別していく
    if (e instanceof NoNetworkError) {
    
    } else if (e instanceof NetworkAccessError) {
    
    } else {
    // その他の場合
    }
}

fetch APIのように例外ではなく、okのような確認メソッドを用意する方法もある。

jsの例外処理の弱点 - メソッドが投げる例外を明示できない。ドキュメント・ソースを確認するしかない。 - 型情報がないので、instanceofで区別するしかない - Promise/asyncで何がrejectに渡されるのか型定義に書く方法がない。

返り値でエラーを返す方法を採用することもできる。 下記合併型を使用する例。タプル・オブジェクトの中にエラーを含めて返す方法もある。 やり方は色々

// 合併型
function create3(name: string, age: number): User | Error {
    if (age < 0) {
        return new Error("before born");
    }
    return {name, age};
}