Skip to content

Commit 6376273

Browse files
committed
Handle @inline on more types
Resolves #2920
1 parent 963e9c9 commit 6376273

File tree

5 files changed

+69
-21
lines changed

5 files changed

+69
-21
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ title: Changelog
66

77
### Bug Fixes
88

9+
- Inlining types can now handle more type variants, #2920.
910
- API: `toString` on types containing index signatures now behave correctly, #2917.
1011

1112
## v0.28.1 (2025-03-20)

site/tags/inline.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ TypeDoc converts references to type aliases and interfaces.
1313

1414
The `@inline` tag may be placed on type aliases and interfaces. When a type is
1515
annotated with `@inline` and the type is referenced, TypeDoc will attempt to inline
16-
the referenced type within the other type.
16+
the referenced type within the other type. TypeDoc is unable to inline some types,
17+
notably type references with type parameters in some cases, and may make incorrect
18+
guesses about the shape of types which aren't object literals, unions, intersections,
19+
or literals. Please report a bug if `@inline` incorrectly converts a type.
1720

1821
> [!note]
1922
> Use of this tag can _significantly_ increase the size of your generated

src/lib/converter/types.ts

+36-20
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,7 @@ const typeLiteralConverter: TypeConverter<ts.TypeLiteralNode> = {
653653
return new ReflectionType(reflection);
654654
},
655655
convertType(context, type) {
656+
// Don't use the third parameter here or you break convertTypeInline
656657
const symbol = type.getSymbol();
657658
const reflection = new DeclarationReflection(
658659
"__type",
@@ -764,16 +765,7 @@ const referenceConverter: TypeConverter<
764765
!node.typeArguments &&
765766
context.shouldInline(symbol, name)
766767
) {
767-
// typeLiteralConverter doesn't use the node, so we can get away with lying here.
768-
// This might not actually be safe, it appears that it is in the relatively small
769-
// amount of testing I've done with it, but I wouldn't be surprised if someone manages
770-
// to find a crash.
771-
return typeLiteralConverter.convertType(
772-
context,
773-
type,
774-
null!,
775-
undefined,
776-
);
768+
return convertTypeInlined(context, type);
777769
}
778770

779771
const ref = context.createSymbolReference(
@@ -808,16 +800,7 @@ const referenceConverter: TypeConverter<
808800
}
809801

810802
if (context.shouldInline(symbol, name)) {
811-
// typeLiteralConverter doesn't use the node, so we can get away with lying here.
812-
// This might not actually be safe, it appears that it is in the relatively small
813-
// amount of testing I've done with it, but I wouldn't be surprised if someone manages
814-
// to find a crash.
815-
return typeLiteralConverter.convertType(
816-
context,
817-
type,
818-
null!,
819-
undefined,
820-
);
803+
return convertTypeInlined(context, type);
821804
}
822805

823806
const ref = context.createSymbolReference(
@@ -1259,3 +1242,36 @@ function normalizeUnion(types: SomeType[]) {
12591242
);
12601243
}
12611244
}
1245+
1246+
function convertTypeInlined(context: Context, type: ts.Type): SomeType {
1247+
if (type.isUnion()) {
1248+
const types = type.types.map(type => convertType(context, type));
1249+
return new UnionType(types);
1250+
}
1251+
1252+
if (type.isIntersection()) {
1253+
const types = type.types.map(type => convertType(context, type));
1254+
return new IntersectionType(types);
1255+
}
1256+
1257+
if (type.isLiteral()) {
1258+
return new LiteralType(
1259+
typeof type.value === "object"
1260+
? BigInt(type.value.base10Value) * (type.value.negative ? -1n : 1n)
1261+
: type.value,
1262+
);
1263+
}
1264+
1265+
if (context.checker.isArrayType(type)) {
1266+
const elementType = convertType(context, context.checker.getTypeArguments(type as ts.TypeReference)[0]);
1267+
return new ArrayType(elementType);
1268+
}
1269+
1270+
// typeLiteralConverter doesn't use the node, so we can get away with lying here.
1271+
return typeLiteralConverter.convertType(
1272+
context,
1273+
type,
1274+
null!,
1275+
undefined,
1276+
);
1277+
}

src/test/converter2/issues/gh2920.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
type EndpointIdentifier = "main" | "test";
2+
3+
/**
4+
* @inlineType EndpointIdentifier
5+
*/
6+
export function test(endpoint: EndpointIdentifier) {}
7+
8+
type InlinedConditional<T> = T extends string ? 1 : 2;
9+
10+
/** @inlineType InlinedConditional */
11+
export type NotInlined = InlinedConditional<string>;
12+
13+
/** @inline */
14+
type StrArr = string[];
15+
16+
export type InlineArray = StrArr;

src/test/issues.c2.test.ts

+12
Original file line numberDiff line numberDiff line change
@@ -2050,4 +2050,16 @@ describe("Issue Tests", () => {
20502050
const mixed = query(project, "Foo.mixed");
20512051
equal(mixed.type?.toString(), "{ (): string; a: string; [key: string]: any }");
20522052
});
2053+
2054+
it("#2920 handles inlining union types", () => {
2055+
const project = convert();
2056+
const test = querySig(project, "test");
2057+
equal(test.parameters?.[0].type?.toString(), '"main" | "test"');
2058+
2059+
const test2 = query(project, "NotInlined");
2060+
equal(test2.type?.toString(), "InlinedConditional<string>");
2061+
2062+
const test3 = query(project, "InlineArray");
2063+
equal(test3.type?.toString(), "string[]");
2064+
});
20532065
});

0 commit comments

Comments
 (0)