-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Open
Description
π Search Terms
Maximum call stack size exceeded in isTypeAssignableTo when using recursive template literal types in a generic context
π Version & Regression Information
- This changed between versions 5.9.3
β― Playground Link
https://codesandbox.io/p/devbox/7ctt7z?file=%2Findex.js%3A40%2C9
π» Code
import ts from "typescript";
const code = `
type A<T extends string> = T extends \`\${infer F}\${infer R}\`
? \`\${F}b\${A<R>}\`
: "";
type B<T extends string> = T extends \`\${infer F}\${infer R}\`
? \`\${F}b\${B<R>}\`
: "";
type C<T extends string> = A<T> | B<T>;
`;
const fileName = "test.ts";
const sourceFile = ts.createSourceFile(
fileName,
code,
ts.ScriptTarget.Latest,
true
);
const host = ts.createCompilerHost({});
host.getSourceFile = (name) => (name === fileName ? sourceFile : undefined);
const program = ts.createProgram([fileName], {}, host);
const checker = program.getTypeChecker();
const types = {};
ts.forEachChild(sourceFile, (node) => {
if (ts.isTypeAliasDeclaration(node)) {
types[node.name.escapedText] = checker.getTypeAtLocation(node);
}
});
const typeA = types["A"];
const typeB = types["B"];
// works
console.log("isTypeAssignableTo(A, B):", checker.isTypeAssignableTo(typeA, typeB));
ts.forEachChildRecursively(sourceFile, (node) => {
if (ts.isTypeReferenceNode(node) && ts.isUnionTypeNode(node.parent)) {
types[node.typeName.escapedText] = checker.getTypeAtLocation(node);
}
});
const typeAInsideGeneric = types["A"];
const typeBInsideGeneric = types["B"];
// Maximum call stack size exceeded
console.log("isTypeAssignableTo(A, B):", checker.isTypeAssignableTo(typeAInsideGeneric, typeBInsideGeneric));π Actual behavior
Maximum call stack
π Expected behavior
Not occur maximum call stack
Additional information about the issue
Metadata
Metadata
Assignees
Labels
No labels