Skip to content

Commit ea553ca

Browse files
authored
Add docs pass (#1341)
1 parent 195a22f commit ea553ca

File tree

11 files changed

+139
-62
lines changed

11 files changed

+139
-62
lines changed

docs/scripts/update-contributors.js

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ const OPENAPI_TS_CONTRIBUTORS = [
9292
"psychedelicious",
9393
"tkrotoff",
9494
"pimveldhuisen",
95+
"asvishnyakov",
9596
]),
9697
];
9798

docs/src/content/docs/advanced.md

+75-4
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,9 @@ However, APIs are language-agnostic, and may contain a different syntax style fr
182182
183183
Instead, treat “consistency” in a more holistic sense, recognizing that preserving the API schema as-written is better than adhering to language-specific style conventions.
184184
185-
### Enable `noUncheckedIndexedAccess` in your tsconfig.json
185+
### Enable `noUncheckedIndexedAccess`
186186
187-
openapi-typescript generates a `Record` for `additionalProperties` and tries to avoid adding a `undefined` union to the index signature. However, this may result in unsafe property access in TypeScript, **unless** the compiler flag `noUncheckedIndexedAccess` is set ([docs](/advanced#enable-nouncheckedindexaccess-in-your-tsconfigjson)). If set, TypeScript will error when you try to access a property that might not be set.
187+
[Additional Properties](https://swagger.io/docs/specification/data-models/dictionaries/) (a.k.a. dictionaries) generate a type of `Record<string, T>` in TypeScript. TypeScript’s default behavior is a bit dangerous because it will confidently assert a key is there even if you haven’t checked for it. For that reason it’s **highly recommended** to enable `compilerOptions.noUncheckedIndexedAccess` ([docs](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess)) so any `additionalProperties` key will be typed as `T | undefined`.
188188
189189
### Be specific in your schema
190190
@@ -268,7 +268,7 @@ When it comes to **tuple types**, you’ll also get better results by representi
268268
<table>
269269
<thead>
270270
<tr>
271-
<td style="width:10%"></td>
271+
<td style="width:10%">&nbsp;</td>
272272
<th scope="col" style="width:40%">Schema</th>
273273
<th scope="col" style="width:40%">Generated Type</th>
274274
</tr>
@@ -352,7 +352,7 @@ prefixItems:
352352
</tbody>
353353
</table>
354354
355-
### Use `$defs` only in objects
355+
### Use `$defs` only in object types
356356
357357
<a href="https://json-schema.org/understanding-json-schema/structuring.html#defs" target="_blank" rel="noopener noreferrer">JSONSchema $defs</a> can be used to provide sub-schema definitions anywhere. However, these won’t always convert cleanly to TypeScript. For example, this works:
358358
@@ -418,3 +418,74 @@ export interface components {
418418
```
419419
420420
So be wary about where you define `$defs` as they may go missing in your final generated types. When in doubt, you can always define `$defs` at the root schema level.
421+
422+
### Use `oneOf` by itself
423+
424+
OpenAPI’s composition tools (`oneOf`/`anyOf`/`allOf`) are powerful tools for reducing the amount of code in your schema while maximizing flexibility. TypeScript unions, however, don’t provide [XOR behavior](https://en.wikipedia.org/wiki/Exclusive_or), which means they don’t map directly to `oneOf`. For that reason, it’s recommended to use `oneOf` by itself, and not combined with other composition methods or other properties. e.g.:
425+
426+
#### ❌ Bad
427+
428+
```yaml
429+
Pet:
430+
type: object
431+
properties:
432+
type:
433+
type: string
434+
enum:
435+
- cat
436+
- dog
437+
- rabbit
438+
- snake
439+
- turtle
440+
name:
441+
type: string
442+
oneOf:
443+
- $ref: "#/components/schemas/Cat"
444+
- $ref: "#/components/schemas/Dog"
445+
- $ref: "#/components/schemas/Rabbit"
446+
- $ref: "#/components/schemas/Snake"
447+
- $ref: "#/components/schemas/Turtle"
448+
```
449+
450+
This generates the following type which mixes both TypeScript unions and intersections. While this is valid TypeScript, it’s complex, and inference may not work as you intended. But the biggest offense is TypeScript can’t discriminate via the `type` property:
451+
452+
```ts
453+
Pet: ({
454+
/** @enum {string} */
455+
type?: "cat" | "dog" | "rabbit" | "snake" | "turtle";
456+
name?: string;
457+
}) & (components["schemas"]["Cat"] | components["schemas"]["Dog"] | components["schemas"]["Rabbit"] | components["schemas"]["Snake"] | components["schemas"]["Turtle"]);
458+
```
459+
460+
#### ✅ Better
461+
462+
```yaml
463+
Pet:
464+
oneOf:
465+
- $ref: "#/components/schemas/Cat"
466+
- $ref: "#/components/schemas/Dog"
467+
- $ref: "#/components/schemas/Rabbit"
468+
- $ref: "#/components/schemas/Snake"
469+
- $ref: "#/components/schemas/Turtle"
470+
PetCommonProperties:
471+
type: object
472+
properties:
473+
name:
474+
type: string
475+
Cat:
476+
allOf:
477+
- "$ref": "#/components/schemas/PetCommonProperties"
478+
type:
479+
type: string
480+
enum:
481+
- cat
482+
```
483+
484+
The resulting generated types are not only simpler; TypeScript can now discriminate using `type` (notice `Cat` has `type` with a single enum value of `"cat"`).
485+
486+
```ts
487+
Pet: components["schemas"]["Cat"] | components["schemas"]["Dog"] | components["schemas"]["Rabbit"] | components["schemas"]["Snake"] | components["schemas"]["Turtle"];
488+
Cat: { type?: "cat"; } & components["schemas"]["PetCommonProperties"];
489+
```
490+
491+
While the schema permits you to use composition in any way you like, it’s good to always take a look at the generated types and see if there’s a simpler way to express your unions & intersections. Limiting the use of `oneOf` is not the only way to do that, but often yields the greatest benefits.

docs/src/content/docs/cli.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: CLI
33
description: openapi-typescript CLI usage
44
---
55

6-
The CLI is the most common way to use openapi-typescript. The CLI can parse JSON and YAML (via <a href="https://www.npmjs.com/package/js-yaml" target="_blank" rel="noopener noreferrer">js-yaml</a>). It can also handle both local and remote schemas.
6+
The CLI is the most common way to use openapi-typescript. The CLI can parse JSON and YAML (via <a href="https://www.npmjs.com/package/js-yaml" target="_blank" rel="noopener noreferrer">js-yaml</a>). It can parse local and remote schemas (and even supports basic auth).
77

88
## Reading schemas
99

docs/src/content/docs/introduction.md

+4-6
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,16 @@ description: Quickstart
55

66
<img src="/assets/openapi-ts.svg" alt="openapi-typescript" width="200" height="40" />
77

8-
openapi-typescript generates TypeScript types from static <a href="https://spec.openapis.org/oas/latest.html" target="_blank" rel="noopener noreferrer">OpenAPI</a> schemas quickly using only Node.js. It is fast, lightweight, (almost) dependency-free, and no Java/node-gyp/running OpenAPI servers necessary.
8+
openapi-typescript turns <a href="https://spec.openapis.org/oas/latest.html" target="_blank" rel="noopener noreferrer">OpenAPI 3.0 & 3.1</a> schemas into TypeScript quickly using Node.js. No Java/node-gyp/running OpenAPI servers necessary.
99

1010
The code is <a href="https://github.com/drwpow/openapi-typescript/blob/main/packages/openapi-typescript/LICENSE" target="_blank" rel="noopener noreferrer">MIT-licensed</a > and free for use.
1111

1212
## Features
1313

1414
- ✅ Supports OpenAPI 3.0 and 3.1 (including advanced features like <a href="https://spec.openapis.org/oas/v3.1.0#discriminator-object" target="_blank" rel="noopener noreferrer">discriminators</a>)
15-
- ✅ Generate **runtime-free types** that outperform old-school codegen
15+
- ✅ Generate **runtime-free types** that outperform old school codegen
1616
- ✅ Load schemas from YAML or JSON, locally or remotely
17-
-Native Node.js code is fast and generates types within milliseconds
17+
-Generate types for even huge schemas within milliseconds
1818

1919
_Note: OpenAPI 2.x is supported with versions `5.x` and previous_
2020

@@ -30,9 +30,7 @@ This library requires the latest version of <a href="https://nodejs.org/en" targ
3030
npm i -D openapi-typescript
3131
```
3232

33-
> **Tip**
34-
>
35-
> Enabling [noUncheckedIndexedAccess](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess) in `tsconfig.json` can go along way to improve type safety ([read more](/advanced#enable-nouncheckedindexaccess-in-your-tsconfigjson))
33+
> **Highly recommended**: enable [noUncheckedIndexedAccess](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess) in your `tsconfig.json` ([docs](/advanced#enable-nouncheckedindexaccess-in-your-tsconfigjson))
3634
3735
## Basic usage
3836

docs/src/content/docs/node.md

+6-12
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,15 @@ title: Node.js API
33
description: Node.js API
44
---
55

6-
The Node API may be useful if dealing with dynamically-created schemas, or you’re using within context of a larger application. Pass in either a JSON-friendly object to load a schema from memory, or a string to load a schema from a local file or remote URL (it will load the file quickly using built-in Node methods).
6+
The Node API may be useful if dealing with dynamically-created schemas, or you’re using within context of a larger application. Pass in either a JSON-friendly object to load a schema from memory, or a string to load a schema from a local file or remote URL.
77

88
## Setup
99

1010
```bash
1111
npm i --save-dev openapi-typescript
1212
```
1313

14-
Note that the Node.js API requires [ESM support](https://nodejs.org/api/esm.html) in Node. This can be enabled by adding
15-
16-
```json
17-
"type": "module"
18-
```
19-
20-
To your `package.json`. Or it can be consumed in a `.mjs` file extension (rather than `.js` or `.cjs`)
14+
> **Recommended**: For the best experience, use Node ESM by adding `"type": "module"` to `package.json` ([docs](https://nodejs.org/api/esm.html#enabling))
2115
2216
## Usage
2317

@@ -37,7 +31,7 @@ const output = await openapiTS(localPath);
3731
const output = await openapiTS("https://myurl.com/v1/openapi.yaml");
3832
```
3933
40-
> ⚠️ Note that unlike the CLI, YAML isn’t supported in the Node.js API. You’ll need to convert it to JSON yourself using <a href="https://www.npmjs.com/package/js-yaml" target="_blank" rel="noopener noreferrer">js-yaml</a> first.
34+
> **Note**: a YAML string isn’t supported in the Node.js API (you’ll need to <a href="https://www.npmjs.com/package/js-yaml" target="_blank" rel="noopener noreferrer">convert it to JSON</a>). But loading YAML via URL is still supported in Node.js
4135
4236
## Options
4337
@@ -53,10 +47,10 @@ The Node API supports all the [CLI flags](/cli#options) in `camelCase` format, p
5347
5448
### transform / postTransform
5549
56-
Use the `transform()` and `postTransform()` options to override the default Schema Object transformer with your own. This is useful for providing non-standard modifications for specific parts of your schema.
50+
Use the `transform()` and `postTransform()` options to override the default Schema Object transformer with your own. This is useful for providing nonstandard modifications for specific parts of your schema.
5751
58-
- `transform()` runs **BEFORE** the conversion to TypeScript (you’re working with the original OpenAPI nodes)
59-
- `postTransform()` runs **AFTER** the conversion to TypeScript (you’re working with TypeScript types)
52+
- `transform()` runs **before** the conversion to TypeScript (you’re working with the original OpenAPI nodes)
53+
- `postTransform()` runs **after** the conversion to TypeScript (you’re working with TypeScript types)
6054
6155
#### Example: `Date` types
6256

docs/src/content/docs/openapi-fetch/index.md

+22-15
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,27 @@ import { paths } from "./v1"; // generated from openapi-typescript
2323

2424
const { GET, PUT } = createClient<paths>({ baseUrl: "https://myapi.dev/v1/" });
2525

26-
// Type-checked request
26+
const {
27+
data, // only present if 2XX response
28+
error, // only present if 4XX or 5XX response
29+
} = await GET("/blogposts/{post_id}", {
30+
params: {
31+
path: { post_id: "123" },
32+
},
33+
});
34+
2735
await PUT("/blogposts", {
2836
body: {
2937
title: "My New Post",
30-
// ❌ Property 'publish_date' is missing in type …
3138
},
3239
});
33-
34-
// Type-checked response
35-
const { data, error } = await GET("/blogposts/{post_id}", { params: { path: { post_id: "123" } } });
36-
console.log(data.title); // ❌ 'data' is possibly 'undefined'
37-
console.log(error.message); // ❌ 'error' is possibly 'undefined'
38-
console.log(data?.foo); // ❌ Property 'foo' does not exist on type …
3940
```
4041

41-
Notice **there are no generics, and no manual typing.** Your endpoint’s exact request & response was inferred automatically off the URL. This makes a **big difference** in the type safety of your endpoints! This eliminates all of the following:
42+
`data` and `error` are typechecked and expose their shapes to Intellisence in VS Code (and any other IDE with TypeScript support). Likewise, the request `body` will also typecheck its fields, erring if any required params are missing, or if there’s a type mismatch.
43+
44+
`GET()`, `PUT()`, `POST()`, etc. are thin wrappers around the native [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) (which you can [swap for any call](/openapi-fetch/api#create-client)).
45+
46+
Notice there are no generics, and no manual typing. Your endpoint’s request and response were inferred automatically. This is a huge improvement in the type safety of your endpoints because **every manual assertion could lead to a bug**! This eliminates all of the following:
4247

4348
- ✅ No typos in URLs or params
4449
- ✅ All parameters, request bodies, and responses are type-checked and 100% match your schema
@@ -56,6 +61,8 @@ npm i openapi-fetch
5661
npm i -D openapi-typescript
5762
```
5863

64+
> **Highly recommended**: enable [noUncheckedIndexedAccess](https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess) in your `tsconfig.json` ([docs](/advanced#enable-nouncheckedindexaccess-in-your-tsconfigjson))
65+
5966
Next, generate TypeScript types from your OpenAPI schema using openapi-typescript:
6067

6168
```bash
@@ -76,18 +83,14 @@ Lastly, be sure to **run typechecking** in your project. This can be done by add
7683

7784
And run `npm run test:ts` in your CI to catch type errors.
7885

79-
> **Tip**
80-
>
81-
> Always use `tsc --noEmit` to check for type errors! Your build tools (Vite, esbuild, webpack, etc.) won’t typecheck as accurately as the TypeScript compiler itself.
86+
> **Tip**: use `tsc --noEmit` to check for type errors rather than relying on your linter or your build command. Nothing will typecheck as accurately as the TypeScript compiler itself.
8287
8388
## Usage
8489

85-
Using **openapi-fetch** is as easy as reading your schema:
90+
The best part about using openapi-fetch over oldschool codegen is no documentation needed. openapi-fetch encourages using your existing OpenAPI documentation rather than trying to find what function to import, or what parameters that function wants:
8691

8792
![OpenAPI schema example](/assets/openapi-schema.png)
8893

89-
Here’s how you’d fetch GET `/blogposts/{post_id}` and PUT `/blogposts`:
90-
9194
```ts
9295
import createClient from "openapi-fetch";
9396
import { paths } from "./v1";
@@ -110,6 +113,10 @@ const { data, error } = await PUT("/blogposts", {
110113
});
111114
```
112115

116+
1. The HTTP method is pulled directly from `createClient()`
117+
2. You pass in your desired `path` to `GET()`, `PUT()`, etc.
118+
3. TypeScript takes over the rest and returns helpful errors for anything missing or invalid
119+
113120
### Pathname
114121

115122
The pathname of `GET()`, `PUT()`, `POST()`, etc. **must match your schema literally.** Note in the example, the URL is `/blogposts/{post_id}`. This library will replace all `path` params for you (so they can be typechecked)

0 commit comments

Comments
 (0)