mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-12-10 11:28:20 -06:00
Enhance type argument completions (#62170)
This commit is contained in:
parent
83ff20281e
commit
d4b15eb56d
@ -421,6 +421,7 @@ import {
|
||||
hasSyntacticModifier,
|
||||
hasSyntacticModifiers,
|
||||
hasType,
|
||||
hasTypeArguments,
|
||||
HeritageClause,
|
||||
hostGetCanonicalFileName,
|
||||
Identifier,
|
||||
@ -42620,6 +42621,51 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets generic signatures from the function's/constructor's type.
|
||||
*/
|
||||
function getUninstantiatedSignatures(node: CallLikeExpression): readonly Signature[] {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.Decorator:
|
||||
return getSignaturesOfType(
|
||||
getTypeOfExpression(node.expression),
|
||||
SignatureKind.Call,
|
||||
);
|
||||
case SyntaxKind.NewExpression:
|
||||
return getSignaturesOfType(
|
||||
getTypeOfExpression(node.expression),
|
||||
SignatureKind.Construct,
|
||||
);
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
case SyntaxKind.JsxOpeningElement:
|
||||
if (isJsxIntrinsicTagName(node.tagName)) return [];
|
||||
return getSignaturesOfType(
|
||||
getTypeOfExpression(node.tagName),
|
||||
SignatureKind.Call,
|
||||
);
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
return getSignaturesOfType(
|
||||
getTypeOfExpression(node.tag),
|
||||
SignatureKind.Call,
|
||||
);
|
||||
case SyntaxKind.BinaryExpression:
|
||||
case SyntaxKind.JsxOpeningFragment:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function getTypeParameterConstraintForPositionAcrossSignatures(signatures: readonly Signature[], position: number) {
|
||||
const relevantTypeParameterConstraints = flatMap(signatures, signature => {
|
||||
const relevantTypeParameter = signature.typeParameters?.[position];
|
||||
if (relevantTypeParameter === undefined) return [];
|
||||
const relevantConstraint = getConstraintOfTypeParameter(relevantTypeParameter);
|
||||
if (relevantConstraint === undefined) return [];
|
||||
return [relevantConstraint];
|
||||
});
|
||||
return getUnionType(relevantTypeParameterConstraints);
|
||||
}
|
||||
|
||||
function checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) {
|
||||
checkGrammarTypeArguments(node, node.typeArguments);
|
||||
if (node.kind === SyntaxKind.TypeReference && !isInJSFile(node) && !isInJSDoc(node) && node.typeArguments && node.typeName.end !== node.typeArguments.pos) {
|
||||
@ -42658,12 +42704,59 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
}
|
||||
|
||||
function getTypeArgumentConstraint(node: TypeNode): Type | undefined {
|
||||
const typeReferenceNode = tryCast(node.parent, isTypeReferenceType);
|
||||
if (!typeReferenceNode) return undefined;
|
||||
const typeParameters = getTypeParametersForTypeReferenceOrImport(typeReferenceNode);
|
||||
if (!typeParameters) return undefined;
|
||||
const constraint = getConstraintOfTypeParameter(typeParameters[typeReferenceNode.typeArguments!.indexOf(node)]);
|
||||
return constraint && instantiateType(constraint, createTypeMapper(typeParameters, getEffectiveTypeArguments(typeReferenceNode, typeParameters)));
|
||||
let typeArgumentPosition;
|
||||
if (hasTypeArguments(node.parent) && Array.isArray(node.parent.typeArguments)) {
|
||||
typeArgumentPosition = node.parent.typeArguments.indexOf(node);
|
||||
}
|
||||
|
||||
if (typeArgumentPosition !== undefined) {
|
||||
// The node could be a type argument of a call, a `new` expression, a decorator, an
|
||||
// instantiation expression, or a generic type instantiation.
|
||||
|
||||
if (isCallLikeExpression(node.parent)) {
|
||||
return getTypeParameterConstraintForPositionAcrossSignatures(
|
||||
getUninstantiatedSignatures(node.parent),
|
||||
typeArgumentPosition,
|
||||
);
|
||||
}
|
||||
|
||||
if (isDecorator(node.parent.parent)) {
|
||||
return getTypeParameterConstraintForPositionAcrossSignatures(
|
||||
getUninstantiatedSignatures(node.parent.parent),
|
||||
typeArgumentPosition,
|
||||
);
|
||||
}
|
||||
|
||||
if (isExpressionWithTypeArguments(node.parent) && isExpressionStatement(node.parent.parent)) {
|
||||
const uninstantiatedType = checkExpression(node.parent.expression);
|
||||
|
||||
const callConstraint = getTypeParameterConstraintForPositionAcrossSignatures(
|
||||
getSignaturesOfType(uninstantiatedType, SignatureKind.Call),
|
||||
typeArgumentPosition,
|
||||
);
|
||||
const constructConstraint = getTypeParameterConstraintForPositionAcrossSignatures(
|
||||
getSignaturesOfType(uninstantiatedType, SignatureKind.Construct),
|
||||
typeArgumentPosition,
|
||||
);
|
||||
|
||||
// An instantiation expression instantiates both call and construct signatures, so
|
||||
// if both exist type arguments must be assignable to both constraints.
|
||||
if (constructConstraint.flags & TypeFlags.Never) return callConstraint;
|
||||
if (callConstraint.flags & TypeFlags.Never) return constructConstraint;
|
||||
return getIntersectionType([callConstraint, constructConstraint]);
|
||||
}
|
||||
|
||||
if (isTypeReferenceType(node.parent)) {
|
||||
const typeParameters = getTypeParametersForTypeReferenceOrImport(node.parent);
|
||||
if (!typeParameters) return undefined;
|
||||
const relevantTypeParameter = typeParameters[typeArgumentPosition];
|
||||
const constraint = getConstraintOfTypeParameter(relevantTypeParameter);
|
||||
return constraint && instantiateType(
|
||||
constraint,
|
||||
createTypeMapper(typeParameters, getEffectiveTypeArguments(node.parent, typeParameters)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkTypeQuery(node: TypeQueryNode) {
|
||||
|
||||
@ -256,7 +256,6 @@ import {
|
||||
isTypeOnlyImportDeclaration,
|
||||
isTypeOnlyImportOrExportDeclaration,
|
||||
isTypeParameterDeclaration,
|
||||
isTypeReferenceType,
|
||||
isValidTypeOnlyAliasUseSite,
|
||||
isVariableDeclaration,
|
||||
isVariableLike,
|
||||
@ -3626,17 +3625,20 @@ function getCompletionData(
|
||||
}
|
||||
|
||||
log("getCompletionData: Semantic work: " + (timestamp() - semanticStart));
|
||||
const contextualType = previousToken && getContextualType(previousToken, position, sourceFile, typeChecker);
|
||||
const contextualTypeOrConstraint = previousToken && (
|
||||
getContextualType(previousToken, position, sourceFile, typeChecker) ??
|
||||
getConstraintOfTypeArgumentProperty(previousToken, typeChecker)
|
||||
);
|
||||
|
||||
// exclude literal suggestions after <input type="text" [||] /> (#51667) and after closing quote (#52675)
|
||||
// for strings getStringLiteralCompletions handles completions
|
||||
const isLiteralExpected = !tryCast(previousToken, isStringLiteralLike) && !isJsxIdentifierExpected;
|
||||
const literals = !isLiteralExpected ? [] : mapDefined(
|
||||
contextualType && (contextualType.isUnion() ? contextualType.types : [contextualType]),
|
||||
contextualTypeOrConstraint && (contextualTypeOrConstraint.isUnion() ? contextualTypeOrConstraint.types : [contextualTypeOrConstraint]),
|
||||
t => t.isLiteral() && !(t.flags & TypeFlags.EnumLiteral) ? t.value : undefined,
|
||||
);
|
||||
|
||||
const recommendedCompletion = previousToken && contextualType && getRecommendedCompletion(previousToken, contextualType, typeChecker);
|
||||
const recommendedCompletion = previousToken && contextualTypeOrConstraint && getRecommendedCompletion(previousToken, contextualTypeOrConstraint, typeChecker);
|
||||
return {
|
||||
kind: CompletionDataKind.Data,
|
||||
symbols,
|
||||
@ -5766,11 +5768,13 @@ function tryGetTypeLiteralNode(node: Node): TypeLiteralNode | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getConstraintOfTypeArgumentProperty(node: Node, checker: TypeChecker): Type | undefined {
|
||||
/** @internal */
|
||||
export function getConstraintOfTypeArgumentProperty(node: Node, checker: TypeChecker): Type | undefined {
|
||||
if (!node) return undefined;
|
||||
|
||||
if (isTypeNode(node) && isTypeReferenceType(node.parent)) {
|
||||
return checker.getTypeArgumentConstraint(node);
|
||||
if (isTypeNode(node)) {
|
||||
const constraint = checker.getTypeArgumentConstraint(node);
|
||||
if (constraint) return constraint;
|
||||
}
|
||||
|
||||
const t = getConstraintOfTypeArgumentProperty(node.parent, checker);
|
||||
@ -5779,10 +5783,19 @@ function getConstraintOfTypeArgumentProperty(node: Node, checker: TypeChecker):
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.PropertySignature:
|
||||
return checker.getTypeOfPropertyOfContextualType(t, (node as PropertySignature).symbol.escapedName);
|
||||
case SyntaxKind.ColonToken:
|
||||
if (node.parent.kind === SyntaxKind.PropertySignature) {
|
||||
// The cursor is at a property value location like `Foo<{ x: | }`.
|
||||
// `t` already refers to the appropriate property type.
|
||||
return t;
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.IntersectionType:
|
||||
case SyntaxKind.TypeLiteral:
|
||||
case SyntaxKind.UnionType:
|
||||
return t;
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
return checker.getElementTypeOfArrayType(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ import {
|
||||
createCompletionDetails,
|
||||
createCompletionDetailsForSymbol,
|
||||
getCompletionEntriesFromSymbols,
|
||||
getConstraintOfTypeArgumentProperty,
|
||||
getDefaultCommitCharacters,
|
||||
getPropertiesForObjectExpression,
|
||||
Log,
|
||||
@ -509,7 +510,12 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL
|
||||
|
||||
function fromUnionableLiteralType(grandParent: Node): StringLiteralCompletionsFromTypes | StringLiteralCompletionsFromProperties | undefined {
|
||||
switch (grandParent.kind) {
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.ExpressionWithTypeArguments:
|
||||
case SyntaxKind.JsxOpeningElement:
|
||||
case SyntaxKind.JsxSelfClosingElement:
|
||||
case SyntaxKind.NewExpression:
|
||||
case SyntaxKind.TaggedTemplateExpression:
|
||||
case SyntaxKind.TypeReference: {
|
||||
const typeArgument = findAncestor(parent, n => n.parent === grandParent) as LiteralTypeNode;
|
||||
if (typeArgument) {
|
||||
@ -529,6 +535,8 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL
|
||||
return undefined;
|
||||
}
|
||||
return stringLiteralCompletionsFromProperties(typeChecker.getTypeFromTypeNode(objectType));
|
||||
case SyntaxKind.PropertySignature:
|
||||
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(getConstraintOfTypeArgumentProperty(grandParent, typeChecker)), isNewIdentifier: false };
|
||||
case SyntaxKind.UnionType: {
|
||||
const result = fromUnionableLiteralType(walkUpParentheses(grandParent.parent));
|
||||
if (!result) {
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: number;
|
||||
////}
|
||||
////interface Bar {
|
||||
//// three: boolean;
|
||||
//// four: {
|
||||
//// five: unknown;
|
||||
//// };
|
||||
////}
|
||||
////
|
||||
////function a<T extends Foo>() {}
|
||||
////a<{/*0*/}>();
|
||||
////
|
||||
////var b = () => <T extends Foo>() => {};
|
||||
////b()<{/*1*/}>();
|
||||
////
|
||||
////declare function c<T extends Foo>(): void
|
||||
////declare function c<T extends Bar>(): void
|
||||
////c<{/*2*/}>();
|
||||
////
|
||||
////function d<T extends Foo, U extends Bar>() {}
|
||||
////d<{/*3*/}, {/*4*/}>();
|
||||
////d<Foo, { four: {/*5*/} }>();
|
||||
////
|
||||
////(<T extends Foo>() => {})<{/*6*/}>();
|
||||
|
||||
verify.completions(
|
||||
{ marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true },
|
||||
{ marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: true },
|
||||
{ marker: "2", unsorted: ["one", "two", "three", "four"], isNewIdentifierLocation: true },
|
||||
{ marker: "3", unsorted: ["one", "two"], isNewIdentifierLocation: true },
|
||||
{ marker: "4", unsorted: ["three", "four"], isNewIdentifierLocation: true },
|
||||
{ marker: "5", unsorted: ["five"], isNewIdentifierLocation: true },
|
||||
{ marker: "6", unsorted: ["one", "two"], isNewIdentifierLocation: true },
|
||||
);
|
||||
@ -0,0 +1,32 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: number;
|
||||
////}
|
||||
////interface Bar {
|
||||
//// three: boolean;
|
||||
//// four: symbol;
|
||||
////}
|
||||
////
|
||||
////class A<T extends Foo> {}
|
||||
////new A<{/*0*/}>();
|
||||
////
|
||||
////class B<T extends Foo, U extends Bar> {}
|
||||
////new B<{/*1*/}, {/*2*/}>();
|
||||
////
|
||||
////declare const C: {
|
||||
//// new <T extends Foo>(): unknown
|
||||
//// new <T extends Bar>(): unknown
|
||||
////}
|
||||
////new C<{/*3*/}>()
|
||||
////
|
||||
////new (class <T extends Foo> {})<{/*4*/}>();
|
||||
|
||||
verify.completions(
|
||||
{ marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true },
|
||||
{ marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: true },
|
||||
{ marker: "2", unsorted: ["three", "four"], isNewIdentifierLocation: true },
|
||||
{ marker: "3", unsorted: ["one", "two", "three", "four"], isNewIdentifierLocation: true },
|
||||
{ marker: "4", unsorted: ["one", "two"], isNewIdentifierLocation: true },
|
||||
);
|
||||
@ -0,0 +1,25 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// kind: 'foo';
|
||||
//// one: string;
|
||||
////}
|
||||
////interface Bar {
|
||||
//// kind: 'bar';
|
||||
//// two: number;
|
||||
////}
|
||||
////
|
||||
////declare function a<T extends Foo>(): void
|
||||
////declare function a<T extends Bar>(): void
|
||||
////a<{ kind: 'bar', /*0*/ }>();
|
||||
////
|
||||
////declare function b<T extends Foo>(kind: 'foo'): void
|
||||
////declare function b<T extends Bar>(kind: 'bar'): void
|
||||
////b<{/*1*/}>('bar');
|
||||
|
||||
// The completion lists are unfortunately not narrowed here (ideally only
|
||||
// properties of `Bar` would be suggested).
|
||||
verify.completions(
|
||||
{ marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true },
|
||||
{ marker: "1", unsorted: ["kind", "one", "two"], isNewIdentifierLocation: true },
|
||||
);
|
||||
@ -0,0 +1,18 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @jsx: preserve
|
||||
// @filename: a.tsx
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: number;
|
||||
////}
|
||||
////
|
||||
////const Component = <T extends Foo>() => <></>;
|
||||
////
|
||||
////<Component<{/*0*/}>></Component>;
|
||||
////<Component<{/*1*/}>/>;
|
||||
|
||||
verify.completions(
|
||||
{ marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true },
|
||||
{ marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: true },
|
||||
);
|
||||
@ -0,0 +1,10 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: number;
|
||||
////}
|
||||
////declare function f<T extends Foo>(x: TemplateStringsArray): void;
|
||||
////f<{/*0*/}>``;
|
||||
|
||||
verify.completions({ marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true });
|
||||
@ -0,0 +1,15 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: number;
|
||||
////}
|
||||
////
|
||||
////declare function decorator<T extends Foo>(originalMethod: unknown, _context: unknown): never
|
||||
////
|
||||
////class {
|
||||
//// @decorator<{/*0*/}>
|
||||
//// method() {}
|
||||
////}
|
||||
|
||||
verify.completions({ marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true });
|
||||
@ -0,0 +1,35 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////interface Foo {
|
||||
//// one: string;
|
||||
//// two: number;
|
||||
////}
|
||||
////interface Bar {
|
||||
//// three: boolean;
|
||||
//// four: {
|
||||
//// five: unknown;
|
||||
//// };
|
||||
////}
|
||||
////
|
||||
////(<T extends Foo>() => {})<{/*0*/}>;
|
||||
////
|
||||
////(class <T extends Foo>{})<{/*1*/}>;
|
||||
////
|
||||
////declare const a: {
|
||||
//// new <T extends Foo>(): {};
|
||||
//// <T extends Bar>(): {};
|
||||
////}
|
||||
////a<{/*2*/}>;
|
||||
////
|
||||
////declare const b: {
|
||||
//// new <T extends { one: true }>(): {};
|
||||
//// <T extends { one: false }>(): {};
|
||||
////}
|
||||
////b<{/*3*/}>;
|
||||
|
||||
verify.completions(
|
||||
{ marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: true },
|
||||
{ marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: true },
|
||||
{ marker: "2", unsorted: ["one", "two", "three", "four"], isNewIdentifierLocation: true },
|
||||
{ marker: "3", unsorted: [], isNewIdentifierLocation: true },
|
||||
);
|
||||
@ -0,0 +1,18 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////class Foo<T extends { x: 'one' | 2 }> {}
|
||||
////function foo<T extends { x: 'one' | 2 }>() {}
|
||||
////
|
||||
////type A = Foo<{ x: /*0*/ }>;
|
||||
////new Foo<{ x: /*1*/ }>();
|
||||
////foo<{ x: /*2*/ }>();
|
||||
////foo<{ x: /*3*/ }>;
|
||||
////Foo<{ x: /*4*/ }>;
|
||||
|
||||
verify.completions(
|
||||
{ marker: "0", includes: ['"one"', '2'], isNewIdentifierLocation: false },
|
||||
{ marker: "1", includes: ['"one"', '2'], isNewIdentifierLocation: false },
|
||||
{ marker: "2", includes: ['"one"', '2'], isNewIdentifierLocation: false },
|
||||
{ marker: "3", includes: ['"one"', '2'], isNewIdentifierLocation: false },
|
||||
{ marker: "4", includes: ['"one"', '2'], isNewIdentifierLocation: false },
|
||||
);
|
||||
@ -0,0 +1,24 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////class Foo<T extends { x: 'one' | 'two' }> {}
|
||||
////function foo<T extends { x: 'one' | 'two' }>() {}
|
||||
////declare function tag<T extends { x: 'one' | 'two' }>(x: TemplateStringsArray): void;
|
||||
////declare function decorator<T extends { x: 'one' | 'two' }>(...args: unknown[]): never
|
||||
////
|
||||
////type A = Foo<{ x: '/*0*/' }>;
|
||||
////new Foo<{ x: '/*1*/' }>();
|
||||
////foo<{ x: '/*2*/' }>();
|
||||
////foo<{ x: '/*3*/' }>;
|
||||
////Foo<{ x: '/*4*/' }>;
|
||||
////tag<{ x: '/*5*/' }>``;
|
||||
////class { @decorator<{ x: '/*6*/' }>; method() {} }
|
||||
|
||||
verify.completions(
|
||||
{ marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "2", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "3", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "4", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "5", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "6", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
);
|
||||
@ -0,0 +1,24 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////class Foo<T extends 'one' | 'two'> {}
|
||||
////function foo<T extends 'one' | 'two'>() {}
|
||||
////declare function tag<T extends 'one' | 'two'>(x: TemplateStringsArray): void;
|
||||
////declare function decorator<T extends 'one' | 'two'>(...args: unknown[]): never
|
||||
////
|
||||
////type A = Foo<'/*0*/'>;
|
||||
////new Foo<'/*1*/'>();
|
||||
////foo<'/*2*/'>();
|
||||
////foo<'/*3*/'>;
|
||||
////Foo<'/*4*/'>;
|
||||
////tag<'/*5*/'>``;
|
||||
////class { @decorator<'/*6*/'>; method() {} }
|
||||
|
||||
verify.completions(
|
||||
{ marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "2", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "3", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "4", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "5", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "6", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
);
|
||||
@ -0,0 +1,18 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
// @jsx: preserve
|
||||
// @filename: a.tsx
|
||||
////const Component1 = <T extends { x: 'one' | 'two' }>() => <></>;
|
||||
////const Component2 = <T extends 'one' | 'two'>() => <></>;
|
||||
////
|
||||
////<Component1<{ x: '/*0*/' }>></Component>;
|
||||
////<Component1<{ x: '/*1*/' }>/>;
|
||||
////<Component2<'/*2*/'>></Component>;
|
||||
////<Component2<'/*3*/'>/>;
|
||||
|
||||
verify.completions(
|
||||
{ marker: "0", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "1", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "2", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
{ marker: "3", unsorted: ["one", "two"], isNewIdentifierLocation: false },
|
||||
);
|
||||
@ -0,0 +1,18 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////class Foo<T extends ('one' | 2)[]> {}
|
||||
////function foo<T extends ('one' | 2)[]>() {}
|
||||
////
|
||||
////type A = Foo<[/*0*/]>;
|
||||
////new Foo<[/*1*/]>();
|
||||
////foo<[/*2*/]>();
|
||||
////foo<[/*3*/]>;
|
||||
////Foo<[/*4*/]>;
|
||||
|
||||
verify.completions(
|
||||
{ marker: "0", includes: ['"one"', '2'], isNewIdentifierLocation: true },
|
||||
{ marker: "1", includes: ['"one"', '2'], isNewIdentifierLocation: true },
|
||||
{ marker: "2", includes: ['"one"', '2'], isNewIdentifierLocation: true },
|
||||
{ marker: "3", includes: ['"one"', '2'], isNewIdentifierLocation: true },
|
||||
{ marker: "4", includes: ['"one"', '2'], isNewIdentifierLocation: true }
|
||||
);
|
||||
Loading…
x
Reference in New Issue
Block a user