diff --git a/src/services/inlayHints.ts b/src/services/inlayHints.ts
index ce779052a05..a3e54cf2378 100644
--- a/src/services/inlayHints.ts
+++ b/src/services/inlayHints.ts
@@ -119,6 +119,7 @@ import {
TupleTypeReference,
Type,
TypeFlags,
+ TypePredicate,
unescapeLeadingUnderscores,
UserPreferences,
usingSingleLineStringWriter,
@@ -405,6 +406,16 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
return;
}
+ const typePredicate = checker.getTypePredicateOfSignature(signature);
+
+ if (typePredicate?.type) {
+ const hintParts = typePredicateToInlayHintParts(typePredicate);
+ if (hintParts) {
+ addTypeHints(hintParts, getTypeAnnotationPosition(decl));
+ return;
+ }
+ }
+
const returnType = checker.getReturnTypeOfSignature(signature);
if (isModuleReferenceType(returnType)) {
return;
@@ -474,6 +485,17 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
});
}
+ function printTypePredicateInSingleLine(typePredicate: TypePredicate) {
+ const flags = NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.AllowUniqueESSymbolType | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope;
+ const printer = createPrinterWithRemoveComments();
+
+ return usingSingleLineStringWriter(writer => {
+ const typePredicateNode = checker.typePredicateToTypePredicateNode(typePredicate, /*enclosingDeclaration*/ undefined, flags);
+ Debug.assertIsDefined(typePredicateNode, "should always get typePredicateNode");
+ printer.writeNode(EmitHint.Unspecified, typePredicateNode, /*sourceFile*/ file, writer);
+ });
+ }
+
function typeToInlayHintParts(type: Type): InlayHintDisplayPart[] | string {
if (!shouldUseInteractiveInlayHints(preferences)) {
return printTypeInSingleLine(type);
@@ -481,10 +503,24 @@ export function provideInlayHints(context: InlayHintsContext): InlayHint[] {
const flags = NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.AllowUniqueESSymbolType | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope;
const typeNode = checker.typeToTypeNode(type, /*enclosingDeclaration*/ undefined, flags);
- Debug.assertIsDefined(typeNode, "should always get typenode");
+ Debug.assertIsDefined(typeNode, "should always get typeNode");
+ return getInlayHintDisplayParts(typeNode);
+ }
+ function typePredicateToInlayHintParts(typePredicate: TypePredicate): InlayHintDisplayPart[] | string {
+ if (!shouldUseInteractiveInlayHints(preferences)) {
+ return printTypePredicateInSingleLine(typePredicate);
+ }
+
+ const flags = NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.AllowUniqueESSymbolType | NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope;
+ const typeNode = checker.typePredicateToTypePredicateNode(typePredicate, /*enclosingDeclaration*/ undefined, flags);
+ Debug.assertIsDefined(typeNode, "should always get typenode");
+ return getInlayHintDisplayParts(typeNode);
+ }
+
+ function getInlayHintDisplayParts(node: Node) {
const parts: InlayHintDisplayPart[] = [];
- visitForDisplayParts(typeNode);
+ visitForDisplayParts(node);
return parts;
function visitForDisplayParts(node: Node) {
diff --git a/tests/baselines/reference/inlayHintsInferredTypePredicate1.baseline b/tests/baselines/reference/inlayHintsInferredTypePredicate1.baseline
new file mode 100644
index 00000000000..df607df5111
--- /dev/null
+++ b/tests/baselines/reference/inlayHintsInferredTypePredicate1.baseline
@@ -0,0 +1,9 @@
+// === Inlay Hints ===
+function test(x: unknown) {
+ ^
+{
+ "text": ": x is number",
+ "position": 25,
+ "kind": "Type",
+ "whitespaceBefore": true
+}
\ No newline at end of file
diff --git a/tests/baselines/reference/inlayHintsInteractiveInferredTypePredicate1.baseline b/tests/baselines/reference/inlayHintsInteractiveInferredTypePredicate1.baseline
new file mode 100644
index 00000000000..7bc4345bf09
--- /dev/null
+++ b/tests/baselines/reference/inlayHintsInteractiveInferredTypePredicate1.baseline
@@ -0,0 +1,23 @@
+// === Inlay Hints ===
+function test(x: unknown) {
+ ^
+{
+ "text": "",
+ "displayParts": [
+ {
+ "text": ": "
+ },
+ {
+ "text": "x"
+ },
+ {
+ "text": " is "
+ },
+ {
+ "text": "number"
+ }
+ ],
+ "position": 25,
+ "kind": "Type",
+ "whitespaceBefore": true
+}
\ No newline at end of file
diff --git a/tests/cases/fourslash/inlayHintsInferredTypePredicate1.ts b/tests/cases/fourslash/inlayHintsInferredTypePredicate1.ts
new file mode 100644
index 00000000000..fd1de15cd9f
--- /dev/null
+++ b/tests/cases/fourslash/inlayHintsInferredTypePredicate1.ts
@@ -0,0 +1,11 @@
+///
+
+// @strict: true
+
+//// function test(x: unknown) {
+//// return typeof x === 'number';
+//// }
+
+verify.baselineInlayHints(undefined, {
+ includeInlayFunctionLikeReturnTypeHints: true,
+});
diff --git a/tests/cases/fourslash/inlayHintsInteractiveInferredTypePredicate1.ts b/tests/cases/fourslash/inlayHintsInteractiveInferredTypePredicate1.ts
new file mode 100644
index 00000000000..88282cf849e
--- /dev/null
+++ b/tests/cases/fourslash/inlayHintsInteractiveInferredTypePredicate1.ts
@@ -0,0 +1,12 @@
+///
+
+// @strict: true
+
+//// function test(x: unknown) {
+//// return typeof x === 'number';
+//// }
+
+verify.baselineInlayHints(undefined, {
+ interactiveInlayHints: true,
+ includeInlayFunctionLikeReturnTypeHints: true,
+});
\ No newline at end of file