Skip to content

Commit 691cb33

Browse files
fix: isPropertyReadonlyInType crash on readonly array of a generic arrow function parameter (#757)
## PR Checklist - [x] Addresses an existing open issue: fixes #754 - [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/ts-api-utils/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22) - [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/ts-api-utils/blob/main/.github/CONTRIBUTING.md) were taken ## Overview The `propertySymbol` being checked in `isInConstContext` might not be a `ts.TransientSymbol` with `.links`. The test case is from typescript-eslint/typescript-eslint#11289 and fails on `main`. 💖 Co-authored-by: Kirk Waiblinger <53019676+kirkwaiblinger@users.noreply.github.com>
1 parent 4b3d274 commit 691cb33

2 files changed

Lines changed: 26 additions & 3 deletions

File tree

src/nodes/utilities.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,12 @@ export function isInConstContext(
8484
}
8585

8686
// I believe we only need to check one level deep, regardless of how deep `node` is.
87-
return isTransientSymbolLinksFlagSet(
88-
(propertySymbol as ts.TransientSymbol).links,
89-
ts.CheckFlags.Readonly,
87+
return (
88+
!!propertySymbol.links &&
89+
isTransientSymbolLinksFlagSet(
90+
(propertySymbol as ts.TransientSymbol).links,
91+
ts.CheckFlags.Readonly,
92+
)
9093
);
9194
}
9295
case ts.SyntaxKind.PrefixUnaryExpression:

src/types/utilities.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,26 @@ describe("isPropertyReadonlyInType", () => {
6565
),
6666
).toBe(false);
6767
});
68+
69+
it("does not crash when the property is inside a readonly array of a generic arrow function parameter", () => {
70+
const { sourceFile, typeChecker } = createSourceFileAndTypeChecker(`
71+
declare const factory: <T>(x: readonly T[]) => (f: (x: T) => void) => void;
72+
73+
factory([{ abc: 42 }])((x) => { });
74+
`);
75+
const node = sourceFile.statements.at(-1) as ts.ExpressionStatement;
76+
const call = node.expression as ts.CallExpression;
77+
const parameter = call.arguments[0] as ts.ArrowFunction;
78+
const type = typeChecker.getTypeAtLocation(parameter.parameters[0]);
79+
80+
expect(
81+
isPropertyReadonlyInType(
82+
type,
83+
ts.escapeLeadingUnderscores("abc"),
84+
typeChecker,
85+
),
86+
).toBe(false);
87+
});
6888
});
6989

7090
describe("symbolHasReadonlyDeclaration", () => {

0 commit comments

Comments
 (0)