Skip to content

Commit

Permalink
fix: Wrap nested readonly members (#1320)
Browse files Browse the repository at this point in the history
  • Loading branch information
duncanbeevers committed Aug 22, 2023
1 parent f6bfdd7 commit 3cf78b9
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/curly-mice-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"openapi-typescript": patch
---

Wrap nested readonly types in parentheses, allowing for nested immutable arrays
6 changes: 3 additions & 3 deletions packages/openapi-typescript/examples/digital-ocean-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5522,10 +5522,10 @@ export interface external {
* "doadmin"
* ]
*/
db_names?: readonly string[] | null;
db_names?: (readonly string[]) | null;
connection?: external["resources/databases/models/database_connection.yml"];
private_connection?: external["resources/databases/models/database_connection.yml"];
users?: readonly external["resources/databases/models/database_user.yml"][] | null;
users?: (readonly external["resources/databases/models/database_user.yml"][]) | null;
maintenance_window?: external["resources/databases/models/database_maintenance_window.yml"];
/**
* Format: uuid
Expand Down Expand Up @@ -15392,7 +15392,7 @@ export interface external {
* @description An array containing the IDs of the Droplets the volume is attached to. Note that at this time, a volume can only be attached to a single Droplet.
* @example []
*/
droplet_ids?: readonly number[] | null;
droplet_ids?: (readonly number[]) | null;
/**
* @description A human-readable name for the block storage volume. Must be lowercase and be composed only of numbers, letters and "-", up to a limit of 64 characters. The name must begin with a letter.
* @example example
Expand Down
14 changes: 7 additions & 7 deletions packages/openapi-typescript/examples/github-api-next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10744,7 +10744,7 @@ export interface components {
/** @description The package version that resolve the vulnerability. */
first_patched_version: string | null;
/** @description The functions in the package that are affected by the vulnerability. */
vulnerable_functions: readonly string[] | null;
vulnerable_functions: (readonly string[]) | null;
})[]) | null;
cvss: ({
/** @description The CVSS vector. */
Expand All @@ -10758,10 +10758,10 @@ export interface components {
/** @description The name of the CWE. */
name: string;
}[] | null;
credits: readonly {
credits: (readonly {
user: components["schemas"]["simple-user"];
type: components["schemas"]["security-advisory-credit-types"];
}[] | null;
}[]) | null;
};
/**
* Basic Error
Expand Down Expand Up @@ -14434,20 +14434,20 @@ export interface components {
/** @description The CVSS score. */
score: number | null;
}) | null;
cwes: readonly {
cwes: (readonly {
/** @description The Common Weakness Enumeration (CWE) identifier. */
cwe_id: string;
/** @description The name of the CWE. */
name: string;
}[] | null;
}[]) | null;
/** @description A list of only the CWE IDs. */
cwe_ids: string[] | null;
credits: {
/** @description The username of the user credited. */
login?: string;
type?: components["schemas"]["security-advisory-credit-types"];
}[] | null;
credits_detailed: readonly components["schemas"]["repository-advisory-credit"][] | null;
credits_detailed: (readonly components["schemas"]["repository-advisory-credit"][]) | null;
/** @description A list of users that collaborate on the advisory. */
collaborating_users: components["schemas"]["simple-user"][] | null;
/** @description A list of teams that collaborate on the advisory. */
Expand Down Expand Up @@ -16518,7 +16518,7 @@ export interface components {
*/
analyses_url?: string | null;
/** @description Any errors that ocurred during processing of the delivery. */
errors?: readonly string[] | null;
errors?: (readonly string[]) | null;
};
/**
* CODEOWNERS errors
Expand Down
14 changes: 7 additions & 7 deletions packages/openapi-typescript/examples/github-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8251,7 +8251,7 @@ export interface components {
/** @description The package version that resolve the vulnerability. */
first_patched_version: string | null;
/** @description The functions in the package that are affected by the vulnerability. */
vulnerable_functions: readonly string[] | null;
vulnerable_functions: (readonly string[]) | null;
})[]) | null;
cvss: ({
/** @description The CVSS vector. */
Expand All @@ -8265,10 +8265,10 @@ export interface components {
/** @description The name of the CWE. */
name: string;
}[] | null;
credits: readonly {
credits: (readonly {
user: components["schemas"]["simple-user"];
type: components["schemas"]["security-advisory-credit-types"];
}[] | null;
}[]) | null;
};
/**
* Basic Error
Expand Down Expand Up @@ -13572,20 +13572,20 @@ export interface components {
/** @description The CVSS score. */
score: number | null;
}) | null;
cwes: readonly {
cwes: (readonly {
/** @description The Common Weakness Enumeration (CWE) identifier. */
cwe_id: string;
/** @description The name of the CWE. */
name: string;
}[] | null;
}[]) | null;
/** @description A list of only the CWE IDs. */
cwe_ids: string[] | null;
credits: {
/** @description The username of the user credited. */
login?: string;
type?: components["schemas"]["security-advisory-credit-types"];
}[] | null;
credits_detailed: readonly components["schemas"]["repository-advisory-credit"][] | null;
credits_detailed: (readonly components["schemas"]["repository-advisory-credit"][]) | null;
/** @description A list of users that collaborate on the advisory. */
collaborating_users: components["schemas"]["simple-user"][] | null;
/** @description A list of teams that collaborate on the advisory. */
Expand Down Expand Up @@ -17232,7 +17232,7 @@ export interface components {
*/
analyses_url?: string | null;
/** @description Any errors that ocurred during processing of the delivery. */
errors?: readonly string[] | null;
errors?: (readonly string[]) | null;
};
/**
* CODEOWNERS errors
Expand Down
5 changes: 3 additions & 2 deletions packages/openapi-typescript/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const TILDE_RE = /~/g;
const FS_RE = /\//g;
export const TS_INDEX_RE = /\[("(\\"|[^"])+"|'(\\'|[^'])+')]/g; // splits apart TS indexes (and allows for escaped quotes)
const TS_UNION_INTERSECTION_RE = /[&|]/;
const TS_READONLY_RE = /^readonly\s+/;
const JS_OBJ_KEY = /^(\d+|[A-Za-z_$][A-Za-z0-9_$]*)$/;

/** Walk through any JSON-serializable object */
Expand Down Expand Up @@ -168,9 +169,9 @@ export function encodeRef(ref: string): string {
return ref.replace(TILDE_RE, "~0").replace(FS_RE, "~1");
}

/** if the type has & or | we should parenthesise it for safety */
/** add parenthesis around union, intersection (| and &) and readonly types */
function parenthesise(type: string) {
return TS_UNION_INTERSECTION_RE.test(type) ? `(${type})` : type;
return TS_UNION_INTERSECTION_RE.test(type) || TS_READONLY_RE.test(type) ? `(${type})` : type;
}

/** T[] */
Expand Down
22 changes: 20 additions & 2 deletions packages/openapi-typescript/test/schema-object.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -790,11 +790,29 @@ describe("Schema Object", () => {
ctx: { ...options.ctx, immutableTypes: true },
});
expect(generated).toBe(`{
readonly array?: readonly {
readonly array?: (readonly {
[key: string]: unknown;
}[] | null;
}[]) | null;
}`);
});

test("readonly arrays", () => {
const schema: SchemaObject = {
type: "array",
items: {
type: "array",
items: {
type: "string",
},
},
};

const generated = transformSchemaObject(schema, {
...options,
ctx: { ...options.ctx, immutableTypes: true },
});
expect(generated).toBe(`readonly (readonly string[])[]`);
});
});
});

Expand Down

0 comments on commit 3cf78b9

Please sign in to comment.