Improve some completions on generic object literals (#34855)

* Weird fix

* Slightly better I guess

* Update APIs

* Make `hasTypeArguments` do what it says on the tin

* Fix merge mistake
This commit is contained in:
Andrew Branch
2019-12-11 11:21:28 -08:00
committed by GitHub
parent 7fd6e0da62
commit f1f874699c
9 changed files with 107 additions and 15 deletions
+2 -2
View File
@@ -21160,9 +21160,9 @@ namespace ts {
if (isJsxOpeningLikeElement(callTarget) && argIndex === 0) {
return getEffectiveFirstArgumentForJsxSignature(signature, callTarget);
}
if (contextFlags && contextFlags & ContextFlags.Completion && signature.target) {
if (contextFlags && contextFlags & ContextFlags.BaseConstraint && signature.target && !hasTypeArguments(callTarget)) {
const baseSignature = getBaseSignature(signature.target);
return intersectTypes(getTypeAtPosition(signature, argIndex), getTypeAtPosition(baseSignature, argIndex));
return getTypeAtPosition(baseSignature, argIndex);
}
return getTypeAtPosition(signature, argIndex);
}
+11 -4
View File
@@ -709,6 +709,13 @@ namespace ts {
| JSDocOptionalType
| JSDocVariadicType;
export type HasTypeArguments =
| CallExpression
| NewExpression
| TaggedTemplateExpression
| JsxOpeningElement
| JsxSelfClosingElement;
export type HasInitializer =
| HasExpressionInitializer
| ForStatement
@@ -3537,10 +3544,10 @@ namespace ts {
/* @internal */
export const enum ContextFlags {
None = 0,
Signature = 1 << 0, // Obtaining contextual signature
NoConstraints = 1 << 1, // Don't obtain type variable constraints
Completion = 1 << 2, // Obtaining constraint type for completion
None = 0,
Signature = 1 << 0, // Obtaining contextual signature
NoConstraints = 1 << 1, // Don't obtain type variable constraints
BaseConstraint = 1 << 2, // Use base constraint type for completions
}
// NOTE: If modifying this enum, must modify `TypeFormatFlags` too!
+4
View File
@@ -2433,6 +2433,10 @@ namespace ts {
return (node as ParameterDeclaration).dotDotDotToken !== undefined || !!type && type.kind === SyntaxKind.JSDocVariadicType;
}
export function hasTypeArguments(node: Node): node is HasTypeArguments {
return !!(node as HasTypeArguments).typeArguments;
}
export const enum AssignmentKind {
None, Definite, Compound
}
+14 -9
View File
@@ -1279,7 +1279,7 @@ namespace ts.Completions {
// Cursor is inside a JSX self-closing element or opening element
const attrsType = jsxContainer && typeChecker.getContextualType(jsxContainer.attributes);
if (!attrsType) return GlobalsSearch.Continue;
symbols = filterJsxAttributes(getPropertiesForObjectExpression(attrsType, jsxContainer!.attributes, typeChecker), jsxContainer!.attributes.properties);
symbols = filterJsxAttributes(getPropertiesForObjectExpression(attrsType, /*baseType*/ undefined, jsxContainer!.attributes, typeChecker), jsxContainer!.attributes.properties);
setSortTextToOptionalMember();
completionKind = CompletionKind.MemberLike;
isNewIdentifierLocation = false;
@@ -1798,10 +1798,11 @@ namespace ts.Completions {
let existingMembers: readonly Declaration[] | undefined;
if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) {
const typeForObject = typeChecker.getContextualType(objectLikeContainer, ContextFlags.Completion);
if (!typeForObject) return GlobalsSearch.Fail;
isNewIdentifierLocation = hasIndexSignature(typeForObject);
typeMembers = getPropertiesForObjectExpression(typeForObject, objectLikeContainer, typeChecker);
const instantiatedType = typeChecker.getContextualType(objectLikeContainer);
const baseType = instantiatedType && typeChecker.getContextualType(objectLikeContainer, ContextFlags.BaseConstraint);
if (!instantiatedType || !baseType) return GlobalsSearch.Fail;
isNewIdentifierLocation = hasIndexSignature(instantiatedType || baseType);
typeMembers = getPropertiesForObjectExpression(instantiatedType, baseType, objectLikeContainer, typeChecker);
existingMembers = objectLikeContainer.properties;
}
else {
@@ -2538,15 +2539,19 @@ namespace ts.Completions {
return jsdoc && jsdoc.tags && (rangeContainsPosition(jsdoc, position) ? findLast(jsdoc.tags, tag => tag.pos < position) : undefined);
}
function getPropertiesForObjectExpression(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes, checker: TypeChecker): Symbol[] {
return contextualType.isUnion()
? checker.getAllPossiblePropertiesOfTypes(contextualType.types.filter(memberType =>
function getPropertiesForObjectExpression(contextualType: Type, baseConstrainedType: Type | undefined, obj: ObjectLiteralExpression | JsxAttributes, checker: TypeChecker): Symbol[] {
const type = baseConstrainedType && !(baseConstrainedType.flags & TypeFlags.AnyOrUnknown)
? checker.getUnionType([contextualType, baseConstrainedType])
: contextualType;
return type.isUnion()
? checker.getAllPossiblePropertiesOfTypes(type.types.filter(memberType =>
// If we're providing completions for an object literal, skip primitive, array-like, or callable types since those shouldn't be implemented by object literals.
!(memberType.flags & TypeFlags.Primitive ||
checker.isArrayLikeType(memberType) ||
typeHasCallOrConstructSignatures(memberType, checker) ||
checker.isTypeInvalidDueToUnionDiscriminant(memberType, obj))))
: contextualType.getApparentProperties();
: type.getApparentProperties();
}
/**
+1
View File
@@ -502,6 +502,7 @@ declare namespace ts {
}
export type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | LabeledStatement | ExpressionStatement | VariableStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | ExportDeclaration | EndOfFileToken;
export type HasType = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertySignature | PropertyDeclaration | TypePredicateNode | ParenthesizedTypeNode | TypeOperatorNode | MappedTypeNode | AssertionExpression | TypeAliasDeclaration | JSDocTypeExpression | JSDocNonNullableType | JSDocNullableType | JSDocOptionalType | JSDocVariadicType;
export type HasTypeArguments = CallExpression | NewExpression | TaggedTemplateExpression | JsxOpeningElement | JsxSelfClosingElement;
export type HasInitializer = HasExpressionInitializer | ForStatement | ForInStatement | ForOfStatement | JsxAttribute;
export type HasExpressionInitializer = VariableDeclaration | ParameterDeclaration | BindingElement | PropertySignature | PropertyDeclaration | PropertyAssignment | EnumMember;
export interface NodeArray<T extends Node> extends ReadonlyArray<T>, TextRange {
+1
View File
@@ -502,6 +502,7 @@ declare namespace ts {
}
export type HasJSDoc = ParameterDeclaration | CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignature | PropertySignature | ArrowFunction | ParenthesizedExpression | SpreadAssignment | ShorthandPropertyAssignment | PropertyAssignment | FunctionExpression | LabeledStatement | ExpressionStatement | VariableStatement | FunctionDeclaration | ConstructorDeclaration | MethodDeclaration | PropertyDeclaration | AccessorDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | EnumMember | EnumDeclaration | ModuleDeclaration | ImportEqualsDeclaration | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | JSDocFunctionType | ExportDeclaration | EndOfFileToken;
export type HasType = SignatureDeclaration | VariableDeclaration | ParameterDeclaration | PropertySignature | PropertyDeclaration | TypePredicateNode | ParenthesizedTypeNode | TypeOperatorNode | MappedTypeNode | AssertionExpression | TypeAliasDeclaration | JSDocTypeExpression | JSDocNonNullableType | JSDocNullableType | JSDocOptionalType | JSDocVariadicType;
export type HasTypeArguments = CallExpression | NewExpression | TaggedTemplateExpression | JsxOpeningElement | JsxSelfClosingElement;
export type HasInitializer = HasExpressionInitializer | ForStatement | ForInStatement | ForOfStatement | JsxAttribute;
export type HasExpressionInitializer = VariableDeclaration | ParameterDeclaration | BindingElement | PropertySignature | PropertyDeclaration | PropertyAssignment | EnumMember;
export interface NodeArray<T extends Node> extends ReadonlyArray<T>, TextRange {
@@ -0,0 +1,22 @@
/// <reference path="fourslash.ts" />
////declare function f<T extends string>(
//// p: { a: T extends 'foo' ? { x: string } : { y: string } }
////): void;
////
////f<'foo'>({ a: { /*1*/ } });
////f<string>({ a: { /*2*/ } });
verify.completions({
marker: '1',
exact: [{
name: 'x'
}]
});
verify.completions({
marker: '2',
exact: [{
name: 'y'
}]
});
@@ -0,0 +1,18 @@
/// <reference path="fourslash.ts" />
// #34825
////interface Sample {
//// addBook: { name: string, year: number }
////}
////
////export declare function testIt<T>(method: T[keyof T]): any
////testIt<Sample>({ /**/ });
verify.completions({
marker: '',
exact: [
{ name: 'name' },
{ name: 'year' },
]
});
@@ -0,0 +1,34 @@
/// <reference path="fourslash.ts" />
// #34825
////export type GetMethodsForType<T, G extends string> = { [K in keyof T]:
//// T[K] extends () => any ? { name: K, group: G, } : T[K] extends (s: infer U) => any ? { name: K, group: G, payload: U } : never }[keyof T];
////
////
////class Sample {
//// count = 0;
//// books: { name: string, year: number }[] = []
//// increment() {
//// this.count++
//// this.count++
//// }
////
//// addBook(book: Sample["books"][0]) {
//// this.books.push(book)
//// }
////}
////export declare function testIt<T, G extends string>(): (input: any, method: GetMethodsForType<T, G>) => any
////
////
////const t = testIt<Sample, "Sample">()
////
////const i = t(null, { name: "addBook", group: "Sample", payload: { /**/ } })
verify.completions({
marker: '',
exact: [
{ name: 'name' },
{ name: 'year' },
]
});