Skip to content

Commit

Permalink
fix: crash in getWellKnownSymbolPropertyOfType for mapped typeof symb…
Browse files Browse the repository at this point in the history
…ol (#698)

<!-- 👋 Hi, thanks for sending a PR to ts-api-utils! 💖.
Please fill out all fields below and make sure each item is true and [x]
checked.
Otherwise we may not be able to review your PR. -->

## PR Checklist

- [x] Addresses an existing open issue: fixes
typescript-eslint/typescript-eslint#10747
- [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

As seen in
typescript-eslint/typescript-eslint#10747, we
can't assume that prop's declarations array can be found. If the type
being checked is a mapped type over the requested well-known symbol,
it'll come up as `undefined`.

💖
  • Loading branch information
JoshuaKGoldberg authored Feb 1, 2025
1 parent d0ae2cb commit e97334e
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 2 deletions.
38 changes: 37 additions & 1 deletion src/types/getters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ describe("getWellKnownSymbolPropertyOfType", () => {
const { sourceFile, typeChecker } = createSourceFileAndTypeChecker(`
declare const x: {
[Symbol.asyncIterator](): AsyncIterator<any>;
}>
};
`);

const node = (sourceFile.statements[0] as ts.VariableStatement)
Expand Down Expand Up @@ -143,4 +143,40 @@ describe("getWellKnownSymbolPropertyOfType", () => {
name: /^__@asyncIterator/,
});
});

it("returns undefined when the type maps over typeof the requested symbol", () => {
const { sourceFile, typeChecker } = createSourceFileAndTypeChecker(`
type MapsOverTypeofSymbolIterator = {
[K in typeof Symbol.iterator]: string;
};
declare const hoverStyles: MapsOverTypeofSymbolIterator;
const testObject = { ...hoverStyles };
`);

const node = (sourceFile.statements.at(-1) as ts.VariableStatement)
.declarationList.declarations[0];

const type = typeChecker.getTypeAtLocation(node);

expect(
getWellKnownSymbolPropertyOfType(type, "iterator", typeChecker),
).toBe(undefined);
});

it("returns undefined when given an error type", () => {
const { sourceFile, typeChecker } = createSourceFileAndTypeChecker(`
declare const x: invalid;
`);

const node = (sourceFile.statements[0] as ts.VariableStatement)
.declarationList.declarations[0].name;

const type = typeChecker.getTypeAtLocation(node);

expect(
getWellKnownSymbolPropertyOfType(type, "asyncIterator", typeChecker),
).toBe(undefined);
});
});
3 changes: 2 additions & 1 deletion src/types/getters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ export function getWellKnownSymbolPropertyOfType(
continue;
}

const declaration = prop.valueDeclaration ?? prop.getDeclarations()![0];
const declaration = prop.valueDeclaration ?? prop.getDeclarations()?.[0];
if (
!declaration ||
!isNamedDeclarationWithName(declaration) ||
declaration.name === undefined ||
!ts.isComputedPropertyName(declaration.name)
Expand Down

0 comments on commit e97334e

Please sign in to comment.