TypeScriptの型まとめ(2024年10月) - ちょっとだけkintone

JSDocでも使うので、型が書けないと実践的なJavaScriptも書けないと思います。
でもめちゃくちゃめんどくさいですよね。

TypeScriptを書き始めのころはなんでもかんでも型を書いちゃうし、型が分からなくてanyにしちゃったりしますよね。

ここではできるだけ型を書かなくてもよくする方法をまとめます。

# 基本
```typescript
type TypeA = {
    type: "a";
    name: "hoge" | "bar";
    value: string;
}
type TypeB = {
    type: "b";
    id: "hoge" | "bar";
    value: 0;
}

//複合的な型(TypeAかTypeBのどちらか)
type ConbinedType = TypeA | TypeB;

//結合された型(TypeAであり同時にTypeBである)
type ConnectedType = TypeA & TypeB;
```


```typescript
/* 型推論 */

type Example = {
    a: string;
    b: string;
}

//プロパティの型を取得する。
type ExampleType = Example['a'];

const array : Example[] = [{
    a: 'a',
    b: 'b',
} , {
    a: 'A',
    b: 'B',
}];

///すごく長いコード......///

//arrayのメンバーの型定義がソースコード上深すぎてわからないとき、型推論から型を取り出す。
type ArrayMemberType = typeof array[0];
```

```typescript
/* クラスの型 */
class Processor {
    process(){
        return 'done';
    }
}

//クラスの型を取得する
type ProcessorType = typeof Processor;

//クラスのメソッドの型を取得する
type ProcessFunctionType = ProcessorType['prototype']['process'];

const instance = new Processor();

//インスタンス化されているクラス内の関数の型を取得する。
type InstanceProcessFunctionType = typeof instance.process;
```

# 戻り値(ReturnType)
```typescript
//例えば初期値を定義する関数があるとする。
function createInit(){
    const initData : {
        id : string;
        name : string;
    } = {
        id : "hoge",
        name : "fuga"
    }

    return initData;
}

//関数の構造を型として定義する
export type CreaterType = typeof createInit;

//関数の戻り値の型をデータ型として定義して使う
export type AppState = ReturnType<CreaterType>;
```

# 非同期処理(Awaited)
```typescript

//文字列や数値が非同期に帰ってくる関数。
async function asyncRandom() {
    if (Math.random() > 0.5) {
        return 'done';
    }else{
        return 0;
    }
}

//() => Promise<"done" | 0>
type AsyncType = typeof asyncRandom;

//戻り値を表現する型
type AsyncReturnType = Awaited<ReturnType<AsyncType>>;
// -> "done" | 0
```



# 複数の型を処理できるようにする
```typescript
type TypeA = {
    //型を複合して使う場合、分類用のプロパティを用意しておく
    type : "a";
    name : "hoge" | "bar";
    value : string;
}
type TypeB = {
    //型を複合して使う場合、分類用のプロパティを用意しておく
    type : "b";
    id : "hoge" | "bar";
    value : 0;
}

//テストデータ
const data : (TypeA | TypeB)[] = [{
    type : "a",
    name : "hoge",
    value : "fuga"
}, {
    type : "b",
    id : "hoge",
    value : 0
}];

for(let datum of data){
    //複合的な型から、型推論を利用して型を矯正する
    if("a" == datum.type){
        //nameはTypeAのプロパティなので、TypeBには存在しない
        console.log(datum.name);
    }else{
        //idはTypeBのプロパティなので、TypeAには存在しない
        console.log(datum.id);
    }
}
```

# プロパティの算法(Partial, Pick, Omit)
```typescript

type ManyProperty = {
    a : string;
    b : string;
    c : string;
    d : string;
    e : string;
}

//"a"しかプロパティがない型
type OnlyA = Pick<ManyProperty, 'a'>;
/* ->
type OnlyA = {
    a: string;
}
*/

//すべてのプロパティが必須ではないかもしれない型
type PatialManyProperty = Partial<ManyProperty>;
/* ->
type PatialManyPropertye = {
    a?: string | undefined;
    b?: string | undefined;
    c?: string | undefined;
    d?: string | undefined;
    e?: string | undefined;
}
*/

//すべてのプロパティが必須になった型
type RequiredManyProperty = Required<PatialManyProperty>;
/* ->
type RequiredManyProperty = {
    a: string;
    b: string;
    c: string;
    d: string;
    e: string;
}
*/

//"a"と"b"をプロパティに含まない型
type NotInclueAB = Omit<ManyProperty, 'a' | 'b'>;
/* ->
type NotInclueAB = {
    c: string;
    d: string;
    e: string;
}
*

```

# KintoneRestAPIClientを利用するときの型推論
インポートする。
```bash
npm i @kintone/rest-api-client
```

```typescript
import type { KintoneRestAPIClient } from '@kintone/rest-api-client';

//getFormFieldsの戻り値の型を取得する。
type FormResponse = Awaited<ReturnType<typeof KintoneRestAPIClient['prototype']['app']['getFormFields']>>;
/*
type FormResponse = {
    properties: Properties;
    revision: string;
}
*/

//レコード取得したときの型（あんまり意味ないけど）
type RecordResponse = Awaited<ReturnType<typeof KintoneRestAPIClient['prototype']['record']['getAllRecords']>>;

//kintoneのフォームに定義できる型(typeプロパティ)の一覧
type FieldType = FormResponse['properties'][string]['type'];

```
👇FieldTypeをVSCodeで覗いたところ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3712902/a963b43b-f31f-a8fe-2d7e-293dd0d66d69.png)

このように、stringではなく、明示的な定数で定義されるので、switch文などの展開で安全にコーディングをすすめられます。

# まとめ
TypeScriptでは型演算と型推論を利用し、処理を書いている間はほぼ型定義なしで書き進められます。

型を利用できるようになると、開発環境のインテリセンス（サジェストを表示する機能）を利用して安全で高速に開発ができます。体感で倍以上は開発速度が違うと思います。

みなさまも、よく使うやり方があったら教えてください！


---
この記事は以上です。ありがとうございました。

https://www.pulsar-works.jp/

kintoneのプラグイン開発や研修などを行っています。
お仕事のお話はこちらまで。