-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Improve autocomplete for union of tuples #58571
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
7962488
620a6e3
bbac8a9
0fca93c
f2e9f32
660fa9f
eee1282
1d36c55
df5b937
ecf39c7
8c31d7e
4967819
79a4a79
dcb9b35
baf6036
d24f618
6c935e8
002e5c4
e127de7
4272086
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23832,7 +23832,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |
findMostOverlappyType(source, target); | ||
} | ||
|
||
function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: (readonly [() => Type, __String])[], related: (source: Type, target: Type) => boolean | Ternary) { | ||
function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: (readonly [() => Type, __String | number])[], related: (source: Type, target: Type) => boolean | Ternary) { | ||
const types = target.types; | ||
const include: Ternary[] = types.map(t => t.flags & TypeFlags.Primitive ? Ternary.False : Ternary.True); | ||
for (const [getDiscriminatingType, propertyName] of discriminators) { | ||
|
@@ -23842,7 +23842,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |
let matched = false; | ||
for (let i = 0; i < types.length; i++) { | ||
if (include[i]) { | ||
const targetType = getTypeOfPropertyOrIndexSignatureOfType(types[i], propertyName); | ||
const targetType = typeof propertyName === "number" | ||
? getContextualTypeForElementExpression(types[i], propertyName) | ||
: getTypeOfPropertyOrIndexSignatureOfType(types[i], propertyName); | ||
if (targetType && related(getDiscriminatingType(), targetType)) { | ||
matched = true; | ||
} | ||
|
@@ -31361,6 +31363,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |
); | ||
} | ||
|
||
function discriminateContextualTypeByElements(node: ArrayLiteralExpression, contextualType: UnionType) { | ||
const key = `D${getNodeId(node)},${getTypeId(contextualType)}`; | ||
const cached = getCachedType(key); | ||
if (cached) return cached; | ||
|
||
// Create a discriminator for each element in the list | ||
const discriminators = map( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the object version of this function there is a check for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd expect both functions to be very similar so I'd say - yes. if |
||
node.elements, | ||
(e, ndx) => [() => getContextFreeTypeOfExpression(e), ndx] as const, | ||
); | ||
const discriminatedType = discriminateTypeByDiscriminableItems( | ||
contextualType, | ||
discriminators, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. food for thought: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. removed the length check in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's relevant because this const tup: [(arg: number) => void] | [(arg: string) => void, number] = [
(arg) => {},
]; |
||
isTypeAssignableTo, | ||
); | ||
|
||
return setCachedType(key, discriminatedType); | ||
} | ||
|
||
// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily | ||
// be "pushed" onto a node using the contextualType property. | ||
function getApparentTypeOfContextualType(node: Expression | MethodDeclaration, contextFlags: ContextFlags | undefined): Type | undefined { | ||
|
@@ -31378,9 +31399,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { | |
t => getObjectFlags(t) & ObjectFlags.Mapped ? t : getApparentType(t), | ||
/*noReductions*/ true, | ||
); | ||
return apparentType.flags & TypeFlags.Union && isObjectLiteralExpression(node) ? discriminateContextualTypeByObjectMembers(node, apparentType as UnionType) : | ||
apparentType.flags & TypeFlags.Union && isJsxAttributes(node) ? discriminateContextualTypeByJSXAttributes(node, apparentType as UnionType) : | ||
apparentType; | ||
const isUnion = apparentType.flags & TypeFlags.Union; | ||
if (isUnion && isObjectLiteralExpression(node)) { | ||
return discriminateContextualTypeByObjectMembers(node, apparentType as UnionType); | ||
} | ||
else if (isUnion && isJsxAttributes(node)) { | ||
return discriminateContextualTypeByJSXAttributes(node, apparentType as UnionType); | ||
} | ||
else if (contextFlags && contextFlags & ContextFlags.Completions && isUnion && isArrayLiteralExpression(node)) { | ||
return discriminateContextualTypeByElements(node, instantiatedType as UnionType); | ||
} | ||
else { | ||
return apparentType; | ||
} | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/// <reference path="fourslash.ts" /> | ||
|
||
////declare const x: ["a", "b"] = ["x", "/**/"] | ||
|
||
verify.completions({ marker: "", exact: ["b"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/// <reference path="fourslash.ts" /> | ||
|
||
////declare const x: ["a", ["b"], "x", ["y"]] = ["a", ["/**/"]] | ||
|
||
verify.completions({ marker: "", exact: ["b"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/// <reference path="fourslash.ts" /> | ||
|
||
////declare const a: "a"; | ||
////declare const x: ['a', 'b'] | ['a', 'c'] | ['d', 'e'] = [a, "/**/"]; | ||
|
||
verify.completions({ marker: "", exact: ["b", "c"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/// <reference path="fourslash.ts" /> | ||
////declare const x: ['a', 'b'] | { '0': 'x', '1': 'y' } | { '1': 'z' } = ['x', '/**/']; | ||
|
||
verify.completions({ marker: "", exact: ["y"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/// <reference path="fourslash.ts" /> | ||
////type ABC = "a" | "b" | "c"; | ||
////declare const x: ['abc', ...ABC[]] | ['abc', 'abc', "f"] = ['abc', 'abc', '/**/']; | ||
|
||
verify.completions({ marker: "", exact: ["f"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/// <reference path="fourslash.ts" /> | ||
////declare const x: [(arg: number) => void, "num"] | [(arg: string) => void, "str"] | [(arg: boolean) => boolean, "val"] = [() => {}, '/**/']; | ||
|
||
verify.completions({ marker: "", exact: ["num", "str"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/// <reference path="fourslash.ts" /> | ||
////declare const x: [(arg: number) => void, "num"] | [(arg: string) => void, "str"] | [(arg: number) => number, "num2"] = [(x: number) => {}, '/**/']; | ||
|
||
verify.completions({ marker: "", exact: ["num"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/// <reference path="fourslash.ts" /> | ||
|
||
////declare const a: "a"; | ||
////declare const x: [{ t: "a" }, "b"] | [{ t: "b" }, "c"] = [{ t: a }, "/**/"] | ||
|
||
verify.completions({ marker: "", exact: ["b"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/// <reference path="fourslash.ts" /> | ||
|
||
////declare const a: "a"; | ||
////declare const x: ['a', 'b', 'f'] | ['a', 'b'] = [a, 'b', "/**/"]; | ||
|
||
verify.completions({ marker: "", exact: ["f"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/// <reference path="fourslash.ts" /> | ||
|
||
////declare const x: ['a', 'b', 'f', 'g'] | ['a', 'b'] = ["a", 'b', 'f', "/**/"]; | ||
|
||
verify.completions({ marker: "", exact: ["g"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/// <reference path="fourslash.ts" /> | ||
|
||
////declare const x: ['a', 'b'] | ['a', 'c'] = ['b', "/**/"]; | ||
|
||
verify.completions({ marker: "", exact: ["b", "c"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/// <reference path="fourslash.ts" /> | ||
|
||
////declare const x: ['a', 'v1'] | ['b', 'v2'] = ['b', "v/**/"]; | ||
|
||
verify.completions({ marker: "", exact: ["v2"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/// <reference path="fourslash.ts" /> | ||
////type ABC = "a" | "b" | "c"; | ||
////type XYZ = "x" | "y" | "z"; | ||
////declare const x: ['abc', ABC] | ['xyz', ...XYZ[]] = ['xyz', 'x', 'x', 'x', 'x', '/**/']; | ||
|
||
verify.completions({ marker: "", exact: ["x", "y", "z"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
/// <reference path="fourslash.ts" /> | ||
////type ABC = "a" | "b" | "c"; | ||
////type XYZ = "x" | "y" | "z"; | ||
////declare const x: ['abc', ...ABC[]] | ['xyz', ...XYZ[]] = ['abc', 'a', 'b', 'c', 'a', '/**/']; | ||
|
||
verify.completions({ marker: "", exact: ["a", "b", "c"] }); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
/// <reference path="fourslash.ts" /> | ||
////type ABC = "a" | "b" | "c"; | ||
////declare const x: ['abc', ...ABC[]] | ['abc', "a", "f"] = ['abc', 'a', '/**/']; | ||
|
||
verify.completions({ marker: "", exact: ["a", "b", "c", "f"] }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
found that this was not working like I wanted with variadic tuple types, so for tuples im using
getContextualTypeForElementExpression
. Not sure if there's a better way to do this, also considered making this function an argument