Repository navigation
Description
Bug Report
Similar to #47041.
π Search Terms
- TypeChecker
- getTypeAtLocation
- flags
- undefined
π Version & Regression Information
No idea, probably not applicable.
β― Playground Link
I can't link to the playground because this issue is about an internal class that is exposed.
Problem
The problem is that I get a run-time error when using the TypeChecker.getTypeAtLocation method in certain contexts.
This is because the signature for the method is wrong. It allows you to pass in a ts.VariableDeclaration, which is a bug, because in some cases doing that will throw a run-time error. For the exact error, see the subsequent section.
π» Code
Consider the following TypeScript code:
const myArray = [1];
const [firstElement] = myArray;When we iterate over the AST for this code, and we get the types of every variable declaration, we get a runtime error when calling the getTypeAtLocation function:
// Create an ESLint plugin
create(context) {
const parserServices = util.getParserServices(context);
const typeChecker = parserServices.program.getTypeChecker();
const sourceFiles = parserServices.program.getSourceFiles();
for (const sourceFile of sourceFiles) {
// We skip over all files except for the one containing the Jest test
// that triggers the buggy runtime error
if (!sourceFile.getText().includes("myArray")) {
continue;
}
for (const statement of sourceFile.statements) {
const variableStatement = statement as ts.VariableStatement;
for (const declaration of variableStatement.declarationList.declarations) {
console.log("Iterating over:", declaration.getText());
const tsType = typeChecker.getTypeAtLocation(declaration); // CRASH HERE
}
}
}
return {};
},The exact runtime error is as follows:
TypeError: Cannot read properties of undefined (reading 'flags')
at Object.getCheckFlags (../../node_modules/typescript/lib/typescript.js:19286:23)
at getTypeOfSymbol (../../node_modules/typescript/lib/typescript.js:55760:33)
at getTypeOfNode (../../node_modules/typescript/lib/typescript.js:84915:24)
at Object.getTypeAtLocation (../../node_modules/typescript/lib/typescript.js:47415:31)
at create (src/rules/strict-enums.ts:65:38)
at create (../utils/dist/eslint-utils/RuleCreator.js:48:20)
at Object.create (../../node_modules/eslint/lib/rule-tester/rule-tester.js:533:73)
at createRuleListeners (../../node_modules/eslint/lib/linter/linter.js:887:21)
at ../../node_modules/eslint/lib/linter/linter.js:1069:31
at Array.forEach (<anonymous>)
Solution
This code can be fixed by changing the following line:
const tsType = typeChecker.getTypeAtLocation(declaration); // "declaration" is of type "ts.Declaration"To:
const tsType = typeChecker.getTypeAtLocation(declaration.name); // "name" is of type "ts.BindingName"In other words, the problem is that you are "supposed" to pass it a variable of type "ts.BindingName" instead of type "ts.Declaration".
But this is not represented in the function type signature. (Normally, when you pass a wrong variable into a function, the TypeScript compiler tells you about it.)
When I asked Nathan Sanders if it was feasible to change the signature, he said:
maybe not impossible for this particular one, but definitely difficult, and maaaybe impossible
Thus, I think that there should be a run-time check added to the source code for the getTypeAtLocation function that checks to see if symbol is undefined, and if it is, returns an "empty" type, or something along those lines.
Adding this check would likely fix #47041 automatically.
Activity