Support getting string literal completions based on a type argument constraint (#21168)
* Support getting string literal completions based on a type argument constraint * Fix bug: look for require call before argument info * Code review * @sandersn code review * Remove test cast * Reduce completions.ts diff * @weswigham review * Remove getTypeArgumentConstraint's dependence on checkTypeArgumentConstraints * Remove TODO
This commit is contained in:
parent
f8a378a1d7
commit
8c2756fdf6
5 changed files with 27 additions and 5 deletions
|
@ -299,6 +299,10 @@ namespace ts {
|
|||
node = getParseTreeNode(node);
|
||||
return node && tryGetThisTypeAt(node);
|
||||
},
|
||||
getTypeArgumentConstraint: node => {
|
||||
node = getParseTreeNode(node, isTypeNode);
|
||||
return node && getTypeArgumentConstraint(node);
|
||||
},
|
||||
};
|
||||
|
||||
const tupleTypes: GenericType[] = [];
|
||||
|
@ -7356,7 +7360,7 @@ namespace ts {
|
|||
type = getTypeReferenceType(node, symbol);
|
||||
}
|
||||
// Cache both the resolved symbol and the resolved type. The resolved symbol is needed in when we check the
|
||||
// type reference in checkTypeReferenceOrExpressionWithTypeArguments.
|
||||
// type reference in checkTypeReferenceNode.
|
||||
links.resolvedSymbol = symbol;
|
||||
links.resolvedType = type;
|
||||
}
|
||||
|
@ -20274,9 +20278,8 @@ namespace ts {
|
|||
typeArguments = getEffectiveTypeArguments(node, typeParameters);
|
||||
mapper = createTypeMapper(typeParameters, typeArguments);
|
||||
}
|
||||
const typeArgument = typeArguments[i];
|
||||
result = result && checkTypeAssignableTo(
|
||||
typeArgument,
|
||||
typeArguments[i],
|
||||
instantiateType(constraint, mapper),
|
||||
node.typeArguments[i],
|
||||
Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
|
||||
|
@ -20320,6 +20323,14 @@ namespace ts {
|
|||
}
|
||||
}
|
||||
|
||||
function getTypeArgumentConstraint(node: TypeNode): Type | undefined {
|
||||
const typeReferenceNode = tryCast(node.parent, isTypeReferenceType);
|
||||
if (!typeReferenceNode) return undefined;
|
||||
const typeParameters = getTypeParametersForTypeReference(typeReferenceNode);
|
||||
const constraint = getConstraintOfTypeParameter(typeParameters[typeReferenceNode.typeArguments.indexOf(node)!]);
|
||||
return constraint && instantiateType(constraint, createTypeMapper(typeParameters, getEffectiveTypeArguments(typeReferenceNode, typeParameters)));
|
||||
}
|
||||
|
||||
function checkTypeQuery(node: TypeQueryNode) {
|
||||
getTypeFromTypeQueryNode(node);
|
||||
}
|
||||
|
|
|
@ -2944,6 +2944,7 @@ namespace ts {
|
|||
/* @internal */ resolveExternalModuleSymbol(symbol: Symbol): Symbol;
|
||||
/** @param node A location where we might consider accessing `this`. Not necessarily a ThisExpression. */
|
||||
/* @internal */ tryGetThisTypeAt(node: Node): Type | undefined;
|
||||
/* @internal */ getTypeArgumentConstraint(node: TypeNode): Type | undefined;
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
|
|
|
@ -5940,4 +5940,9 @@ namespace ts {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* @internal */
|
||||
export function isTypeReferenceType(node: Node): node is TypeReferenceType {
|
||||
return node.kind === SyntaxKind.TypeReference || node.kind === SyntaxKind.ExpressionWithTypeArguments;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -358,8 +358,7 @@ namespace ts.Completions {
|
|||
case SyntaxKind.LiteralType:
|
||||
switch (node.parent.parent.kind) {
|
||||
case SyntaxKind.TypeReference:
|
||||
// TODO: GH#21168
|
||||
return undefined;
|
||||
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(typeChecker.getTypeArgumentConstraint(node.parent as LiteralTypeNode), typeChecker) };
|
||||
case SyntaxKind.IndexedAccessType:
|
||||
// Get all apparent property names
|
||||
// i.e. interface Foo {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo { foo: string; bar: string; }
|
||||
////type T = Pick<Foo, "/**/">;
|
||||
|
||||
verify.completionsAt("", ["foo", "bar"]);
|
Loading…
Reference in a new issue