mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-14 19:16:17 -06:00
Improve isPossiblyTypeArgumentPosition (#25043)
* Improve isPossiblyTypeArgumentPosition * Update API (#24966) * Handle new Type<Type< * Add test for `new C < C<`
This commit is contained in:
parent
97f10bc3a2
commit
52486ae362
@ -4767,7 +4767,7 @@ namespace FourSlashInterface {
|
||||
}
|
||||
|
||||
export interface VerifyCompletionsOptions {
|
||||
readonly marker?: ArrayOrSingle<string>;
|
||||
readonly marker?: ArrayOrSingle<string | FourSlash.Marker>;
|
||||
readonly isNewIdentifierLocation?: boolean;
|
||||
readonly exact?: ArrayOrSingle<ExpectedCompletionEntry>;
|
||||
readonly includes?: ArrayOrSingle<ExpectedCompletionEntry>;
|
||||
|
||||
@ -1060,7 +1060,7 @@ namespace ts.Completions {
|
||||
const isImportType = isLiteralImportTypeNode(node);
|
||||
const isTypeLocation = insideJsDocTagTypeExpression || (isImportType && !(node as ImportTypeNode).isTypeOf) || isPartOfTypeNode(node.parent);
|
||||
const isRhsOfImportDeclaration = isInRightSideOfInternalImportEqualsDeclaration(node);
|
||||
const allowTypeOrValue = isRhsOfImportDeclaration || (!isTypeLocation && isPossiblyTypeArgumentPosition(contextToken, sourceFile));
|
||||
const allowTypeOrValue = isRhsOfImportDeclaration || (!isTypeLocation && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker));
|
||||
if (isEntityName(node) || isImportType) {
|
||||
let symbol = typeChecker.getSymbolAtLocation(node);
|
||||
if (symbol) {
|
||||
@ -1275,7 +1275,7 @@ namespace ts.Completions {
|
||||
|
||||
function filterGlobalCompletion(symbols: Symbol[]): void {
|
||||
const isTypeOnlyCompletion = insideJsDocTagTypeExpression || !isContextTokenValueLocation(contextToken) && (isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken));
|
||||
const allowTypes = isTypeOnlyCompletion || !isContextTokenValueLocation(contextToken) && isPossiblyTypeArgumentPosition(contextToken, sourceFile);
|
||||
const allowTypes = isTypeOnlyCompletion || !isContextTokenValueLocation(contextToken) && isPossiblyTypeArgumentPosition(contextToken, sourceFile, typeChecker);
|
||||
if (isTypeOnlyCompletion) keywordFilters = KeywordCompletionFilters.TypeKeywords;
|
||||
|
||||
filterMutate(symbols, symbol => {
|
||||
|
||||
@ -103,9 +103,7 @@ namespace ts.SignatureHelp {
|
||||
if (onlyUseSyntacticOwners && !lessThanFollowsCalledExpression(startingToken, sourceFile, invocation.called)) {
|
||||
return undefined;
|
||||
}
|
||||
const type = checker.getTypeAtLocation(invocation.called)!; // TODO: GH#18217
|
||||
const signatures = isNewExpression(invocation.called.parent) ? type.getConstructSignatures() : type.getCallSignatures();
|
||||
const candidates = signatures.filter(candidate => !!candidate.typeParameters && candidate.typeParameters.length >= argumentInfo.argumentCount);
|
||||
const candidates = getPossibleGenericSignatures(invocation.called, argumentInfo.argumentCount, checker);
|
||||
return candidates.length === 0 ? undefined : { candidates, resolvedSignature: first(candidates) };
|
||||
}
|
||||
else {
|
||||
@ -261,7 +259,7 @@ namespace ts.SignatureHelp {
|
||||
};
|
||||
}
|
||||
else {
|
||||
const typeArgInfo = isPossiblyTypeArgumentPosition(node, sourceFile);
|
||||
const typeArgInfo = getPossibleTypeArgumentsInfo(node, sourceFile);
|
||||
if (typeArgInfo) {
|
||||
const { called, nTypeArguments } = typeArgInfo;
|
||||
const invocation: Invocation = { kind: InvocationKind.TypeArgs, called };
|
||||
|
||||
@ -917,11 +917,25 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function isPossiblyTypeArgumentPosition(token: Node, sourceFile: SourceFile, checker: TypeChecker): boolean {
|
||||
const info = getPossibleTypeArgumentsInfo(token, sourceFile);
|
||||
return info !== undefined && (isPartOfTypeNode(info.called) ||
|
||||
getPossibleGenericSignatures(info.called, info.nTypeArguments, checker).length !== 0 ||
|
||||
isPossiblyTypeArgumentPosition(info.called, sourceFile, checker));
|
||||
}
|
||||
|
||||
export function getPossibleGenericSignatures(called: Expression, typeArgumentCount: number, checker: TypeChecker): ReadonlyArray<Signature> {
|
||||
const type = checker.getTypeAtLocation(called)!; // TODO: GH#18217
|
||||
const signatures = isNewExpression(called.parent) ? type.getConstructSignatures() : type.getCallSignatures();
|
||||
return signatures.filter(candidate => !!candidate.typeParameters && candidate.typeParameters.length >= typeArgumentCount);
|
||||
}
|
||||
|
||||
export interface PossibleTypeArgumentInfo {
|
||||
readonly called: Identifier;
|
||||
readonly nTypeArguments: number;
|
||||
}
|
||||
export function isPossiblyTypeArgumentPosition(tokenIn: Node, sourceFile: SourceFile): PossibleTypeArgumentInfo | undefined {
|
||||
// Get info for an expression like `f <` that may be the start of type arguments.
|
||||
export function getPossibleTypeArgumentsInfo(tokenIn: Node, sourceFile: SourceFile): PossibleTypeArgumentInfo | undefined {
|
||||
let token: Node | undefined = tokenIn;
|
||||
// This function determines if the node could be type argument position
|
||||
// Since during editing, when type argument list is not complete,
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////const x = 0;
|
||||
////type T = number;
|
||||
////function f(x: number) {}
|
||||
////function g<T>(x: T) {}
|
||||
////class C<T> {}
|
||||
|
||||
////x + {| "valueOnly": true |}
|
||||
////x < {| "valueOnly": true |}
|
||||
////f < {| "valueOnly": true |}
|
||||
////g < {| "valueOnly": false |}
|
||||
////const something: C<{| "valueOnly": false |};
|
||||
////const something2: C<C<{| "valueOnly": false |};
|
||||
////new C<{| "valueOnly": false |};
|
||||
////new C<C<{| "valueOnly": false |};
|
||||
////
|
||||
////declare const callAndConstruct: { new<T>(): callAndConstruct<T>; <T>(): string; };
|
||||
////interface callAndConstruct<T> {}
|
||||
////new callAndConstruct<callAndConstruct</*callAndConstruct*/
|
||||
|
||||
for (const marker of test.markers()) {
|
||||
if (marker.data && marker.data.valueOnly) {
|
||||
verify.completions({ marker, includes: "x", excludes: "T" });
|
||||
}
|
||||
else {
|
||||
verify.completions({ marker, includes: ["x", "T"] });
|
||||
}
|
||||
}
|
||||
|
||||
verify.signatureHelp({
|
||||
marker: "callAndConstruct",
|
||||
text: "callAndConstruct<T>(): string",
|
||||
parameterName: "T",
|
||||
parameterSpan: "T",
|
||||
parameterCount: 1,
|
||||
argumentCount: 1,
|
||||
});
|
||||
@ -200,7 +200,7 @@ declare namespace FourSlashInterface {
|
||||
assertHasRanges(ranges: Range[]): void;
|
||||
caretAtMarker(markerName?: string): void;
|
||||
completions(...options: {
|
||||
readonly marker?: ArrayOrSingle<string>,
|
||||
readonly marker?: ArrayOrSingle<string | Marker>,
|
||||
readonly isNewIdentifierLocation?: boolean;
|
||||
readonly exact?: ArrayOrSingle<ExpectedCompletionEntry>;
|
||||
readonly includes?: ArrayOrSingle<ExpectedCompletionEntry>;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user