Improve references search and quick info on properties with type errors within nullable contextual types (#61383)

This commit is contained in:
Mateusz Burzyński 2025-10-30 19:54:29 +01:00 committed by GitHub
parent 6fd2874d0d
commit 2b2d6cebcb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 176 additions and 0 deletions

View File

@ -3575,6 +3575,7 @@ function getSymbolAtLocationForQuickInfo(node: Node, checker: TypeChecker): Symb
* @internal
*/
export function getPropertySymbolsFromContextualType(node: ObjectLiteralElementWithName, checker: TypeChecker, contextualType: Type, unionSymbolOk: boolean): readonly Symbol[] {
contextualType = contextualType.getNonNullableType();
const name = getNameFromPropertyName(node.name);
if (!name) return emptyArray;
if (!contextualType.isUnion()) {

View File

@ -0,0 +1,121 @@
// === findAllReferences ===
// === /tests/cases/fourslash/findAllRefsFromContextualUnionType1.ts ===
// function test1(arg: { <|[|prop|]: "foo"|> }) {}
// test1({ /*FIND ALL REFS*/<|[|{| isWriteAccess: true, isDefinition: true |}prop|]: "bar"|> });
//
// function test2(arg: { prop: "foo" } | undefined) {}
// test2({ prop: "bar" });
// === Definitions ===
// === /tests/cases/fourslash/findAllRefsFromContextualUnionType1.ts ===
// function test1(arg: { <|[|prop|]: "foo"|> }) {}
// test1({ /*FIND ALL REFS*/prop: "bar" });
//
// function test2(arg: { prop: "foo" } | undefined) {}
// test2({ prop: "bar" });
// === Details ===
[
{
"containerKind": "",
"containerName": "",
"kind": "property",
"name": "(property) prop: \"foo\"",
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "property",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "prop",
"kind": "propertyName"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "\"foo\"",
"kind": "stringLiteral"
}
]
}
]
// === findAllReferences ===
// === /tests/cases/fourslash/findAllRefsFromContextualUnionType1.ts ===
// function test1(arg: { prop: "foo" }) {}
// test1({ prop: "bar" });
//
// function test2(arg: { <|[|prop|]: "foo"|> } | undefined) {}
// test2({ /*FIND ALL REFS*/<|[|{| isWriteAccess: true, isDefinition: true |}prop|]: "bar"|> });
// === Definitions ===
// === /tests/cases/fourslash/findAllRefsFromContextualUnionType1.ts ===
// function test1(arg: { prop: "foo" }) {}
// test1({ prop: "bar" });
//
// function test2(arg: { <|[|prop|]: "foo"|> } | undefined) {}
// test2({ /*FIND ALL REFS*/prop: "bar" });
// === Details ===
[
{
"containerKind": "",
"containerName": "",
"kind": "property",
"name": "(property) prop: \"foo\"",
"displayParts": [
{
"text": "(",
"kind": "punctuation"
},
{
"text": "property",
"kind": "text"
},
{
"text": ")",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "prop",
"kind": "propertyName"
},
{
"text": ":",
"kind": "punctuation"
},
{
"text": " ",
"kind": "space"
},
{
"text": "\"foo\"",
"kind": "stringLiteral"
}
]
}
]

View File

@ -0,0 +1,10 @@
/// <reference path='fourslash.ts' />
// @strict: true
//// function test1(arg: { prop: "foo" }) {}
//// test1({ /*1*/prop: "bar" });
////
//// function test2(arg: { prop: "foo" } | undefined) {}
//// test2({ /*2*/prop: "bar" });
verify.baselineFindAllReferences("1", "2");

View File

@ -0,0 +1,11 @@
/// <reference path='fourslash.ts' />
// @strict: true
//// function test1(arg: { prop: "foo" }) {}
//// test1({ /*1*/prop: "bar" });
////
//// function test2(arg: { prop: "foo" } | undefined) {}
//// test2({ /*2*/prop: "bar" });
verify.quickInfoAt("1", '(property) prop: "foo"');
verify.quickInfoAt("2", '(property) prop: "foo"');

View File

@ -0,0 +1,33 @@
/// <reference path='fourslash.ts' />
// https://github.com/microsoft/TypeScript/issues/61382
// @strict: true
//// declare const foo1: <D extends Foo1<D>>(definition: D) => D;
////
//// type Foo1<D, Bar = Prop<D, "bar">> = {
//// bar: {
//// [K in keyof Bar]: Bar[K] extends boolean
//// ? Bar[K]
//// : "Error: bar should be boolean";
//// };
//// };
////
//// declare const foo2: <D extends Foo2<D>>(definition: D) => D;
////
//// type Foo2<D, Bar = Prop<D, "bar">> = {
//// bar?: {
//// [K in keyof Bar]: Bar[K] extends boolean
//// ? Bar[K]
//// : "Error: bar should be boolean";
//// };
//// };
////
//// type Prop<T, K> = K extends keyof T ? T[K] : never;
////
//// foo1({ bar: { /*1*/X: "test" } });
////
//// foo2({ bar: { /*2*/X: "test" } });
verify.quickInfoAt("1", '(property) X: "Error: bar should be boolean"');
verify.quickInfoAt("2", '(property) X: "Error: bar should be boolean"');