From a636bd5f4848cccb8b8d1928970c9079f88724a0 Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Wed, 27 Dec 2023 11:49:07 -0700 Subject: [PATCH] Fix #2451 --- CHANGELOG.md | 1 + scripts/testcase.js | 2 ++ src/lib/converter/types.ts | 21 +++++++++++---------- src/test/converter2/issues/gh2451.ts | 15 +++++++++++++++ src/test/issues.c2.test.ts | 9 ++++++++- 5 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 src/test/converter2/issues/gh2451.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 4524b60ae..93a124933 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ ### Bug Fixes +- Fix crash when converting some complicated union/intersection types, #2451. - Navigation triangle markers should no longer display on a separate line with some font settings, #2457. - `@group` and `@category` organization is now applied later to allow inherited comments to create groups/categories, #2459. - Keyword syntax highlighting introduced in 0.25.4 was not always applied to keywords. diff --git a/scripts/testcase.js b/scripts/testcase.js index 64c28e729..b531d9529 100644 --- a/scripts/testcase.js +++ b/scripts/testcase.js @@ -57,6 +57,8 @@ async function main() { ); if (!code) { console.log("No codeblock found"); + const file = `src/test/converter2/issues/gh${issue}.ts`; + await exec(`code ${file}`); return; } diff --git a/src/lib/converter/types.ts b/src/lib/converter/types.ts index 1d5ae1e11..a54fab61c 100644 --- a/src/lib/converter/types.ts +++ b/src/lib/converter/types.ts @@ -132,6 +132,17 @@ export function convertType( return requestBugReport(context, typeOrNode); } + // TS 4.2 added this to enable better tracking of type aliases. + // We need to check it here, not just in the union checker, because typeToTypeNode + // will use the origin when serializing + if ( + typeOrNode.isUnion() && + typeOrNode.origin && + !typeOrNode.origin.isUnion() + ) { + return convertType(context, typeOrNode.origin); + } + // IgnoreErrors is important, without it, we can't assert that we will get a node. const node = context.checker.typeToTypeNode( typeOrNode, @@ -1011,11 +1022,6 @@ const typeOperatorConverter: TypeConverter = { // keyof will only show up with generic functions, otherwise it gets eagerly // resolved to a union of strings. if (node.operator === ts.SyntaxKind.KeyOfKeyword) { - // TS 4.2 added this to enable better tracking of type aliases. - if (type.isUnion() && type.origin) { - return convertType(context, type.origin); - } - // There's probably an interface for this somewhere... I couldn't find it. const targetType = (type as ts.Type & { type: ts.Type }).type; return new TypeOperatorType( @@ -1038,11 +1044,6 @@ const unionConverter: TypeConverter = { ); }, convertType(context, type) { - // TS 4.2 added this to enable better tracking of type aliases. - if (type.origin) { - return convertType(context, type.origin); - } - return new UnionType( type.types.map((type) => convertType(context, type)), ); diff --git a/src/test/converter2/issues/gh2451.ts b/src/test/converter2/issues/gh2451.ts new file mode 100644 index 000000000..aaf4af3e0 --- /dev/null +++ b/src/test/converter2/issues/gh2451.ts @@ -0,0 +1,15 @@ +export type Foo = FooA | FooB; + +export interface BaseFoo { + type: T; + + is( + type: Type, + ): this is Foo & { + type: Type; + }; +} + +export interface FooA extends BaseFoo<"A"> {} + +export interface FooB extends BaseFoo<"B"> {} diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 5ca72e39d..6a299b9a9 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -27,7 +27,7 @@ import { getConverter2Program, } from "./programs"; import { TestLogger } from "./TestLogger"; -import { getComment, getLinks, query } from "./utils"; +import { getComment, getLinks, query, querySig } from "./utils"; const base = getConverter2Base(); const app = getConverter2App(); @@ -1225,4 +1225,11 @@ describe("Issue Tests", () => { equal(boolEq.signatures![0].parameters![0].type?.toString(), "boolean"); equal(numEq.signatures![0].parameters![0].type?.toString(), "number"); }); + + it("Handles unions created due to union within intersection, #2451", () => { + const project = convert(); + + const is = querySig(project, "FooA.is"); + equal(is.type?.toString(), "this is Foo & Object"); + }); });