mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-03-15 14:05:47 -05:00
Handle completions in interface / type literal similar to class (#22701)
* Handle completions in interface / type literal similar to class * Code review
This commit is contained in:
@@ -990,7 +990,7 @@ namespace ts {
|
||||
|
||||
export interface MethodSignature extends SignatureDeclarationBase, TypeElement {
|
||||
kind: SyntaxKind.MethodSignature;
|
||||
parent?: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
|
||||
parent?: ObjectTypeDeclaration;
|
||||
name: PropertyName;
|
||||
}
|
||||
|
||||
@@ -1045,7 +1045,7 @@ namespace ts {
|
||||
|
||||
export interface IndexSignatureDeclaration extends SignatureDeclarationBase, ClassElement, TypeElement {
|
||||
kind: SyntaxKind.IndexSignature;
|
||||
parent?: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
|
||||
parent?: ObjectTypeDeclaration;
|
||||
}
|
||||
|
||||
export interface TypeNode extends Node {
|
||||
@@ -2026,6 +2026,8 @@ namespace ts {
|
||||
block: Block;
|
||||
}
|
||||
|
||||
export type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
|
||||
|
||||
export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag;
|
||||
|
||||
export interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
|
||||
|
||||
@@ -948,7 +948,7 @@ namespace ts {
|
||||
case SyntaxKind.ClassDeclaration:
|
||||
case SyntaxKind.ClassExpression:
|
||||
case SyntaxKind.TypeLiteral:
|
||||
return (<ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode>node).members;
|
||||
return (<ObjectTypeDeclaration>node).members;
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
return (<ObjectLiteralExpression>node).properties;
|
||||
}
|
||||
@@ -3910,6 +3910,10 @@ namespace ts {
|
||||
seen.set(key, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isObjectTypeDeclaration(node: Node): node is ObjectTypeDeclaration {
|
||||
return isClassLike(node) || isInterfaceDeclaration(node) || isTypeLiteralNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts {
|
||||
|
||||
@@ -18,7 +18,8 @@ namespace ts.Completions {
|
||||
|
||||
const enum KeywordCompletionFilters {
|
||||
None,
|
||||
ClassElementKeywords, // Keywords at class keyword
|
||||
ClassElementKeywords, // Keywords inside class body
|
||||
InterfaceElementKeywords, // Keywords inside interface body
|
||||
ConstructorParameterKeywords, // Keywords at constructor parameter
|
||||
FunctionLikeBodyKeywords, // Keywords at function like body
|
||||
TypeKeywords,
|
||||
@@ -1527,58 +1528,51 @@ namespace ts.Completions {
|
||||
* Relevant symbols are stored in the captured 'symbols' variable.
|
||||
*/
|
||||
function tryGetClassLikeCompletionSymbols(): GlobalsSearch {
|
||||
const classLikeDeclaration = tryGetClassLikeCompletionContainer(contextToken);
|
||||
if (!classLikeDeclaration) return GlobalsSearch.Continue;
|
||||
const decl = tryGetObjectTypeDeclarationCompletionContainer(sourceFile, contextToken, location);
|
||||
if (!decl) return GlobalsSearch.Continue;
|
||||
|
||||
// We're looking up possible property names from parent type.
|
||||
completionKind = CompletionKind.MemberLike;
|
||||
// Declaring new property/method/accessor
|
||||
isNewIdentifierLocation = true;
|
||||
// Has keywords for class elements
|
||||
keywordFilters = KeywordCompletionFilters.ClassElementKeywords;
|
||||
keywordFilters = isClassLike(decl) ? KeywordCompletionFilters.ClassElementKeywords : KeywordCompletionFilters.InterfaceElementKeywords;
|
||||
|
||||
const baseTypeNode = getClassExtendsHeritageClauseElement(classLikeDeclaration);
|
||||
const implementsTypeNodes = getClassImplementsHeritageClauseElements(classLikeDeclaration);
|
||||
if (baseTypeNode || implementsTypeNodes) {
|
||||
const classElement = contextToken.parent;
|
||||
let classElementModifierFlags = isClassElement(classElement) && getModifierFlags(classElement);
|
||||
// If you're in an interface you don't want to repeat things from super-interface. So just stop here.
|
||||
if (!isClassLike(decl)) return GlobalsSearch.Success;
|
||||
|
||||
const baseTypeNode = getClassExtendsHeritageClauseElement(decl);
|
||||
const implementsTypeNodes = getClassImplementsHeritageClauseElements(decl);
|
||||
if (!baseTypeNode && !implementsTypeNodes) return GlobalsSearch.Success;
|
||||
|
||||
const classElement = contextToken.parent;
|
||||
const classElementModifierFlags = (isClassElement(classElement) ? getModifierFlags(classElement) : ModifierFlags.None)
|
||||
// If this is context token is not something we are editing now, consider if this would lead to be modifier
|
||||
if (contextToken.kind === SyntaxKind.Identifier && !isCurrentlyEditingNode(contextToken)) {
|
||||
switch (contextToken.getText()) {
|
||||
case "private":
|
||||
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Private;
|
||||
break;
|
||||
case "static":
|
||||
classElementModifierFlags = classElementModifierFlags | ModifierFlags.Static;
|
||||
break;
|
||||
}
|
||||
}
|
||||
| (isIdentifier(contextToken) && !isCurrentlyEditingNode(contextToken) ? modifierToFlag(contextToken.originalKeywordKind) : ModifierFlags.None);
|
||||
|
||||
// No member list for private methods
|
||||
if (!(classElementModifierFlags & ModifierFlags.Private)) {
|
||||
let baseClassTypeToGetPropertiesFrom: Type;
|
||||
if (baseTypeNode) {
|
||||
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeAtLocation(baseTypeNode);
|
||||
if (classElementModifierFlags & ModifierFlags.Static) {
|
||||
// Use static class to get property symbols from
|
||||
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeOfSymbolAtLocation(
|
||||
baseClassTypeToGetPropertiesFrom.symbol, classLikeDeclaration);
|
||||
}
|
||||
}
|
||||
const implementedInterfaceTypePropertySymbols = (classElementModifierFlags & ModifierFlags.Static) ?
|
||||
emptyArray :
|
||||
flatMap(implementsTypeNodes || emptyArray, typeNode => typeChecker.getPropertiesOfType(typeChecker.getTypeAtLocation(typeNode)));
|
||||
// No member list for private methods
|
||||
if (classElementModifierFlags & ModifierFlags.Private) return GlobalsSearch.Success;
|
||||
|
||||
// List of property symbols of base type that are not private and already implemented
|
||||
symbols = filterClassMembersList(
|
||||
baseClassTypeToGetPropertiesFrom ?
|
||||
typeChecker.getPropertiesOfType(baseClassTypeToGetPropertiesFrom) :
|
||||
emptyArray,
|
||||
implementedInterfaceTypePropertySymbols,
|
||||
classLikeDeclaration.members,
|
||||
classElementModifierFlags);
|
||||
let baseClassTypeToGetPropertiesFrom: Type | undefined;
|
||||
if (baseTypeNode) {
|
||||
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeAtLocation(baseTypeNode);
|
||||
if (classElementModifierFlags & ModifierFlags.Static) {
|
||||
// Use static class to get property symbols from
|
||||
baseClassTypeToGetPropertiesFrom = typeChecker.getTypeOfSymbolAtLocation(baseClassTypeToGetPropertiesFrom.symbol, decl);
|
||||
}
|
||||
}
|
||||
|
||||
const implementedInterfaceTypePropertySymbols = !implementsTypeNodes || (classElementModifierFlags & ModifierFlags.Static)
|
||||
? emptyArray
|
||||
: flatMap(implementsTypeNodes, typeNode => typeChecker.getPropertiesOfType(typeChecker.getTypeAtLocation(typeNode)));
|
||||
|
||||
// List of property symbols of base type that are not private and already implemented
|
||||
symbols = filterClassMembersList(
|
||||
baseClassTypeToGetPropertiesFrom ? typeChecker.getPropertiesOfType(baseClassTypeToGetPropertiesFrom) : emptyArray,
|
||||
implementedInterfaceTypePropertySymbols,
|
||||
decl.members,
|
||||
classElementModifierFlags);
|
||||
|
||||
return GlobalsSearch.Success;
|
||||
}
|
||||
|
||||
@@ -1622,10 +1616,6 @@ namespace ts.Completions {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isFromClassElementDeclaration(node: Node) {
|
||||
return node.parent && isClassElement(node.parent) && isClassLike(node.parent.parent);
|
||||
}
|
||||
|
||||
function isParameterOfConstructorDeclaration(node: Node) {
|
||||
return isParameter(node) && isConstructorDeclaration(node.parent);
|
||||
}
|
||||
@@ -1636,56 +1626,6 @@ namespace ts.Completions {
|
||||
(isConstructorParameterCompletionKeyword(node.kind) || isDeclarationName(node));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the immediate owning class declaration of a context token,
|
||||
* on the condition that one exists and that the context implies completion should be given.
|
||||
*/
|
||||
function tryGetClassLikeCompletionContainer(contextToken: Node): ClassLikeDeclaration {
|
||||
if (contextToken) {
|
||||
switch (contextToken.kind) {
|
||||
case SyntaxKind.OpenBraceToken: // class c { |
|
||||
if (isClassLike(contextToken.parent)) {
|
||||
return contextToken.parent;
|
||||
}
|
||||
break;
|
||||
|
||||
// class c {getValue(): number, | }
|
||||
case SyntaxKind.CommaToken:
|
||||
if (isClassLike(contextToken.parent)) {
|
||||
return contextToken.parent;
|
||||
}
|
||||
break;
|
||||
|
||||
// class c {getValue(): number; | }
|
||||
case SyntaxKind.SemicolonToken:
|
||||
// class c { method() { } | }
|
||||
case SyntaxKind.CloseBraceToken:
|
||||
if (isClassLike(location)) {
|
||||
return location;
|
||||
}
|
||||
// class c { method() { } b| }
|
||||
if (isFromClassElementDeclaration(location) &&
|
||||
(location.parent as ClassElement).name === location) {
|
||||
return location.parent.parent as ClassLikeDeclaration;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isFromClassElementDeclaration(contextToken) &&
|
||||
(isClassMemberCompletionKeyword(contextToken.kind) ||
|
||||
isClassMemberCompletionKeywordText(contextToken.getText()))) {
|
||||
return contextToken.parent.parent as ClassLikeDeclaration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// class c { method() { } | method2() { } }
|
||||
if (location && location.kind === SyntaxKind.SyntaxList && isClassLike(location.parent)) {
|
||||
return location.parent;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the immediate owning class declaration of a context token,
|
||||
* on the condition that one exists and that the context implies completion should be given.
|
||||
@@ -1820,15 +1760,7 @@ namespace ts.Completions {
|
||||
isFunctionLikeButNotConstructor(containingNodeKind);
|
||||
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
return containingNodeKind === SyntaxKind.EnumDeclaration || // enum a { |
|
||||
containingNodeKind === SyntaxKind.InterfaceDeclaration || // interface a { |
|
||||
containingNodeKind === SyntaxKind.TypeLiteral; // const x : { |
|
||||
|
||||
case SyntaxKind.SemicolonToken:
|
||||
return containingNodeKind === SyntaxKind.PropertySignature &&
|
||||
contextToken.parent && contextToken.parent.parent &&
|
||||
(contextToken.parent.parent.kind === SyntaxKind.InterfaceDeclaration || // interface a { f; |
|
||||
contextToken.parent.parent.kind === SyntaxKind.TypeLiteral); // const x : { a; |
|
||||
return containingNodeKind === SyntaxKind.EnumDeclaration; // enum a { |
|
||||
|
||||
case SyntaxKind.LessThanToken:
|
||||
return containingNodeKind === SyntaxKind.ClassDeclaration || // class A< |
|
||||
@@ -1857,7 +1789,7 @@ namespace ts.Completions {
|
||||
|
||||
case SyntaxKind.GetKeyword:
|
||||
case SyntaxKind.SetKeyword:
|
||||
if (isFromClassElementDeclaration(contextToken)) {
|
||||
if (isFromObjectTypeDeclaration(contextToken)) {
|
||||
return false;
|
||||
}
|
||||
// falls through
|
||||
@@ -1877,7 +1809,7 @@ namespace ts.Completions {
|
||||
// If the previous token is keyword correspoding to class member completion keyword
|
||||
// there will be completion available here
|
||||
if (isClassMemberCompletionKeywordText(contextToken.getText()) &&
|
||||
isFromClassElementDeclaration(contextToken)) {
|
||||
isFromObjectTypeDeclaration(contextToken)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2162,6 +2094,8 @@ namespace ts.Completions {
|
||||
return kind !== SyntaxKind.UndefinedKeyword;
|
||||
case KeywordCompletionFilters.ClassElementKeywords:
|
||||
return isClassMemberCompletionKeyword(kind);
|
||||
case KeywordCompletionFilters.InterfaceElementKeywords:
|
||||
return isInterfaceOrTypeLiteralCompletionKeyword(kind);
|
||||
case KeywordCompletionFilters.ConstructorParameterKeywords:
|
||||
return isConstructorParameterCompletionKeyword(kind);
|
||||
case KeywordCompletionFilters.FunctionLikeBodyKeywords:
|
||||
@@ -2174,6 +2108,10 @@ namespace ts.Completions {
|
||||
}));
|
||||
}
|
||||
|
||||
function isInterfaceOrTypeLiteralCompletionKeyword(kind: SyntaxKind): boolean {
|
||||
return kind === SyntaxKind.ReadonlyKeyword;
|
||||
}
|
||||
|
||||
function isClassMemberCompletionKeyword(kind: SyntaxKind) {
|
||||
switch (kind) {
|
||||
case SyntaxKind.PublicKeyword:
|
||||
@@ -2282,4 +2220,44 @@ namespace ts.Completions {
|
||||
!(memberType.flags & TypeFlags.Primitive || checker.isArrayLikeType(memberType) || typeHasCallOrConstructSignatures(memberType, checker)));
|
||||
return Debug.assertEachDefined(checker.getAllPossiblePropertiesOfTypes(filteredTypes), "getAllPossiblePropertiesOfTypes() should all be defined");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the immediate owning class declaration of a context token,
|
||||
* on the condition that one exists and that the context implies completion should be given.
|
||||
*/
|
||||
function tryGetObjectTypeDeclarationCompletionContainer(sourceFile: SourceFile, contextToken: Node | undefined, location: Node): ObjectTypeDeclaration | undefined {
|
||||
// class c { method() { } | method2() { } }
|
||||
switch (location.kind) {
|
||||
case SyntaxKind.SyntaxList:
|
||||
return tryCast(location.parent, isObjectTypeDeclaration);
|
||||
case SyntaxKind.EndOfFileToken:
|
||||
const cls = tryCast(lastOrUndefined(cast(location.parent, isSourceFile).statements), isObjectTypeDeclaration);
|
||||
if (cls && !findChildOfKind(cls, SyntaxKind.CloseBraceToken, sourceFile)) {
|
||||
return cls;
|
||||
}
|
||||
}
|
||||
|
||||
if (!contextToken) return undefined;
|
||||
switch (contextToken.kind) {
|
||||
case SyntaxKind.SemicolonToken: // class c {getValue(): number; | }
|
||||
case SyntaxKind.CloseBraceToken: // class c { method() { } | }
|
||||
// class c { method() { } b| }
|
||||
return isFromObjectTypeDeclaration(location) && (location.parent as ClassElement | TypeElement).name === location
|
||||
? location.parent.parent as ObjectTypeDeclaration
|
||||
: tryCast(location, isObjectTypeDeclaration);
|
||||
case SyntaxKind.OpenBraceToken: // class c { |
|
||||
case SyntaxKind.CommaToken: // class c {getValue(): number, | }
|
||||
return tryCast(contextToken.parent, isObjectTypeDeclaration);
|
||||
default:
|
||||
if (!isFromObjectTypeDeclaration(contextToken)) return undefined;
|
||||
const isValidKeyword = isClassLike(contextToken.parent.parent) ? isClassMemberCompletionKeyword : isInterfaceOrTypeLiteralCompletionKeyword;
|
||||
return (isValidKeyword(contextToken.kind) || isIdentifier(contextToken) && isValidKeyword(stringToToken(contextToken.text)))
|
||||
? contextToken.parent.parent as ObjectTypeDeclaration : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: GH#19856 Would like to return `node is Node & { parent: (ClassElement | TypeElement) & { parent: ObjectTypeDeclaration } }` but then compilation takes > 10 minutes
|
||||
function isFromObjectTypeDeclaration(node: Node): boolean {
|
||||
return node.parent && (isClassElement(node.parent) || isTypeElement(node.parent)) && isObjectTypeDeclaration(node.parent.parent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -655,7 +655,7 @@ declare namespace ts {
|
||||
}
|
||||
interface MethodSignature extends SignatureDeclarationBase, TypeElement {
|
||||
kind: SyntaxKind.MethodSignature;
|
||||
parent?: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
|
||||
parent?: ObjectTypeDeclaration;
|
||||
name: PropertyName;
|
||||
}
|
||||
interface MethodDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
|
||||
@@ -689,7 +689,7 @@ declare namespace ts {
|
||||
type AccessorDeclaration = GetAccessorDeclaration | SetAccessorDeclaration;
|
||||
interface IndexSignatureDeclaration extends SignatureDeclarationBase, ClassElement, TypeElement {
|
||||
kind: SyntaxKind.IndexSignature;
|
||||
parent?: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
|
||||
parent?: ObjectTypeDeclaration;
|
||||
}
|
||||
interface TypeNode extends Node {
|
||||
_typeNodeBrand: any;
|
||||
@@ -1266,6 +1266,7 @@ declare namespace ts {
|
||||
variableDeclaration?: VariableDeclaration;
|
||||
block: Block;
|
||||
}
|
||||
type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
|
||||
type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag;
|
||||
interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
|
||||
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
|
||||
|
||||
@@ -655,7 +655,7 @@ declare namespace ts {
|
||||
}
|
||||
interface MethodSignature extends SignatureDeclarationBase, TypeElement {
|
||||
kind: SyntaxKind.MethodSignature;
|
||||
parent?: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
|
||||
parent?: ObjectTypeDeclaration;
|
||||
name: PropertyName;
|
||||
}
|
||||
interface MethodDeclaration extends FunctionLikeDeclarationBase, ClassElement, ObjectLiteralElement, JSDocContainer {
|
||||
@@ -689,7 +689,7 @@ declare namespace ts {
|
||||
type AccessorDeclaration = GetAccessorDeclaration | SetAccessorDeclaration;
|
||||
interface IndexSignatureDeclaration extends SignatureDeclarationBase, ClassElement, TypeElement {
|
||||
kind: SyntaxKind.IndexSignature;
|
||||
parent?: ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
|
||||
parent?: ObjectTypeDeclaration;
|
||||
}
|
||||
interface TypeNode extends Node {
|
||||
_typeNodeBrand: any;
|
||||
@@ -1266,6 +1266,7 @@ declare namespace ts {
|
||||
variableDeclaration?: VariableDeclaration;
|
||||
block: Block;
|
||||
}
|
||||
type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
|
||||
type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag;
|
||||
interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
|
||||
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////var aa = 1;
|
||||
|
||||
////interface a { /*interfaceValue1*/
|
||||
|
||||
goTo.eachMarker(() => verify.completionListIsEmpty());
|
||||
@@ -1,7 +0,0 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////var aa = 1;
|
||||
|
||||
////interface a { f/*interfaceValue2*/
|
||||
|
||||
goTo.eachMarker(() => verify.completionListIsEmpty());
|
||||
@@ -1,7 +0,0 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////var aa = 1;
|
||||
|
||||
////interface a { f; /*interfaceValue3*/
|
||||
|
||||
goTo.eachMarker(() => verify.completionListIsEmpty());
|
||||
@@ -7,10 +7,4 @@
|
||||
////
|
||||
////declare function foo<TString, TNumber>(obj: I<TString, TNumber>): { /*1*/
|
||||
|
||||
goTo.marker("1");
|
||||
|
||||
verify.not.completionListContains("I");
|
||||
verify.not.completionListContains("TString");
|
||||
verify.not.completionListContains("TNumber");
|
||||
verify.not.completionListContains("foo");
|
||||
verify.not.completionListContains("obj");
|
||||
verify.completionsAt("1", ["readonly"]);
|
||||
|
||||
@@ -311,7 +311,7 @@ goToMarkAndGeneralVerify('class', { isClassScope: true });
|
||||
//verify.not.completionListContains('ceVar');
|
||||
|
||||
// from interface in mod1
|
||||
goToMarkAndGeneralVerify('interface', { insideMod1: true });
|
||||
verify.completionsAt("interface", ["readonly"]);
|
||||
|
||||
// from namespace in mod1
|
||||
verifyNamespaceInMod1('namespace');
|
||||
@@ -348,7 +348,7 @@ verify.not.completionListContains('ceFunc');
|
||||
verify.not.completionListContains('ceVar');
|
||||
|
||||
// from exported interface in mod1
|
||||
goToMarkAndGeneralVerify('exportedInterface', { insideMod1: true });
|
||||
verify.completionsAt("exportedInterface", ["readonly"]);
|
||||
|
||||
// from exported namespace in mod1
|
||||
verifyExportedNamespace('exportedNamespace');
|
||||
|
||||
18
tests/cases/fourslash/completionsInterfaceElement.ts
Normal file
18
tests/cases/fourslash/completionsInterfaceElement.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
////const foo = 0;
|
||||
////interface I {
|
||||
//// m(): void;
|
||||
//// fo/*i*/
|
||||
////}
|
||||
////interface J { /*j*/ }
|
||||
////interface K { f; /*k*/ }
|
||||
|
||||
////type T = { fo/*t*/ };
|
||||
////type U = { /*u*/ };
|
||||
|
||||
////interface EndOfFile { f; /*e*/
|
||||
|
||||
for (const marker of test.markerNames()) {
|
||||
verify.completionsAt(marker, ["readonly"]);
|
||||
}
|
||||
Reference in New Issue
Block a user