Skip to content

Commit 12cc079

Browse files
authored
Fix immutable types (#1443)
1 parent 9663b74 commit 12cc079

12 files changed

+243300
-66
lines changed

packages/openapi-typescript/examples/github-api-export-type-immutable.ts

+121,611
Large diffs are not rendered by default.

packages/openapi-typescript/examples/github-api-immutable.ts

+121,611
Large diffs are not rendered by default.

packages/openapi-typescript/src/transform/index.ts

+4-16
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,7 @@ export default function transformSchema(schema: OpenAPI3, ctx: GlobalContext) {
2828

2929
for (const root of Object.keys(transformers) as SchemaTransforms[]) {
3030
const emptyObj = ts.factory.createTypeAliasDeclaration(
31-
/* modifiers */ tsModifiers({
32-
export: true,
33-
readonly: ctx.immutable,
34-
}),
31+
/* modifiers */ tsModifiers({ export: true }),
3532
/* name */ root,
3633
/* typeParameters */ undefined,
3734
/* type */ tsRecord(STRING, NEVER),
@@ -44,19 +41,13 @@ export default function transformSchema(schema: OpenAPI3, ctx: GlobalContext) {
4441
type.push(
4542
ctx.exportType
4643
? ts.factory.createTypeAliasDeclaration(
47-
/* modifiers */ tsModifiers({
48-
export: true,
49-
readonly: ctx.immutable,
50-
}),
44+
/* modifiers */ tsModifiers({ export: true }),
5145
/* name */ root,
5246
/* typeParameters */ undefined,
5347
/* type */ subType,
5448
)
5549
: ts.factory.createInterfaceDeclaration(
56-
/* modifiers */ tsModifiers({
57-
export: true,
58-
readonly: ctx.immutable,
59-
}),
50+
/* modifiers */ tsModifiers({ export: true }),
6051
/* name */ root,
6152
/* typeParameters */ undefined,
6253
/* heritageClauses */ undefined,
@@ -89,10 +80,7 @@ export default function transformSchema(schema: OpenAPI3, ctx: GlobalContext) {
8980
// if no operations created, inject empty operations type
9081
type.push(
9182
ts.factory.createTypeAliasDeclaration(
92-
/* modifiers */ tsModifiers({
93-
export: true,
94-
readonly: ctx.immutable,
95-
}),
83+
/* modifiers */ tsModifiers({ export: true }),
9684
/* name */ "operations",
9785
/* typeParameters */ undefined,
9886
/* type */ tsRecord(STRING, NEVER),

packages/openapi-typescript/src/transform/operation-object.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export default function transformOperationObject(
5959
} else {
6060
type.push(
6161
ts.factory.createPropertySignature(
62-
/* modifiers */ undefined,
62+
/* modifiers */ tsModifiers({ readonly: options.ctx.immutable }),
6363
/* name */ tsPropertyIndex("requestBody"),
6464
/* questionToken */ QUESTION_TOKEN,
6565
/* type */ NEVER,
@@ -70,7 +70,7 @@ export default function transformOperationObject(
7070
// responses
7171
type.push(
7272
ts.factory.createPropertySignature(
73-
/* modifiers */ undefined,
73+
/* modifiers */ tsModifiers({ readonly: options.ctx.immutable }),
7474
/* name */ tsPropertyIndex("responses"),
7575
/* questionToken */ undefined,
7676
/* type */ transformResponsesObject(
@@ -99,7 +99,7 @@ export function injectOperationObject(
9999
operations = ts.factory.createInterfaceDeclaration(
100100
/* modifiers */ tsModifiers({
101101
export: true,
102-
readonly: options.ctx.immutable,
102+
// important: do NOT make this immutable
103103
}),
104104
/* name */ ts.factory.createIdentifier("operations"),
105105
/* typeParameters */ undefined,

packages/openapi-typescript/src/transform/parameters-array.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
QUESTION_TOKEN,
55
addJSDocComment,
66
oapiRef,
7+
tsModifiers,
78
tsPropertyIndex,
89
} from "../lib/ts.js";
910
import { createRef } from "../lib/utils.js";
@@ -66,7 +67,7 @@ export function transformParametersArray(
6667
]),
6768
});
6869
const property = ts.factory.createPropertySignature(
69-
/* modifiers */ undefined,
70+
/* modifiers */ tsModifiers({ readonly: options.ctx.immutable }),
7071
/* name */ tsPropertyIndex(resolved?.name),
7172
/* questionToken */ optional,
7273
/* type */ subType,
@@ -77,7 +78,7 @@ export function transformParametersArray(
7778
const allOptional = paramLocType.every((node) => !!node.questionToken);
7879
paramType.push(
7980
ts.factory.createPropertySignature(
80-
/* modifiers */ undefined,
81+
/* modifiers */ tsModifiers({ readonly: options.ctx.immutable }),
8182
/* name */ tsPropertyIndex(paramIn),
8283
/* questionToken */ allOptional || !paramLocType.length
8384
? QUESTION_TOKEN
@@ -90,7 +91,7 @@ export function transformParametersArray(
9091
}
9192
type.push(
9293
ts.factory.createPropertySignature(
93-
/* modifiers */ undefined,
94+
/* modifiers */ tsModifiers({ readonly: options.ctx.immutable }),
9495
/* name */ tsPropertyIndex("parameters"),
9596
/* questionToken */ !paramType.length ? QUESTION_TOKEN : undefined,
9697
/* type */ paramType.length

packages/openapi-typescript/src/transform/path-item-object.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
QUESTION_TOKEN,
55
addJSDocComment,
66
oapiRef,
7+
tsModifiers,
78
tsPropertyIndex,
89
} from "../lib/ts.js";
910
import { createRef } from "../lib/utils.js";
@@ -69,7 +70,7 @@ export default function transformPathItemObject(
6970
) {
7071
type.push(
7172
ts.factory.createPropertySignature(
72-
/* modifiers */ undefined,
73+
/* modifiers */ tsModifiers({ readonly: options.ctx.immutable }),
7374
/* name */ tsPropertyIndex(method),
7475
/* questionToken */ QUESTION_TOKEN,
7576
/* type */ NEVER,
@@ -116,7 +117,7 @@ export default function transformPathItemObject(
116117
);
117118
}
118119
const property = ts.factory.createPropertySignature(
119-
/* modifiers */ undefined,
120+
/* modifiers */ tsModifiers({ readonly: options.ctx.immutable }),
120121
/* name */ tsPropertyIndex(method),
121122
/* questionToken */ undefined,
122123
/* type */ operationType,

packages/openapi-typescript/src/transform/paths-object.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
addJSDocComment,
44
oapiRef,
55
stringToAST,
6+
tsModifiers,
67
tsPropertyIndex,
78
} from "../lib/ts.js";
89
import { createRef, debug, getEntries } from "../lib/utils.js";
@@ -37,7 +38,7 @@ export default function transformPathsObject(
3738
// handle $ref
3839
if ("$ref" in pathItemObject) {
3940
const property = ts.factory.createPropertySignature(
40-
/* modifiers */ undefined,
41+
/* modifiers */ tsModifiers({ readonly: ctx.immutable }),
4142
/* name */ tsPropertyIndex(url),
4243
/* questionToken */ undefined,
4344
/* type */ oapiRef(pathItemObject.$ref),
@@ -74,7 +75,7 @@ export default function transformPathsObject(
7475
if (pathType) {
7576
type.push(
7677
ts.factory.createIndexSignature(
77-
/* modifiers */ undefined,
78+
/* modifiers */ tsModifiers({ readonly: ctx.immutable }),
7879
/* parameters */ [
7980
ts.factory.createParameterDeclaration(
8081
/* modifiers */ undefined,
@@ -96,7 +97,7 @@ export default function transformPathsObject(
9697

9798
type.push(
9899
ts.factory.createPropertySignature(
99-
/* modifiers */ undefined,
100+
/* modifiers */ tsModifiers({ readonly: ctx.immutable }),
100101
/* name */ tsPropertyIndex(url),
101102
/* questionToken */ undefined,
102103
/* type */ pathItemType,

packages/openapi-typescript/src/transform/schema-object.ts

+2-10
Original file line numberDiff line numberDiff line change
@@ -299,12 +299,6 @@ function transformSchemaObjectCore(
299299
// standard array type
300300
else if (schemaObject.items) {
301301
itemType = transformSchemaObject(schemaObject.items, options);
302-
if (options.ctx.immutable) {
303-
itemType = ts.factory.createTypeOperatorNode(
304-
ts.SyntaxKind.ReadonlyKeyword,
305-
itemType,
306-
);
307-
}
308302
}
309303

310304
const min: number =
@@ -499,7 +493,7 @@ function transformSchemaObjectCore(
499493
const defKeys: ts.TypeElement[] = [];
500494
for (const [k, v] of Object.entries(schemaObject.$defs)) {
501495
const property = ts.factory.createPropertySignature(
502-
/* modifiers */ tsModifiers({
496+
/* modifiers */ tsModifiers({
503497
readonly:
504498
options.ctx.immutable || ("readonly" in v && !!v.readOnly),
505499
}),
@@ -515,9 +509,7 @@ function transformSchemaObjectCore(
515509
}
516510
coreObjectType.push(
517511
ts.factory.createPropertySignature(
518-
/* modifiers */ tsModifiers({
519-
readonly: options.ctx.immutable,
520-
}),
512+
/* modifiers */ undefined,
521513
/* name */ tsPropertyIndex("$defs"),
522514
/* questionToken */ undefined,
523515
/* type */ ts.factory.createTypeLiteralNode(defKeys),

packages/openapi-typescript/test/cli.test.ts

+22-6
Original file line numberDiff line numberDiff line change
@@ -14,39 +14,55 @@ describe("CLI", () => {
1414
[
1515
"snapshot > GitHub API",
1616
{
17-
given: "./examples/github-api.yaml",
17+
given: ["./examples/github-api.yaml"],
1818
want: new URL("./examples/github-api.ts", root),
1919
ci: { timeout: TIMEOUT },
2020
},
2121
],
22+
[
23+
"snapshot > GitHub API (immutable)",
24+
{
25+
given: ["./examples/github-api.yaml", "--immutable"],
26+
want: new URL("./examples/github-api-immutable.ts", root),
27+
ci: { timeout: TIMEOUT },
28+
},
29+
],
30+
[
31+
"snapshot > GitHub API (types + immutable)",
32+
{
33+
given: ["./examples/github-api.yaml", "--immutable", "--export-type"],
34+
want: new URL("./examples/github-api-export-type-immutable.ts", root),
35+
ci: { timeout: TIMEOUT },
36+
},
37+
],
2238
[
2339
"snapshot > GitHub API (next)",
2440
{
25-
given: "./examples/github-api-next.yaml",
41+
given: ["./examples/github-api-next.yaml"],
2642
want: new URL("./examples/github-api-next.ts", root),
2743
ci: { timeout: TIMEOUT },
2844
},
2945
],
3046
[
3147
"snapshot > Octokit GHES 3.6 Diff to API",
3248
{
33-
given: "./examples/octokit-ghes-3.6-diff-to-api.json",
49+
given: ["./examples/octokit-ghes-3.6-diff-to-api.json"],
3450
want: new URL("./examples/octokit-ghes-3.6-diff-to-api.ts", root),
3551
ci: { timeout: TIMEOUT },
3652
},
3753
],
3854
[
3955
"snapshot > Stripe API",
4056
{
41-
given: "./examples/stripe-api.yaml",
57+
given: ["./examples/stripe-api.yaml"],
4258
want: new URL("./examples/stripe-api.ts", root),
4359
ci: { timeout: TIMEOUT },
4460
},
4561
],
4662
[
4763
"snapshot > DigitalOcean",
4864
{
49-
given: "./examples/digital-ocean-api/DigitalOcean-public.v2.yaml",
65+
given: ["./examples/digital-ocean-api/DigitalOcean-public.v2.yaml"],
5066
want: new URL("./examples/digital-ocean-api.ts", root),
5167
ci: { timeout: TIMEOUT },
5268
},
@@ -57,7 +73,7 @@ describe("CLI", () => {
5773
test.skipIf(ci?.skipIf)(
5874
testName,
5975
async () => {
60-
const { stdout } = await execa(cmd, [given], { cwd });
76+
const { stdout } = await execa(cmd, given, { cwd });
6177
if (want instanceof URL) {
6278
expect(stdout).toMatchFileSnapshot(fileURLToPath(want));
6379
} else {

packages/openapi-typescript/test/transform/components-object.test.ts

+19-19
Original file line numberDiff line numberDiff line change
@@ -383,29 +383,29 @@ describe("transformComponentsObject", () => {
383383
};
384384
pathItems: {
385385
readonly UploadUser: {
386-
parameters: {
387-
query?: never;
388-
header?: never;
389-
path?: never;
390-
cookie?: never;
386+
readonly parameters: {
387+
readonly query?: never;
388+
readonly header?: never;
389+
readonly path?: never;
390+
readonly cookie?: never;
391391
};
392-
get: {
393-
parameters: {
394-
query?: never;
395-
header?: never;
396-
path?: never;
397-
cookie?: never;
392+
readonly get: {
393+
readonly parameters: {
394+
readonly query?: never;
395+
readonly header?: never;
396+
readonly path?: never;
397+
readonly cookie?: never;
398398
};
399399
readonly requestBody?: components["requestBodies"]["UploadUser"];
400-
responses: never;
400+
readonly responses: never;
401401
};
402-
put?: never;
403-
post?: never;
404-
delete?: never;
405-
options?: never;
406-
head?: never;
407-
patch?: never;
408-
trace?: never;
402+
readonly put?: never;
403+
readonly post?: never;
404+
readonly delete?: never;
405+
readonly options?: never;
406+
readonly head?: never;
407+
readonly patch?: never;
408+
readonly trace?: never;
409409
};
410410
};
411411
}`,

packages/openapi-typescript/test/transform/schema-object/array.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ describe("transformSchemaObject > array", () => {
157157
type: "array",
158158
items: { type: "array", items: { type: "string" } },
159159
},
160-
want: `(readonly (readonly string)[])[]`,
160+
want: `string[][]`,
161161
options: {
162162
...DEFAULT_OPTIONS,
163163
ctx: { ...DEFAULT_OPTIONS.ctx, immutable: true },

packages/openapi-typescript/test/transform/schema-object/object.test.ts

+16-3
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,20 @@ describe("transformSchemaObject > object", () => {
279279
},
280280
],
281281
[
282-
"options > immutable: true",
282+
"options > immutable (string)",
283+
{
284+
given: {
285+
type: "string",
286+
},
287+
want: `string`,
288+
options: {
289+
...DEFAULT_OPTIONS,
290+
ctx: { ...DEFAULT_OPTIONS.ctx, immutable: true },
291+
},
292+
},
293+
],
294+
[
295+
"options > immutable (array)",
283296
{
284297
given: {
285298
type: "object",
@@ -295,9 +308,9 @@ describe("transformSchemaObject > object", () => {
295308
},
296309
},
297310
want: `{
298-
readonly array?: (readonly {
311+
readonly array?: {
299312
readonly [key: string]: unknown;
300-
})[] | null;
313+
}[] | null;
301314
}`,
302315
options: {
303316
...DEFAULT_OPTIONS,

0 commit comments

Comments
 (0)