Skip to content

Commit d7ff565

Browse files
authored
Fixed inlay hints for inferred type predicates (#59441)
1 parent 6864825 commit d7ff565

5 files changed

+92
-1
lines changed

src/services/inlayHints.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ import {
119119
TupleTypeReference,
120120
Type,
121121
TypeFlags,
122+
TypePredicate,
122123
unescapeLeadingUnderscores,
123124
UserPreferences,
124125
usingSingleLineStringWriter,
@@ -405,6 +406,16 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
405406
return;
406407
}
407408

409+
const typePredicate = checker.getTypePredicateOfSignature(signature);
410+
411+
if (typePredicate?.type) {
412+
const hintParts = typePredicateToInlayHintParts(typePredicate);
413+
if (hintParts) {
414+
addTypeHints(hintParts, getTypeAnnotationPosition(decl));
415+
return;
416+
}
417+
}
418+
408419
const returnType = checker.getReturnTypeOfSignature(signature);
409420
if (isModuleReferenceType(returnType)) {
410421
return;
@@ -474,17 +485,42 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
474485
});
475486
}
476487

488+
function printTypePredicateInSingleLine(typePredicate: TypePredicate) {
489+
const flags = NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.AllowUniqueESSymbolType | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope;
490+
const printer = createPrinterWithRemoveComments();
491+
492+
return usingSingleLineStringWriter(writer => {
493+
const typePredicateNode = checker.typePredicateToTypePredicateNode(typePredicate, /*enclosingDeclaration*/ undefined, flags);
494+
Debug.assertIsDefined(typePredicateNode, "should always get typePredicateNode");
495+
printer.writeNode(EmitHint.Unspecified, typePredicateNode, /*sourceFile*/ file, writer);
496+
});
497+
}
498+
477499
function typeToInlayHintParts(type: Type): InlayHintDisplayPart[] | string {
478500
if (!shouldUseInteractiveInlayHints(preferences)) {
479501
return printTypeInSingleLine(type);
480502
}
481503

482504
const flags = NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.AllowUniqueESSymbolType | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope;
483505
const typeNode = checker.typeToTypeNode(type, /*enclosingDeclaration*/ undefined, flags);
506+
Debug.assertIsDefined(typeNode, "should always get typeNode");
507+
return getInlayHintDisplayParts(typeNode);
508+
}
509+
510+
function typePredicateToInlayHintParts(typePredicate: TypePredicate): InlayHintDisplayPart[] | string {
511+
if (!shouldUseInteractiveInlayHints(preferences)) {
512+
return printTypePredicateInSingleLine(typePredicate);
513+
}
514+
515+
const flags = NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.AllowUniqueESSymbolType | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope;
516+
const typeNode = checker.typePredicateToTypePredicateNode(typePredicate, /*enclosingDeclaration*/ undefined, flags);
484517
Debug.assertIsDefined(typeNode, "should always get typenode");
518+
return getInlayHintDisplayParts(typeNode);
519+
}
485520

521+
function getInlayHintDisplayParts(node: Node) {
486522
const parts: InlayHintDisplayPart[] = [];
487-
visitForDisplayParts(typeNode);
523+
visitForDisplayParts(node);
488524
return parts;
489525

490526
function visitForDisplayParts(node: Node) {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// === Inlay Hints ===
2+
function test(x: unknown) {
3+
^
4+
{
5+
"text": ": x is number",
6+
"position": 25,
7+
"kind": "Type",
8+
"whitespaceBefore": true
9+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// === Inlay Hints ===
2+
function test(x: unknown) {
3+
^
4+
{
5+
"text": "",
6+
"displayParts": [
7+
{
8+
"text": ": "
9+
},
10+
{
11+
"text": "x"
12+
},
13+
{
14+
"text": " is "
15+
},
16+
{
17+
"text": "number"
18+
}
19+
],
20+
"position": 25,
21+
"kind": "Type",
22+
"whitespaceBefore": true
23+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @strict: true
4+
5+
//// function test(x: unknown) {
6+
//// return typeof x === 'number';
7+
//// }
8+
9+
verify.baselineInlayHints(undefined, {
10+
includeInlayFunctionLikeReturnTypeHints: true,
11+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @strict: true
4+
5+
//// function test(x: unknown) {
6+
//// return typeof x === 'number';
7+
//// }
8+
9+
verify.baselineInlayHints(undefined, {
10+
interactiveInlayHints: true,
11+
includeInlayFunctionLikeReturnTypeHints: true,
12+
});

0 commit comments

Comments
 (0)