openapi-typescript@v7(β)でreadonly修飾子が追加されるようになった

# openapi-typescript
`openapi-typescript`はOpenAPI SpecからTypescriptの型を生成してくれるライブラリです．

https://www.npmjs.com/package/openapi-typescript

例えば下のようなOpenAPI Specから

```yaml
openapi: "3.0.3"

info:
  title: "Sample"
  version: "1.0.0"

paths:
  "/todo":
    get:
      summary: "get todo"
      responses:
        "200":
          description: "success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Todo"
components:
  schemas:
    Todo:
      type: object
      required: ["commonProp"]
      properties:
        commonProp:
          type: string
```

このような型が生成できます．

```typescript
export interface components {
  schemas: {
    Todo: {
      commonProp: string;
    };
  };
};
```

# readOnly属性(OpenAPI Spec)

OpenAPIではスキーマのプロパティに対してreadOnly属性(writeOnly)を付与することができる．これにより一つのスキーマでリクエストとレスポンスで異なるスキーマを表現できる．
例えば次のようなOpenAPI Specの場合

```yaml
openapi: "3.0.3"

info:
  title: "Sample"
  version: "1.0.0"

paths:
  "/todo":
    post:
      summary: "post todo"
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Todo"
      responses:
        "200":
          description: "success"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Todo"
components:
  schemas:
    Todo:
      type: object
      required: ["commonProp", "readProp"]
      properties:
        commonProp:
          type: string
        readProp:
          type: string
          readOnly: true
```


Postのリクエストとレスポンスに同じ`Todo`スキーマを使用しているが、生成されるドキュメントでは

- リクエスト => `{"commonProp": "string"}`
- レスポンス => `{"commonProp": "string","readProp": "string"}`

<details><summary>Redoc</summary><div>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/293802/c94554bc-f941-5960-4442-37d67c259a9f.png)

</div></details>

といった型として表現される。


# openapi-typescript@v6 の場合

先ほどの`readProp`を含むOprnAPI Specから現行のopenapi-typescript(6.7.1)による型生成を行うと以下のような出力が得られる.

```typescript
export interface components {
  schemas: {
    Todo: {
      commonProp: string;
      readProp: string;
    };
  };
};
```

OpenAPI Spec上の`readOnly`が無視された型が生成される.  
もしリクエスト用の型を作りたい場合は,

```typescript
type Todo = components["schemas"]["Todo"];
type TodoRequest = Omit<Todo, "readProp">;
/*
type TodoRequest = {
    commonProp: string;
}
*/
```

のように手動で取り除く必要があった.

- Issueは上がっているが進展は無さそう (対応するための実装は書いてある)
  
https://github.com/drwpow/openapi-typescript/issues/604

# openapi-typescript@v7 の場合

同様に先ほどのOpenAPI Specを openapi-typescriptの`7.0.0-next.5`で型生成を行うと次のような型が生成される.

```typescript
export interface components {
    schemas: {
        Todo: {
            commonProp: string;
            readonly readProp: string;
        };
    };
};
```

このようにOpenAPI Spec上で`readOnly`属性を付けたプロパティにはTypescript上でも`readonly`修飾子が設定されている.

これで先ほどのTodoRequestを作成したい場合は[`readonly`を取り除くようなType](#readonlyプロパティ除外する型)を書いて

```typescript
type Todo = components["schemas"]["Todo"];
type readonlyプロパティ除外する型<T> = ... 
type TodoRequest = readonlyプロパティ除外する型<Todo>
```

のようにすれば良い.

# まとめ

`readonly` がついて嬉しい！（ただし, `writeOnly`は非対応）

openapi-typescript@v7ではRedoclyによるスキーマ検証や,EnumをトップレベルにUnion型として生成する等の機能が含まれています.

https://github.com/drwpow/openapi-typescript/issues/1368#issue-1929252332

# (参考) readonlyプロパティ除外する型

任意のobject型からreadonly修飾子のついたプロパティを除外する方法は次のような方法がある.


https://stackoverflow.com/questions/49579094/typescript-conditional-types-filter-out-readonly-properties-pick-only-requir/49579497#49579497

こちらについてはまた別の機会に記事にするかもしれません.