diff --git a/src/nodes/utilities.ts b/src/nodes/utilities.ts index 2f111886..12b5dc0b 100644 --- a/src/nodes/utilities.ts +++ b/src/nodes/utilities.ts @@ -84,9 +84,12 @@ export function isInConstContext( } // I believe we only need to check one level deep, regardless of how deep `node` is. - return isTransientSymbolLinksFlagSet( - (propertySymbol as ts.TransientSymbol).links, - ts.CheckFlags.Readonly, + return ( + !!propertySymbol.links && + isTransientSymbolLinksFlagSet( + (propertySymbol as ts.TransientSymbol).links, + ts.CheckFlags.Readonly, + ) ); } case ts.SyntaxKind.PrefixUnaryExpression: diff --git a/src/types/utilities.test.ts b/src/types/utilities.test.ts index 6f7c69e4..7647b768 100644 --- a/src/types/utilities.test.ts +++ b/src/types/utilities.test.ts @@ -65,6 +65,26 @@ describe("isPropertyReadonlyInType", () => { ), ).toBe(false); }); + + it("does not crash when the property is inside a readonly array of a generic arrow function parameter", () => { + const { sourceFile, typeChecker } = createSourceFileAndTypeChecker(` + declare const factory: (x: readonly T[]) => (f: (x: T) => void) => void; + + factory([{ abc: 42 }])((x) => { }); + `); + const node = sourceFile.statements.at(-1) as ts.ExpressionStatement; + const call = node.expression as ts.CallExpression; + const parameter = call.arguments[0] as ts.ArrowFunction; + const type = typeChecker.getTypeAtLocation(parameter.parameters[0]); + + expect( + isPropertyReadonlyInType( + type, + ts.escapeLeadingUnderscores("abc"), + typeChecker, + ), + ).toBe(false); + }); }); describe("symbolHasReadonlyDeclaration", () => {