diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 448a15e513..4e55a1c46b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -61,6 +61,7 @@ namespace ts { const noImplicitThis = compilerOptions.noImplicitThis === undefined ? compilerOptions.strict : compilerOptions.noImplicitThis; const emitResolver = createResolver(); + const nodeBuilder = createNodeBuilder(); const undefinedSymbol = createSymbol(SymbolFlags.Property, "undefined"); undefinedSymbol.declarations = []; @@ -106,6 +107,9 @@ namespace ts { getParameterType: getTypeAtPosition, getReturnTypeOfSignature, getNonNullableType, + typeToTypeNode: nodeBuilder.typeToTypeNode, + indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration, + signatureToSignatureDeclaration: nodeBuilder.signatureToSignatureDeclaration, getSymbolsInScope: (location, meaning) => { location = getParseTreeNode(location); return location ? getSymbolsInScope(location, meaning) : []; @@ -2198,6 +2202,584 @@ namespace ts { return result; } + function createNodeBuilder() { + let context: NodeBuilderContext; + + return { + typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { + context = createNodeBuilderContext(enclosingDeclaration, flags); + const resultingNode = typeToTypeNodeHelper(type); + const result = context.encounteredError ? undefined : resultingNode; + return result; + }, + indexInfoToIndexSignatureDeclaration: (indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { + context = createNodeBuilderContext(enclosingDeclaration, flags); + const resultingNode = indexInfoToIndexSignatureDeclarationHelper(indexInfo, kind); + const result = context.encounteredError ? undefined : resultingNode; + return result; + }, + signatureToSignatureDeclaration: (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { + context = createNodeBuilderContext(enclosingDeclaration, flags); + const resultingNode = signatureToSignatureDeclarationHelper(signature, kind); + const result = context.encounteredError ? undefined : resultingNode; + return result; + } + }; + + interface NodeBuilderContext { + readonly enclosingDeclaration: Node | undefined; + readonly flags: NodeBuilderFlags | undefined; + encounteredError: boolean; + inObjectTypeLiteral: boolean; + checkAlias: boolean; + symbolStack: Symbol[] | undefined; + } + + function createNodeBuilderContext(enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined): NodeBuilderContext { + return { + enclosingDeclaration, + flags, + encounteredError: false, + inObjectTypeLiteral: false, + checkAlias: true, + symbolStack: undefined + }; + } + + function typeToTypeNodeHelper(type: Type): TypeNode { + if (!type) { + context.encounteredError = true; + // TODO(aozgaa): should we return implict any (undefined) or explicit any (keywordtypenode)? + return undefined; + } + + if (type.flags & TypeFlags.Any) { + return createKeywordTypeNode(SyntaxKind.AnyKeyword); + } + if (type.flags & TypeFlags.String) { + return createKeywordTypeNode(SyntaxKind.StringKeyword); + } + if (type.flags & TypeFlags.Number) { + return createKeywordTypeNode(SyntaxKind.NumberKeyword); + } + if (type.flags & TypeFlags.Boolean) { + return createKeywordTypeNode(SyntaxKind.BooleanKeyword); + } + if (type.flags & TypeFlags.Enum) { + const name = symbolToName(type.symbol, /*expectsIdentifier*/ false); + return createTypeReferenceNode(name, /*typeArguments*/ undefined); + } + if (type.flags & (TypeFlags.StringLiteral)) { + return createLiteralTypeNode((createLiteral((type).text))); + } + if (type.flags & (TypeFlags.NumberLiteral)) { + return createLiteralTypeNode((createNumericLiteral((type).text))); + } + if (type.flags & TypeFlags.BooleanLiteral) { + return (type).intrinsicName === "true" ? createTrue() : createFalse(); + } + if (type.flags & TypeFlags.EnumLiteral) { + const name = symbolToName(type.symbol, /*expectsIdentifier*/ false); + return createTypeReferenceNode(name, /*typeArguments*/ undefined); + } + if (type.flags & TypeFlags.Void) { + return createKeywordTypeNode(SyntaxKind.VoidKeyword); + } + if (type.flags & TypeFlags.Undefined) { + return createKeywordTypeNode(SyntaxKind.UndefinedKeyword); + } + if (type.flags & TypeFlags.Null) { + return createKeywordTypeNode(SyntaxKind.NullKeyword); + } + if (type.flags & TypeFlags.Never) { + return createKeywordTypeNode(SyntaxKind.NeverKeyword); + } + if (type.flags & TypeFlags.ESSymbol) { + return createKeywordTypeNode(SyntaxKind.SymbolKeyword); + } + if (type.flags & TypeFlags.NonPrimitive) { + return createKeywordTypeNode(SyntaxKind.ObjectKeyword); + } + if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) { + if (context.inObjectTypeLiteral) { + if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowThisInObjectLiteral)) { + context.encounteredError = true; + } + } + return createThis(); + } + + const objectFlags = getObjectFlags(type); + + if (objectFlags & ObjectFlags.Reference) { + Debug.assert(!!(type.flags & TypeFlags.Object)); + return typeReferenceToTypeNode(type); + } + if (objectFlags & ObjectFlags.ClassOrInterface) { + Debug.assert(!!(type.flags & TypeFlags.Object)); + const name = symbolToName(type.symbol, /*expectsIdentifier*/ false); + // TODO(aozgaa): handle type arguments. + return createTypeReferenceNode(name, /*typeArguments*/ undefined); + } + if (type.flags & TypeFlags.TypeParameter) { + const name = symbolToName(type.symbol, /*expectsIdentifier*/ false); + // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. + return createTypeReferenceNode(name, /*typeArguments*/ undefined); + } + + if (context.checkAlias && type.aliasSymbol) { + const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false); + const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments); + return createTypeReferenceNode(name, typeArgumentNodes); + } + context.checkAlias = false; + + if (type.flags & TypeFlags.Union) { + const formattedUnionTypes = formatUnionTypes((type).types); + const unionTypeNodes = formattedUnionTypes && mapToTypeNodeArray(formattedUnionTypes); + if (unionTypeNodes && unionTypeNodes.length > 0) { + return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, unionTypeNodes); + } + else { + if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowEmptyUnionOrIntersection)) { + context.encounteredError = true; + } + return undefined; + } + } + + if (type.flags & TypeFlags.Intersection) { + return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, mapToTypeNodeArray((type as UnionType).types)); + } + + if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) { + Debug.assert(!!(type.flags & TypeFlags.Object)); + // The type is an object literal type. + return createAnonymousTypeNode(type); + } + + if (type.flags & TypeFlags.Index) { + const indexedType = (type).type; + const indexTypeNode = typeToTypeNodeHelper(indexedType); + return createTypeOperatorNode(indexTypeNode); + } + if (type.flags & TypeFlags.IndexedAccess) { + const objectTypeNode = typeToTypeNodeHelper((type).objectType); + const indexTypeNode = typeToTypeNodeHelper((type).indexType); + return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode); + } + + Debug.fail("Should be unreachable."); + + function mapToTypeNodeArray(types: Type[]): TypeNode[] { + const result = []; + for (const type of types) { + const typeNode = typeToTypeNodeHelper(type); + if (typeNode) { + result.push(typeNode); + } + } + return result; + } + + function createMappedTypeNodeFromType(type: MappedType) { + Debug.assert(!!(type.flags & TypeFlags.Object)); + const typeParameter = getTypeParameterFromMappedType(type); + const typeParameterNode = typeParameterToDeclaration(typeParameter); + + const templateType = getTemplateTypeFromMappedType(type); + const templateTypeNode = typeToTypeNodeHelper(templateType); + const readonlyToken = type.declaration && type.declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; + const questionToken = type.declaration && type.declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined; + + return createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode); + } + + function createAnonymousTypeNode(type: ObjectType): TypeNode { + const symbol = type.symbol; + if (symbol) { + // Always use 'typeof T' for type of class, enum, and module objects + if (symbol.flags & SymbolFlags.Class && !getBaseTypeVariableOfClass(symbol) || + symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule) || + shouldWriteTypeOfFunctionSymbol()) { + return createTypeQueryNodeFromSymbol(symbol); + } + else if (contains(context.symbolStack, symbol)) { + // If type is an anonymous type literal in a type alias declaration, use type alias name + const typeAlias = getTypeAliasForTypeLiteral(type); + if (typeAlias) { + // The specified symbol flags need to be reinterpreted as type flags + const entityName = symbolToName(typeAlias, /*expectsIdentifier*/ false); + return createTypeReferenceNode(entityName, /*typeArguments*/ undefined); + } + else { + return createKeywordTypeNode(SyntaxKind.AnyKeyword); + } + } + else { + // Since instantiations of the same anonymous type have the same symbol, tracking symbols instead + // of types allows us to catch circular references to instantiations of the same anonymous type + if (!context.symbolStack) { + context.symbolStack = []; + } + context.symbolStack.push(symbol); + const result = createTypeNodeFromObjectType(type); + context.symbolStack.pop(); + return result; + } + } + else { + // Anonymous types without a symbol are never circular. + return createTypeNodeFromObjectType(type); + } + + function shouldWriteTypeOfFunctionSymbol() { + const isStaticMethodSymbol = !!(symbol.flags & SymbolFlags.Method && // typeof static method + forEach(symbol.declarations, declaration => getModifierFlags(declaration) & ModifierFlags.Static)); + const isNonLocalFunctionSymbol = !!(symbol.flags & SymbolFlags.Function) && + (symbol.parent || // is exported function symbol + forEach(symbol.declarations, declaration => + declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock)); + if (isStaticMethodSymbol || isNonLocalFunctionSymbol) { + // typeof is allowed only for static/non local functions + return contains(context.symbolStack, symbol); // it is type of the symbol uses itself recursively + } + } + } + + function createTypeNodeFromObjectType(type: ObjectType): TypeNode { + if (type.objectFlags & ObjectFlags.Mapped) { + if (getConstraintTypeFromMappedType(type).flags & (TypeFlags.TypeParameter | TypeFlags.Index)) { + return createMappedTypeNodeFromType(type); + } + } + + const resolved = resolveStructuredTypeMembers(type); + if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { + if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { + return createTypeLiteralNode(/*members*/ undefined); + } + + if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { + const signature = resolved.callSignatures[0]; + return signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType); + } + if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { + const signature = resolved.constructSignatures[0]; + return signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType); + } + } + + const saveInObjectTypeLiteral = context.inObjectTypeLiteral; + context.inObjectTypeLiteral = true; + const members = createTypeNodesFromResolvedType(resolved); + context.inObjectTypeLiteral = saveInObjectTypeLiteral; + return createTypeLiteralNode(members); + } + + function createTypeQueryNodeFromSymbol(symbol: Symbol) { + const entityName = symbolToName(symbol, /*expectsIdentifier*/ false); + return createTypeQueryNode(entityName); + } + + function typeReferenceToTypeNode(type: TypeReference) { + const typeArguments: Type[] = type.typeArguments || emptyArray; + if (type.target === globalArrayType) { + const elementType = typeToTypeNodeHelper(typeArguments[0]); + return createArrayTypeNode(elementType); + } + else if (type.target.objectFlags & ObjectFlags.Tuple) { + if (typeArguments.length > 0) { + const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type))); + if (tupleConstituentNodes && tupleConstituentNodes.length > 0) { + return createTupleTypeNode(tupleConstituentNodes); + } + } + if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowEmptyTuple)) { + context.encounteredError = true; + } + return undefined; + } + else { + const outerTypeParameters = type.target.outerTypeParameters; + let i = 0; + let qualifiedName: QualifiedName | undefined = undefined; + if (outerTypeParameters) { + const length = outerTypeParameters.length; + while (i < length) { + // Find group of type arguments for type parameters with the same declaring container. + const start = i; + const parent = getParentSymbolOfTypeParameter(outerTypeParameters[i]); + do { + i++; + } while (i < length && getParentSymbolOfTypeParameter(outerTypeParameters[i]) === parent); + // When type parameters are their own type arguments for the whole group (i.e. we have + // the default outer type arguments), we don't show the group. + if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { + const qualifiedNamePart = symbolToName(parent, /*expectsIdentifier*/ true); + if (!qualifiedName) { + qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined); + } + else { + Debug.assert(!qualifiedName.right); + qualifiedName.right = qualifiedNamePart; + qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined); + } + } + } + } + let entityName: EntityName = undefined; + const nameIdentifier = symbolToName(type.symbol, /*expectsIdentifier*/ true); + if (qualifiedName) { + Debug.assert(!qualifiedName.right); + qualifiedName.right = nameIdentifier; + entityName = qualifiedName; + } + else { + entityName = nameIdentifier; + } + const typeParameterCount = (type.target.typeParameters || emptyArray).length; + const typeArgumentNodes = some(typeArguments) ? mapToTypeNodeArray(typeArguments.slice(i, typeParameterCount - i)) : undefined; + return createTypeReferenceNode(entityName, typeArgumentNodes); + } + } + + function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] { + const typeElements: TypeElement[] = []; + for (const signature of resolvedType.callSignatures) { + typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.CallSignature)); + } + for (const signature of resolvedType.constructSignatures) { + typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature)); + } + if (resolvedType.stringIndexInfo) { + typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.stringIndexInfo, IndexKind.String)); + } + if (resolvedType.numberIndexInfo) { + typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.numberIndexInfo, IndexKind.Number)); + } + + const properties = resolvedType.properties; + if (!properties) { + return typeElements; + } + + for (const propertySymbol of properties) { + const propertyType = getTypeOfSymbol(propertySymbol); + const oldDeclaration = propertySymbol.declarations && propertySymbol.declarations[0] as TypeElement; + if (!oldDeclaration) { + return; + } + const propertyName = oldDeclaration.name; + const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined; + if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) { + const signatures = getSignaturesOfType(propertyType, SignatureKind.Call); + for (const signature of signatures) { + const methodDeclaration = signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature); + methodDeclaration.name = propertyName; + methodDeclaration.questionToken = optionalToken; + typeElements.push(methodDeclaration); + } + } + else { + + // TODO(aozgaa): should we create a node with explicit or implict any? + const propertyTypeNode = propertyType ? typeToTypeNodeHelper(propertyType) : createKeywordTypeNode(SyntaxKind.AnyKeyword); + typeElements.push(createPropertySignature( + propertyName, + optionalToken, + propertyTypeNode, + /*initializer*/undefined)); + } + } + return typeElements.length ? typeElements : undefined; + } + } + + function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind): IndexSignatureDeclaration { + const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); + const name = getNameFromIndexInfo(indexInfo); + + const indexingParameter = createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + name, + /*questionToken*/ undefined, + indexerTypeNode, + /*initializer*/ undefined); + const typeNode = typeToTypeNodeHelper(indexInfo.type); + return createIndexSignatureDeclaration( + /*decorators*/ undefined, + indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined, + [indexingParameter], + typeNode); + } + + function signatureToSignatureDeclarationHelper(signature: Signature, kind: SyntaxKind): SignatureDeclaration { + + const typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter)); + const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter)); + let returnTypeNode: TypeNode | TypePredicate; + if (signature.typePredicate) { + const typePredicate = signature.typePredicate; + const parameterName = typePredicate.kind === TypePredicateKind.Identifier ? createIdentifier((typePredicate).parameterName) : createThisTypeNode(); + const typeNode = typeToTypeNodeHelper(typePredicate.type); + returnTypeNode = createTypePredicateNode(parameterName, typeNode); + } + else { + const returnType = getReturnTypeOfSignature(signature); + returnTypeNode = returnType && typeToTypeNodeHelper(returnType); + } + const returnTypeNodeExceptAny = returnTypeNode && returnTypeNode.kind !== SyntaxKind.AnyKeyword ? returnTypeNode : undefined; + + return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNodeExceptAny); + } + + function typeParameterToDeclaration(type: TypeParameter): TypeParameterDeclaration { + const constraint = getConstraintFromTypeParameter(type); + const constraintNode = constraint && typeToTypeNodeHelper(constraint); + const defaultParameter = getDefaultFromTypeParameter(type); + const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter); + const name = symbolToName(type.symbol, /*expectsIdentifier*/ true); + return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode); + } + + function symbolToParameterDeclaration(parameterSymbol: Symbol): ParameterDeclaration { + const parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter); + const parameterType = getTypeOfSymbol(parameterSymbol); + const parameterTypeNode = typeToTypeNodeHelper(parameterType); + // TODO(aozgaa): In the future, check initializer accessibility. + const parameterNode = createParameter( + parameterDeclaration.decorators, + parameterDeclaration.modifiers, + parameterDeclaration.dotDotDotToken && createToken(SyntaxKind.DotDotDotToken), + // Clone name to remove trivia. + getSynthesizedClone(parameterDeclaration.name), + parameterDeclaration.questionToken && createToken(SyntaxKind.QuestionToken), + parameterTypeNode, + parameterDeclaration.initializer); + return parameterNode; + } + + function symbolToName(symbol: Symbol, expectsIdentifier: true): Identifier; + function symbolToName(symbol: Symbol, expectsIdentifier: false): EntityName; + function symbolToName(symbol: Symbol, expectsIdentifier: boolean): EntityName { + + // Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration. + let chain: Symbol[]; + const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter; + if (!isTypeParameter && context.enclosingDeclaration) { + chain = getSymbolChain(symbol, SymbolFlags.None, /*endOfChain*/ true); + Debug.assert(chain && chain.length > 0); + } + else { + chain = [symbol]; + } + + if (expectsIdentifier && chain.length !== 1 + && !context.encounteredError + && !(context.flags & NodeBuilderFlags.allowQualifedNameInPlaceOfIdentifier)) { + context.encounteredError = true; + } + return createEntityNameFromSymbolChain(chain, chain.length - 1); + + function createEntityNameFromSymbolChain(chain: Symbol[], index: number): EntityName { + Debug.assert(chain && 0 <= index && index < chain.length); + // const parentIndex = index - 1; + const symbol = chain[index]; + let typeParameterString = ""; + if (index > 0) { + + const parentSymbol = chain[index - 1]; + let typeParameters: TypeParameter[]; + if (getCheckFlags(symbol) & CheckFlags.Instantiated) { + typeParameters = getTypeParametersOfClassOrInterface(parentSymbol); + } + else { + const targetSymbol = getTargetSymbol(parentSymbol); + if (targetSymbol.flags & (SymbolFlags.Class | SymbolFlags.Interface | SymbolFlags.TypeAlias)) { + typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); + } + } + if (typeParameters && typeParameters.length > 0) { + if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowTypeParameterInQualifiedName)) { + context.encounteredError = true; + } + const writer = getSingleLineStringWriter(); + const displayBuilder = getSymbolDisplayBuilder(); + displayBuilder.buildDisplayForTypeParametersAndDelimiters(typeParameters, writer, context.enclosingDeclaration, 0); + typeParameterString = writer.string(); + releaseStringWriter(writer); + + } + } + const symbolName = getNameOfSymbol(symbol); + const symbolNameWithTypeParameters = typeParameterString.length > 0 ? `${symbolName}<${typeParameterString}>` : symbolName; + const identifier = createIdentifier(symbolNameWithTypeParameters); + + return index > 0 ? createQualifiedName(createEntityNameFromSymbolChain(chain, index - 1), identifier) : identifier; + } + + /** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */ + function getSymbolChain(symbol: Symbol, meaning: SymbolFlags, endOfChain: boolean): Symbol[] | undefined { + let accessibleSymbolChain = getAccessibleSymbolChain(symbol, context.enclosingDeclaration, meaning, /*useOnlyExternalAliasing*/false); + let parentSymbol: Symbol; + + if (!accessibleSymbolChain || + needsQualification(accessibleSymbolChain[0], context.enclosingDeclaration, accessibleSymbolChain.length === 1 ? meaning : getQualifiedLeftMeaning(meaning))) { + + // Go up and add our parent. + const parent = getParentOfSymbol(accessibleSymbolChain ? accessibleSymbolChain[0] : symbol); + if (parent) { + const parentChain = getSymbolChain(parent, getQualifiedLeftMeaning(meaning), /*endOfChain*/ false); + if (parentChain) { + parentSymbol = parent; + accessibleSymbolChain = parentChain.concat(accessibleSymbolChain || [symbol]); + } + } + } + + if (accessibleSymbolChain) { + return accessibleSymbolChain; + } + if ( + // If this is the last part of outputting the symbol, always output. The cases apply only to parent symbols. + endOfChain || + // If a parent symbol is an external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.) + !(!parentSymbol && ts.forEach(symbol.declarations, hasExternalModuleSymbol)) && + // If a parent symbol is an anonymous type, don't write it. + !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral))) { + + return [symbol]; + } + } + + function getNameOfSymbol(symbol: Symbol): string { + const declaration = firstOrUndefined(symbol.declarations); + if (declaration) { + if (declaration.name) { + return declarationNameToString(declaration.name); + } + if (declaration.parent && declaration.parent.kind === SyntaxKind.VariableDeclaration) { + return declarationNameToString((declaration.parent).name); + } + if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowAnonymousIdentifier)) { + context.encounteredError = true; + } + switch (declaration.kind) { + case SyntaxKind.ClassExpression: + return "(Anonymous class)"; + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + return "(Anonymous function)"; + } + } + return symbol.name; + } + } + } + function typePredicateToString(typePredicate: TypePredicate, enclosingDeclaration?: Declaration, flags?: TypeFormatFlags): string { const writer = getSingleLineStringWriter(); getSymbolDisplayBuilder().buildTypePredicateDisplay(typePredicate, writer, enclosingDeclaration, flags); @@ -2466,6 +3048,7 @@ namespace ts { } } + function writeTypeList(types: Type[], delimiter: SyntaxKind) { for (let i = 0; i < types.length; i++) { if (i > 0) { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 453cd04114..66b3eb713e 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -45,7 +45,7 @@ namespace ts { * Creates a shallow, memberwise clone of a node with no source map location. */ /* @internal */ - export function getSynthesizedClone(node: T): T { + export function getSynthesizedClone(node: T | undefined): T { // We don't use "clone" from core.ts here, as we need to preserve the prototype chain of // the original node. We also need to exclude specific properties and only include own- // properties (to skip members already defined on the shared prototype). @@ -166,23 +166,23 @@ namespace ts { // Reserved words export function createSuper() { - return createSynthesizedNode(SyntaxKind.SuperKeyword); + return createSynthesizedNode(SyntaxKind.SuperKeyword); } export function createThis() { - return createSynthesizedNode(SyntaxKind.ThisKeyword); + return >createSynthesizedNode(SyntaxKind.ThisKeyword); } export function createNull() { - return createSynthesizedNode(SyntaxKind.NullKeyword); + return >createSynthesizedNode(SyntaxKind.NullKeyword); } export function createTrue() { - return createSynthesizedNode(SyntaxKind.TrueKeyword); + return >createSynthesizedNode(SyntaxKind.TrueKeyword); } export function createFalse() { - return createSynthesizedNode(SyntaxKind.FalseKeyword); + return >createSynthesizedNode(SyntaxKind.FalseKeyword); } // Names @@ -213,6 +213,284 @@ namespace ts { : node; } + // Type Elements + + export function createSignatureDeclaration(kind: SyntaxKind, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined) { + const signatureDeclaration = createSynthesizedNode(kind) as SignatureDeclaration; + signatureDeclaration.typeParameters = asNodeArray(typeParameters); + signatureDeclaration.parameters = asNodeArray(parameters); + signatureDeclaration.type = type; + return signatureDeclaration; + } + + function updateSignatureDeclaration(node: SignatureDeclaration, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined) { + return node.typeParameters !== typeParameters + || node.parameters !== parameters + || node.type !== type + ? updateNode(createSignatureDeclaration(node.kind, typeParameters, parameters, type), node) + : node; + } + + export function createFunctionTypeNode(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined) { + return createSignatureDeclaration(SyntaxKind.FunctionType, typeParameters, parameters, type) as FunctionTypeNode; + } + + export function updateFunctionTypeNode(node: FunctionTypeNode, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined) { + return updateSignatureDeclaration(node, typeParameters, parameters, type); + } + + export function createConstructorTypeNode(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined) { + return createSignatureDeclaration(SyntaxKind.ConstructorType, typeParameters, parameters, type) as ConstructorTypeNode; + } + + export function updateConstructorTypeNode(node: ConstructorTypeNode, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined) { + return updateSignatureDeclaration(node, typeParameters, parameters, type); + } + + export function createCallSignatureDeclaration(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined) { + return createSignatureDeclaration(SyntaxKind.CallSignature, typeParameters, parameters, type) as CallSignatureDeclaration; + } + + export function updateCallSignatureDeclaration(node: CallSignatureDeclaration, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined) { + return updateSignatureDeclaration(node, typeParameters, parameters, type); + } + + export function createConstructSignatureDeclaration(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined) { + return createSignatureDeclaration(SyntaxKind.ConstructSignature, typeParameters, parameters, type) as ConstructSignatureDeclaration; + } + + export function updateConstructSignatureDeclaration(node: ConstructSignatureDeclaration, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined) { + return updateSignatureDeclaration(node, typeParameters, parameters, type); + } + + export function createMethodSignature(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, name: string | PropertyName, questionToken: QuestionToken | undefined) { + const methodSignature = createSignatureDeclaration(SyntaxKind.MethodSignature, typeParameters, parameters, type) as MethodSignature; + methodSignature.name = asName(name); + methodSignature.questionToken = questionToken; + return methodSignature; + } + + export function updateMethodSignature(node: MethodSignature, typeParameters: NodeArray | undefined, parameters: NodeArray, type: TypeNode | undefined, name: PropertyName, questionToken: QuestionToken | undefined) { + return node.typeParameters !== typeParameters + || node.parameters !== parameters + || node.type !== type + || node.name !== name + || node.questionToken !== questionToken + ? updateNode(createMethodSignature(typeParameters, parameters, type, name, questionToken), node) + : node; + } + + // Types + + export function createKeywordTypeNode(kind: KeywordTypeNode["kind"]) { + return createSynthesizedNode(kind); + } + + export function createThisTypeNode() { + return createSynthesizedNode(SyntaxKind.ThisType); + } + + export function createLiteralTypeNode(literal: Expression) { + const literalTypeNode = createSynthesizedNode(SyntaxKind.LiteralType) as LiteralTypeNode; + literalTypeNode.literal = literal; + return literalTypeNode; + } + + export function updateLiteralTypeNode(node: LiteralTypeNode, literal: Expression) { + return node.literal !== literal + ? updateNode(createLiteralTypeNode(literal), node) + : node; + } + + export function createTypeReferenceNode(typeName: string | EntityName, typeArguments: TypeNode[] | undefined) { + const typeReference = createSynthesizedNode(SyntaxKind.TypeReference) as TypeReferenceNode; + typeReference.typeName = asName(typeName); + typeReference.typeArguments = asNodeArray(typeArguments); + return typeReference; + } + + export function updateTypeReferenceNode(node: TypeReferenceNode, typeName: EntityName, typeArguments: NodeArray | undefined) { + return node.typeName !== typeName + || node.typeArguments !== typeArguments + ? updateNode(createTypeReferenceNode(typeName, typeArguments), node) + : node; + } + + export function createTypePredicateNode(parameterName: Identifier | ThisTypeNode | string, type: TypeNode) { + const typePredicateNode = createSynthesizedNode(SyntaxKind.TypePredicate) as TypePredicateNode; + typePredicateNode.parameterName = asName(parameterName); + typePredicateNode.type = type; + return typePredicateNode; + } + + export function updateTypePredicateNode(node: TypePredicateNode, parameterName: Identifier | ThisTypeNode, type: TypeNode) { + return node.parameterName !== parameterName + || node.type !== type + ? updateNode(createTypePredicateNode(parameterName, type), node) + : node; + } + + export function createTypeQueryNode(exprName: EntityName) { + const typeQueryNode = createSynthesizedNode(SyntaxKind.TypeQuery) as TypeQueryNode; + typeQueryNode.exprName = exprName; + return typeQueryNode; + } + + export function updateTypeQueryNode(node: TypeQueryNode, exprName: EntityName) { + return node.exprName !== exprName ? updateNode(createTypeQueryNode(exprName), node) : node; + } + + export function createArrayTypeNode(elementType: TypeNode) { + const arrayTypeNode = createSynthesizedNode(SyntaxKind.ArrayType) as ArrayTypeNode; + arrayTypeNode.elementType = elementType; + return arrayTypeNode; + } + + export function updateArrayTypeNode(node: ArrayTypeNode, elementType: TypeNode): ArrayTypeNode { + return node.elementType !== elementType + ? updateNode(createArrayTypeNode(elementType), node) + : node; + } + + export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType, types: TypeNode[]): UnionTypeNode; + export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.IntersectionType, types: TypeNode[]): IntersectionTypeNode; + export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: TypeNode[]): UnionOrIntersectionTypeNode; + export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: TypeNode[]) { + const unionTypeNode = createSynthesizedNode(kind) as UnionTypeNode | IntersectionTypeNode; + unionTypeNode.types = createNodeArray(types); + return unionTypeNode; + } + + export function updateUnionOrIntersectionTypeNode(node: UnionOrIntersectionTypeNode, types: NodeArray) { + return node.types !== types + ? updateNode(createUnionOrIntersectionTypeNode(node.kind, types), node) + : node; + } + + export function createTypeLiteralNode(members: TypeElement[]) { + const typeLiteralNode = createSynthesizedNode(SyntaxKind.TypeLiteral) as TypeLiteralNode; + typeLiteralNode.members = createNodeArray(members); + return typeLiteralNode; + } + + export function updateTypeLiteralNode(node: TypeLiteralNode, members: NodeArray) { + return node.members !== members + ? updateNode(createTypeLiteralNode(members), node) + : node; + } + + export function createTupleTypeNode(elementTypes: TypeNode[]) { + const tupleTypeNode = createSynthesizedNode(SyntaxKind.TupleType) as TupleTypeNode; + tupleTypeNode.elementTypes = createNodeArray(elementTypes); + return tupleTypeNode; + } + + export function updateTypleTypeNode(node: TupleTypeNode, elementTypes: TypeNode[]) { + return node.elementTypes !== elementTypes + ? updateNode(createTupleTypeNode(elementTypes), node) + : node; + } + + export function createMappedTypeNode(readonlyToken: ReadonlyToken | undefined, typeParameter: TypeParameterDeclaration, questionToken: QuestionToken | undefined, type: TypeNode | undefined): MappedTypeNode { + const mappedTypeNode = createSynthesizedNode(SyntaxKind.MappedType) as MappedTypeNode; + mappedTypeNode.readonlyToken = readonlyToken; + mappedTypeNode.typeParameter = typeParameter; + mappedTypeNode.questionToken = questionToken; + mappedTypeNode.type = type; + return mappedTypeNode; + } + + export function updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyToken | undefined, typeParameter: TypeParameterDeclaration, questionToken: QuestionToken | undefined, type: TypeNode | undefined): MappedTypeNode { + return node.readonlyToken !== readonlyToken + || node.typeParameter !== typeParameter + || node.questionToken !== questionToken + || node.type !== type + ? updateNode(createMappedTypeNode(readonlyToken, typeParameter, questionToken, type), node) + : node; + } + + export function createTypeOperatorNode(type: TypeNode) { + const typeOperatorNode = createSynthesizedNode(SyntaxKind.TypeOperator) as TypeOperatorNode; + typeOperatorNode.operator = SyntaxKind.KeyOfKeyword; + typeOperatorNode.type = type; + return typeOperatorNode; + } + + export function updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode) { + return node.type !== type ? updateNode(createTypeOperatorNode(type), node) : node; + } + + export function createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode) { + const indexedAccessTypeNode = createSynthesizedNode(SyntaxKind.IndexedAccessType) as IndexedAccessTypeNode; + indexedAccessTypeNode.objectType = objectType; + indexedAccessTypeNode.indexType = indexType; + return indexedAccessTypeNode; + } + + export function updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode) { + return node.objectType !== objectType + || node.indexType !== indexType + ? updateNode(createIndexedAccessTypeNode(objectType, indexType), node) + : node; + } + + // Type Declarations + + export function createTypeParameterDeclaration(name: string | Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined) { + const typeParameter = createSynthesizedNode(SyntaxKind.TypeParameter) as TypeParameterDeclaration; + typeParameter.name = asName(name); + typeParameter.constraint = constraint; + typeParameter.default = defaultType; + + return typeParameter; + } + + export function updateTypeParameterDeclaration(node: TypeParameterDeclaration, name: Identifier, constraint: TypeNode | undefined, defaultType: TypeNode | undefined) { + return node.name !== name + || node.constraint !== constraint + || node.default !== defaultType + ? updateNode(createTypeParameterDeclaration(name, constraint, defaultType), node) + : node; + } + + // Signature elements + + export function createPropertySignature(name: PropertyName | string, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined): PropertySignature { + const propertySignature = createSynthesizedNode(SyntaxKind.PropertySignature) as PropertySignature; + propertySignature.name = asName(name); + propertySignature.questionToken = questionToken; + propertySignature.type = type; + propertySignature.initializer = initializer; + return propertySignature; + } + + export function updatePropertySignature(node: PropertySignature, name: PropertyName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined) { + return node.name !== name + || node.questionToken !== questionToken + || node.type !== type + || node.initializer !== initializer + ? updateNode(createPropertySignature(name, questionToken, type, initializer), node) + : node; + } + + export function createIndexSignatureDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, parameters: ParameterDeclaration[], type: TypeNode): IndexSignatureDeclaration { + const indexSignature = createSynthesizedNode(SyntaxKind.IndexSignature) as IndexSignatureDeclaration; + indexSignature.decorators = asNodeArray(decorators); + indexSignature.modifiers = asNodeArray(modifiers); + indexSignature.parameters = createNodeArray(parameters); + indexSignature.type = type; + return indexSignature; + } + + export function updateIndexSignatureDeclaration(node: IndexSignatureDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, parameters: ParameterDeclaration[], type: TypeNode) { + return node.parameters !== parameters + || node.type !== type + || node.decorators !== decorators + || node.modifiers !== modifiers + ? updateNode(createIndexSignatureDeclaration(decorators, modifiers, parameters, type), node) + : node; + } + // Signature elements export function createParameter(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression) { @@ -227,11 +505,12 @@ namespace ts { return node; } - export function updateParameter(node: ParameterDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: BindingName, type: TypeNode | undefined, initializer: Expression | undefined) { + export function updateParameter(node: ParameterDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined) { return node.decorators !== decorators || node.modifiers !== modifiers || node.dotDotDotToken !== dotDotDotToken || node.name !== name + || node.questionToken !== questionToken || node.type !== type || node.initializer !== initializer ? updateNode(createParameter(decorators, modifiers, dotDotDotToken, name, node.questionToken, type, initializer), node) @@ -273,12 +552,13 @@ namespace ts { : node; } - export function createMethod(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: string | PropertyName, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined) { + export function createMethodDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: string | PropertyName, questionToken: QuestionToken | undefined, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined) { const node = createSynthesizedNode(SyntaxKind.MethodDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); node.asteriskToken = asteriskToken; node.name = asName(name); + node.questionToken = questionToken; node.typeParameters = asNodeArray(typeParameters); node.parameters = createNodeArray(parameters); node.type = type; @@ -286,7 +566,7 @@ namespace ts { return node; } - export function updateMethod(node: MethodDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: PropertyName, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined) { + export function updateMethod(node: MethodDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: PropertyName, questionToken: QuestionToken | undefined, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined) { return node.decorators !== decorators || node.modifiers !== modifiers || node.asteriskToken !== asteriskToken @@ -295,7 +575,7 @@ namespace ts { || node.parameters !== parameters || node.type !== type || node.body !== body - ? updateNode(createMethod(decorators, modifiers, asteriskToken, name, typeParameters, parameters, type, body), node) + ? updateNode(createMethodDeclaration(decorators, modifiers, asteriskToken, name, questionToken, typeParameters, parameters, type, body), node) : node; } @@ -1099,10 +1379,6 @@ namespace ts { : node; } - export function createKeywordTypeNode(kind: KeywordTypeNode["kind"]): KeywordTypeNode { - return createSynthesizedNode(kind); - } - export function createFunctionDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: string | Identifier | undefined, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined) { const node = createSynthesizedNode(SyntaxKind.FunctionDeclaration); node.decorators = asNodeArray(decorators); @@ -1793,7 +2069,9 @@ namespace ts { function asName(name: string | Identifier): Identifier; function asName(name: string | BindingName): BindingName; function asName(name: string | PropertyName): PropertyName; - function asName(name: string | Identifier | BindingName | PropertyName) { + function asName(name: string | EntityName): EntityName; + function asName(name: string | Identifier | ThisTypeNode): Identifier | ThisTypeNode; + function asName(name: string | Identifier | BindingName | PropertyName | QualifiedName | ThisTypeNode) { return typeof name === "string" ? createIdentifier(name) : name; } diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 31aae5055a..088c57866d 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -125,6 +125,7 @@ namespace ts { visitNodes(node.modifiers, visitor, isModifier), node.asteriskToken, node.name, + /*questionToken*/ undefined, /*typeParameters*/ undefined, visitParameterList(node.parameters, visitor, context), /*type*/ undefined, diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 971dddd20e..83e84e6eab 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -475,6 +475,7 @@ namespace ts { /*modifiers*/ undefined, node.dotDotDotToken, getGeneratedNameForNode(node), + /*questionToken*/ undefined, /*type*/ undefined, visitNode(node.initializer, visitor, isExpression) ); @@ -540,6 +541,7 @@ namespace ts { ? undefined : node.asteriskToken, visitNode(node.name, visitor, isPropertyName), + visitNode(/*questionToken*/ undefined, visitor, isToken), /*typeParameters*/ undefined, visitParameterList(node.parameters, visitor, context), /*type*/ undefined, diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 885f8fad89..6dc643b954 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -2049,6 +2049,7 @@ namespace ts { visitNodes(node.modifiers, modifierVisitor, isModifier), node.asteriskToken, visitPropertyNameOfClassElement(node), + /*questionToken*/ undefined, /*typeParameters*/ undefined, visitParameterList(node.parameters, visitor, context), /*type*/ undefined, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ceeb2922d4..038f8bf316 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -819,7 +819,7 @@ namespace ts { body?: FunctionBody; } - // For when we encounter a semicolon in a class declaration. ES6 allows these as class elements. + /** For when we encounter a semicolon in a class declaration. ES6 allows these as class elements.*/ export interface SemicolonClassElement extends ClassElement { kind: SyntaxKind.SemicolonClassElement; parent?: ClassDeclaration | ClassExpression; @@ -861,7 +861,11 @@ namespace ts { | SyntaxKind.BooleanKeyword | SyntaxKind.StringKeyword | SyntaxKind.SymbolKeyword - | SyntaxKind.VoidKeyword; + | SyntaxKind.ThisKeyword + | SyntaxKind.VoidKeyword + | SyntaxKind.UndefinedKeyword + | SyntaxKind.NullKeyword + | SyntaxKind.NeverKeyword; } export interface ThisTypeNode extends TypeNode { @@ -1030,15 +1034,15 @@ namespace ts { _primaryExpressionBrand: any; } - export interface NullLiteral extends PrimaryExpression { + export interface NullLiteral extends PrimaryExpression, TypeNode { kind: SyntaxKind.NullKeyword; } - export interface BooleanLiteral extends PrimaryExpression { + export interface BooleanLiteral extends PrimaryExpression, TypeNode { kind: SyntaxKind.TrueKeyword | SyntaxKind.FalseKeyword; } - export interface ThisExpression extends PrimaryExpression { + export interface ThisExpression extends PrimaryExpression, KeywordTypeNode { kind: SyntaxKind.ThisKeyword; } @@ -2472,6 +2476,14 @@ namespace ts { /* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type; getNonNullableType(type: Type): Type; + /** Note that the resulting nodes cannot be checked. */ + + typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): TypeNode; + /** Note that the resulting nodes cannot be checked. */ + signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): SignatureDeclaration; + /** Note that the resulting nodes cannot be checked. */ + indexInfoToIndexSignatureDeclaration(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): IndexSignatureDeclaration; + getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; getSymbolAtLocation(node: Node): Symbol; getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): Symbol[]; @@ -2523,6 +2535,16 @@ namespace ts { /* @internal */ getTypeCount(): number; } + export enum NodeBuilderFlags { + None = 0, + allowThisInObjectLiteral = 1 << 0, + allowQualifedNameInPlaceOfIdentifier = 1 << 1, + allowTypeParameterInQualifiedName = 1 << 2, + allowAnonymousIdentifier = 1 << 3, + allowEmptyUnionOrIntersection = 1 << 4, + allowEmptyTuple = 1 << 5 + } + export interface SymbolDisplayBuilder { buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; buildSymbolDisplay(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void; @@ -4073,7 +4095,7 @@ namespace ts { export type Transformer = (node: T) => T; /** - * A function that accepts and possible transforms a node. + * A function that accepts and possibly transforms a node. */ export type Visitor = (node: Node) => VisitResult; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 48d90bbca5..33cb0d8f48 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -525,6 +525,10 @@ namespace ts { return getFullWidth(name) === 0 ? "(Missing)" : getTextOfNode(name); } + export function getNameFromIndexInfo(info: IndexInfo) { + return info.declaration ? declarationNameToString(info.declaration.parameters[0].name) : undefined; + } + export function getTextOfPropertyName(name: PropertyName): string { switch (name.kind) { case SyntaxKind.Identifier: @@ -740,8 +744,8 @@ namespace ts { // // let a: A.B.C; // - // Calling isPartOfTypeNode would consider the qualified name A.B a type node. Only C or - // A.B.C is a type node. + // Calling isPartOfTypeNode would consider the qualified name A.B a type node. + // Only C and A.B.C are type nodes. if (SyntaxKind.FirstTypeNode <= parent.kind && parent.kind <= SyntaxKind.LastTypeNode) { return true; } @@ -3713,10 +3717,14 @@ namespace ts { return (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) || kind === SyntaxKind.AnyKeyword || kind === SyntaxKind.NumberKeyword + || kind === SyntaxKind.ObjectKeyword || kind === SyntaxKind.BooleanKeyword || kind === SyntaxKind.StringKeyword || kind === SyntaxKind.SymbolKeyword + || kind === SyntaxKind.ThisKeyword || kind === SyntaxKind.VoidKeyword + || kind === SyntaxKind.UndefinedKeyword + || kind === SyntaxKind.NullKeyword || kind === SyntaxKind.NeverKeyword || kind === SyntaxKind.ExpressionWithTypeArguments; } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 133c62e192..297d2cc0dd 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -204,21 +204,17 @@ namespace ts { * @param visitor The callback used to visit each child. * @param context A lexical environment context for the visitor. */ - export function visitEachChild(node: T | undefined, visitor: Visitor, context: TransformationContext, nodesVisitor?: typeof visitNodes): T | undefined; + export function visitEachChild(node: T | undefined, visitor: Visitor, context: TransformationContext, nodesVisitor?: typeof visitNodes, tokenVisitor?: Visitor): T | undefined; - export function visitEachChild(node: Node, visitor: Visitor, context: TransformationContext, nodesVisitor = visitNodes): Node { + export function visitEachChild(node: Node, visitor: Visitor, context: TransformationContext, nodesVisitor = visitNodes, tokenVisitor?: Visitor): Node { if (node === undefined) { return undefined; } const kind = node.kind; - // No need to visit nodes with no children. - if ((kind > SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken)) { - return node; - } - // We do not yet support types. - if ((kind >= SyntaxKind.TypePredicate && kind <= SyntaxKind.LiteralType)) { + // No need to visit nodes with no children. + if ((kind > SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken) || kind === SyntaxKind.ThisType) { return node; } @@ -227,6 +223,8 @@ namespace ts { case SyntaxKind.EmptyStatement: case SyntaxKind.OmittedExpression: case SyntaxKind.DebuggerStatement: + case SyntaxKind.EndOfDeclarationMarker: + case SyntaxKind.MissingDeclaration: // No need to visit nodes with no children. return node; @@ -240,13 +238,53 @@ namespace ts { return updateComputedPropertyName(node, visitNode((node).expression, visitor, isExpression)); - // Signature elements + // Signatures and Signature Elements + case SyntaxKind.FunctionType: + return updateFunctionTypeNode(node, + nodesVisitor((node).typeParameters, visitor, isTypeParameter), + visitParameterList((node).parameters, visitor, context, nodesVisitor), + visitNode((node).type, visitor, isTypeNode)); + + case SyntaxKind.ConstructorType: + return updateConstructorTypeNode(node, + nodesVisitor((node).typeParameters, visitor, isTypeParameter), + visitParameterList((node).parameters, visitor, context, nodesVisitor), + visitNode((node).type, visitor, isTypeNode)); + + case SyntaxKind.CallSignature: + return updateCallSignatureDeclaration(node, + nodesVisitor((node).typeParameters, visitor, isTypeParameter), + visitParameterList((node).parameters, visitor, context, nodesVisitor), + visitNode((node).type, visitor, isTypeNode)); + + case SyntaxKind.ConstructSignature: + return updateConstructSignatureDeclaration(node, + nodesVisitor((node).typeParameters, visitor, isTypeParameter), + visitParameterList((node).parameters, visitor, context, nodesVisitor), + visitNode((node).type, visitor, isTypeNode)); + + case SyntaxKind.MethodSignature: + return updateMethodSignature(node, + nodesVisitor((node).typeParameters, visitor, isTypeParameter), + visitParameterList((node).parameters, visitor, context, nodesVisitor), + visitNode((node).type, visitor, isTypeNode), + visitNode((node).name, visitor, isPropertyName), + visitNode((node).questionToken, tokenVisitor, isToken)); + + case SyntaxKind.IndexSignature: + return updateIndexSignatureDeclaration(node, + nodesVisitor((node).decorators, visitor, isDecorator), + nodesVisitor((node).modifiers, visitor, isModifier), + visitParameterList((node).parameters, visitor, context, nodesVisitor), + visitNode((node).type, visitor, isTypeNode)); + case SyntaxKind.Parameter: return updateParameter(node, nodesVisitor((node).decorators, visitor, isDecorator), nodesVisitor((node).modifiers, visitor, isModifier), - (node).dotDotDotToken, + visitNode((node).dotDotDotToken, tokenVisitor, isToken), visitNode((node).name, visitor, isBindingName), + visitNode((node).questionToken, tokenVisitor, isToken), visitNode((node).type, visitor, isTypeNode), visitNode((node).initializer, visitor, isExpression)); @@ -254,7 +292,74 @@ namespace ts { return updateDecorator(node, visitNode((node).expression, visitor, isExpression)); - // Type member + // Types + + case SyntaxKind.TypeReference: + return updateTypeReferenceNode(node, + visitNode((node).typeName, visitor, isEntityName), + nodesVisitor((node).typeArguments, visitor, isTypeNode)); + + case SyntaxKind.TypePredicate: + return updateTypePredicateNode(node, + visitNode((node).parameterName, visitor), + visitNode((node).type, visitor, isTypeNode)); + + case SyntaxKind.TypeQuery: + return updateTypeQueryNode((node), visitNode((node).exprName, visitor, isEntityName)); + + case SyntaxKind.TypeLiteral: + return updateTypeLiteralNode((node), nodesVisitor((node).members, visitor)); + + case SyntaxKind.ArrayType: + return updateArrayTypeNode(node, visitNode((node).elementType, visitor, isTypeNode)); + + case SyntaxKind.TupleType: + return updateTypleTypeNode((node), nodesVisitor((node).elementTypes, visitor, isTypeNode)); + + case SyntaxKind.UnionType: + case SyntaxKind.IntersectionType: + return updateUnionOrIntersectionTypeNode(node, + nodesVisitor((node).types, visitor, isTypeNode)); + + case SyntaxKind.ParenthesizedType: + Debug.fail("not implemented."); + + case SyntaxKind.TypeOperator: + return updateTypeOperatorNode(node, visitNode((node).type, visitor, isTypeNode)); + + case SyntaxKind.IndexedAccessType: + return updateIndexedAccessTypeNode((node), + visitNode((node).objectType, visitor, isTypeNode), + visitNode((node).indexType, visitor, isTypeNode)); + + case SyntaxKind.MappedType: + return updateMappedTypeNode((node), + visitNode((node).readonlyToken, tokenVisitor, isToken), + visitNode((node).typeParameter, visitor, isTypeParameter), + visitNode((node).questionToken, tokenVisitor, isToken), + visitNode((node).type, visitor, isTypeNode)); + + case SyntaxKind.LiteralType: + return updateLiteralTypeNode(node, + visitNode((node).literal, visitor, isExpression)); + + // Type Declarations + + case SyntaxKind.TypeParameter: + return updateTypeParameterDeclaration(node, + visitNode((node).name, visitor, isIdentifier), + visitNode((node).constraint, visitor, isTypeNode), + visitNode((node).default, visitor, isTypeNode)); + + // Type members + + case SyntaxKind.PropertySignature: + return updatePropertySignature((node), + visitNode((node).name, visitor, isPropertyName), + visitNode((node).questionToken, tokenVisitor, isToken), + visitNode((node).type, visitor, isTypeNode), + visitNode((node).initializer, visitor, isExpression)); + case SyntaxKind.PropertyDeclaration: return updateProperty(node, nodesVisitor((node).decorators, visitor, isDecorator), @@ -267,8 +372,9 @@ namespace ts { return updateMethod(node, nodesVisitor((node).decorators, visitor, isDecorator), nodesVisitor((node).modifiers, visitor, isModifier), - (node).asteriskToken, + visitNode((node).asteriskToken, tokenVisitor, isToken), visitNode((node).name, visitor, isPropertyName), + visitNode((node).questionToken, tokenVisitor, isToken), nodesVisitor((node).typeParameters, visitor, isTypeParameter), visitParameterList((node).parameters, visitor, context, nodesVisitor), visitNode((node).type, visitor, isTypeNode), @@ -309,7 +415,7 @@ namespace ts { case SyntaxKind.BindingElement: return updateBindingElement(node, - (node).dotDotDotToken, + visitNode((node).dotDotDotToken, tokenVisitor, isToken), visitNode((node).propertyName, visitor, isPropertyName), visitNode((node).name, visitor, isBindingName), visitNode((node).initializer, visitor, isExpression)); @@ -362,7 +468,7 @@ namespace ts { case SyntaxKind.FunctionExpression: return updateFunctionExpression(node, nodesVisitor((node).modifiers, visitor, isModifier), - (node).asteriskToken, + visitNode((node).asteriskToken, tokenVisitor, isToken), visitNode((node).name, visitor, isIdentifier), nodesVisitor((node).typeParameters, visitor, isTypeParameter), visitParameterList((node).parameters, visitor, context, nodesVisitor), @@ -419,7 +525,7 @@ namespace ts { case SyntaxKind.YieldExpression: return updateYield(node, - (node).asteriskToken, + visitNode((node).asteriskToken, tokenVisitor, isToken), visitNode((node).expression, visitor, isExpression)); case SyntaxKind.SpreadElement: @@ -555,7 +661,7 @@ namespace ts { return updateFunctionDeclaration(node, nodesVisitor((node).decorators, visitor, isDecorator), nodesVisitor((node).modifiers, visitor, isModifier), - (node).asteriskToken, + visitNode((node).asteriskToken, tokenVisitor, isToken), visitNode((node).name, visitor, isIdentifier), nodesVisitor((node).typeParameters, visitor, isTypeParameter), visitParameterList((node).parameters, visitor, context, nodesVisitor), diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 6ae2ba3f51..bfb931e866 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -31,37 +31,55 @@ namespace ts.codefix { return undefined; } - let typeString = "any"; + let typeNode: TypeNode; if (token.parent.parent.kind === SyntaxKind.BinaryExpression) { const binaryExpression = token.parent.parent as BinaryExpression; const checker = context.program.getTypeChecker(); const widenedType = checker.getWidenedType(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(binaryExpression.right))); - typeString = checker.typeToString(widenedType); + typeNode = checker.typeToTypeNode(widenedType, classDeclaration) || typeNode; } - const startPos = classDeclaration.members.pos; + typeNode = typeNode ? typeNode : createKeywordTypeNode(SyntaxKind.AnyKeyword); + + const openBrace = getOpenBraceOfClassLike(classDeclaration, sourceFile); + + const property = createProperty( + /*decorators*/undefined, + /*modifiers*/ undefined, + token.getText(sourceFile), + /*questionToken*/ undefined, + typeNode, + /*initializer*/ undefined); + const propertyChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context); + propertyChangeTracker.insertNodeAfter(sourceFile, openBrace, property, { suffix: context.newLineCharacter }); + + const stringTypeNode = createKeywordTypeNode(SyntaxKind.StringKeyword); + const indexingParameter = createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + "x", + /*questionToken*/ undefined, + stringTypeNode, + /*initializer*/ undefined); + const indexSignature = createIndexSignatureDeclaration( + /*decorators*/undefined, + /*modifiers*/ undefined, + [indexingParameter], + typeNode); + + const indexSignatureChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context); + indexSignatureChangeTracker.insertNodeAfter(sourceFile, openBrace, indexSignature, { suffix: context.newLineCharacter }); return [{ description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_property_0), [token.getText()]), - changes: [{ - fileName: sourceFile.fileName, - textChanges: [{ - span: { start: startPos, length: 0 }, - newText: `${token.getFullText(sourceFile)}: ${typeString};` - }] - }] + changes: propertyChangeTracker.getChanges() }, { description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_missing_property_0), [token.getText()]), - changes: [{ - fileName: sourceFile.fileName, - textChanges: [{ - span: { start: startPos, length: 0 }, - newText: `[name: string]: ${typeString};` - }] - }] + changes: indexSignatureChangeTracker.getChanges() }]; } } \ No newline at end of file diff --git a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts index 16edce0a51..62f89b1a21 100644 --- a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts +++ b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts @@ -19,10 +19,9 @@ namespace ts.codefix { const checker = context.program.getTypeChecker(); if (isClassLike(token.parent)) { - const classDecl = token.parent as ClassLikeDeclaration; - const startPos = classDecl.members.pos; + const classDeclaration = token.parent as ClassLikeDeclaration; - const extendsNode = getClassExtendsHeritageClauseElement(classDecl); + const extendsNode = getClassExtendsHeritageClauseElement(classDeclaration); const instantiatedExtendsType = checker.getTypeAtLocation(extendsNode); // Note that this is ultimately derived from a map indexed by symbol names, @@ -30,18 +29,12 @@ namespace ts.codefix { const extendsSymbols = checker.getPropertiesOfType(instantiatedExtendsType); const abstractAndNonPrivateExtendsSymbols = extendsSymbols.filter(symbolPointsToNonPrivateAndAbstractMember); - const insertion = getMissingMembersInsertion(classDecl, abstractAndNonPrivateExtendsSymbols, checker, context.newLineCharacter); - - if (insertion.length) { + const newNodes = createMissingMemberNodes(classDeclaration, abstractAndNonPrivateExtendsSymbols, checker); + const changes = newNodesToChanges(newNodes, getOpenBraceOfClassLike(classDeclaration, sourceFile), context); + if (changes && changes.length > 0) { return [{ description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class), - changes: [{ - fileName: sourceFile.fileName, - textChanges: [{ - span: { start: startPos, length: 0 }, - newText: insertion - }] - }] + changes }]; } } diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 67b2242c8d..3b1b99febb 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -11,14 +11,14 @@ namespace ts.codefix { const token = getTokenAtPosition(sourceFile, start); const checker = context.program.getTypeChecker(); - const classDecl = getContainingClass(token); - if (!classDecl) { + const classDeclaration = getContainingClass(token); + if (!classDeclaration) { return undefined; } - const startPos: number = classDecl.members.pos; - const classType = checker.getTypeAtLocation(classDecl) as InterfaceType; - const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDecl); + const openBrace = getOpenBraceOfClassLike(classDeclaration, sourceFile); + const classType = checker.getTypeAtLocation(classDeclaration) as InterfaceType; + const implementedTypeNodes = getClassImplementsHeritageClauseElements(classDeclaration); const hasNumericIndexSignature = !!checker.getIndexTypeOfType(classType, IndexKind.Number); const hasStringIndexSignature = !!checker.getIndexTypeOfType(classType, IndexKind.String); @@ -31,43 +31,36 @@ namespace ts.codefix { const implementedTypeSymbols = checker.getPropertiesOfType(implementedType); const nonPrivateMembers = implementedTypeSymbols.filter(symbol => !(getModifierFlags(symbol.valueDeclaration) & ModifierFlags.Private)); - let insertion = getMissingIndexSignatureInsertion(implementedType, IndexKind.Number, classDecl, hasNumericIndexSignature); - insertion += getMissingIndexSignatureInsertion(implementedType, IndexKind.String, classDecl, hasStringIndexSignature); - insertion += getMissingMembersInsertion(classDecl, nonPrivateMembers, checker, context.newLineCharacter); - + let newNodes: Node[] = []; + createAndAddMissingIndexSignatureDeclaration(implementedType, IndexKind.Number, hasNumericIndexSignature, newNodes); + createAndAddMissingIndexSignatureDeclaration(implementedType, IndexKind.String, hasStringIndexSignature, newNodes); + newNodes = newNodes.concat(createMissingMemberNodes(classDeclaration, nonPrivateMembers, checker)); const message = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Implement_interface_0), [implementedTypeNode.getText()]); - if (insertion) { - pushAction(result, insertion, message); + if (newNodes.length > 0) { + pushAction(result, newNodes, message); } } return result; - function getMissingIndexSignatureInsertion(type: InterfaceType, kind: IndexKind, enclosingDeclaration: ClassLikeDeclaration, hasIndexSigOfKind: boolean) { - if (!hasIndexSigOfKind) { - const IndexInfoOfKind = checker.getIndexInfoOfType(type, kind); - if (IndexInfoOfKind) { - const writer = getSingleLineStringWriter(); - checker.getSymbolDisplayBuilder().buildIndexSignatureDisplay(IndexInfoOfKind, writer, kind, enclosingDeclaration); - const result = writer.string(); - releaseStringWriter(writer); - - return result; - } + function createAndAddMissingIndexSignatureDeclaration(type: InterfaceType, kind: IndexKind, hasIndexSigOfKind: boolean, newNodes: Node[]): void { + if (hasIndexSigOfKind) { + return; } - return ""; + + const indexInfoOfKind = checker.getIndexInfoOfType(type, kind); + + if (!indexInfoOfKind) { + return; + } + const newIndexSignatureDeclaration = checker.indexInfoToIndexSignatureDeclaration(indexInfoOfKind, kind, classDeclaration); + newNodes.push(newIndexSignatureDeclaration); } - function pushAction(result: CodeAction[], insertion: string, description: string): void { + function pushAction(result: CodeAction[], newNodes: Node[], description: string): void { const newAction: CodeAction = { description: description, - changes: [{ - fileName: sourceFile.fileName, - textChanges: [{ - span: { start: startPos, length: 0 }, - newText: insertion - }] - }] + changes: newNodesToChanges(newNodes, openBrace, context) }; result.push(newAction); } diff --git a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts index 6d7efb64c6..fc2be3cd0c 100644 --- a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts +++ b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts @@ -16,7 +16,7 @@ namespace ts.codefix { return undefined; } - // figure out if the this access is actuall inside the supercall + // figure out if the `this` access is actually inside the supercall // i.e. super(this.a), since in that case we won't suggest a fix if (superCall.expression && superCall.expression.kind == SyntaxKind.CallExpression) { const arguments = (superCall.expression).arguments; diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index d20fc0129c..b836815fc2 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -1,47 +1,88 @@ /* @internal */ namespace ts.codefix { + export function newNodesToChanges(newNodes: Node[], insertAfter: Node, context: CodeFixContext) { + const sourceFile = context.sourceFile; + + const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context); + + for (const newNode of newNodes) { + changeTracker.insertNodeAfter(sourceFile, insertAfter, newNode, { suffix: context.newLineCharacter }); + } + + const changes = changeTracker.getChanges(); + if (!some(changes)) { + return changes; + } + + Debug.assert(changes.length === 1); + const consolidatedChanges: FileTextChanges[] = [{ + fileName: changes[0].fileName, + textChanges: [{ + span: changes[0].textChanges[0].span, + newText: changes[0].textChanges.reduce((prev, cur) => prev + cur.newText, "") + }] + + }]; + return consolidatedChanges; + } + /** * Finds members of the resolved type that are missing in the class pointed to by class decl * and generates source code for the missing members. * @param possiblyMissingSymbols The collection of symbols to filter and then get insertions for. * @returns Empty string iff there are no member insertions. */ - export function getMissingMembersInsertion(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: Symbol[], checker: TypeChecker, newlineChar: string): string { + export function createMissingMemberNodes(classDeclaration: ClassLikeDeclaration, possiblyMissingSymbols: Symbol[], checker: TypeChecker): Node[] { const classMembers = classDeclaration.symbol.members; const missingMembers = possiblyMissingSymbols.filter(symbol => !classMembers.has(symbol.getName())); - let insertion = ""; - + let newNodes: Node[] = []; for (const symbol of missingMembers) { - insertion = insertion.concat(getInsertionForMemberSymbol(symbol, classDeclaration, checker, newlineChar)); + const newNode = createNewNodeForMemberSymbol(symbol, classDeclaration, checker); + if (newNode) { + if (Array.isArray(newNode)) { + newNodes = newNodes.concat(newNode); + } + else { + newNodes.push(newNode); + } + } } - return insertion; + return newNodes; } /** * @returns Empty string iff there we can't figure out a representation for `symbol` in `enclosingDeclaration`. */ - function getInsertionForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker, newlineChar: string): string { + function createNewNodeForMemberSymbol(symbol: Symbol, enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker): Node[] | Node | undefined { const declarations = symbol.getDeclarations(); if (!(declarations && declarations.length)) { - return ""; + return undefined; } const declaration = declarations[0] as Declaration; - const name = declaration.name ? declaration.name.getText() : undefined; - const visibility = getVisibilityPrefixWithSpace(getModifierFlags(declaration)); - - const type = checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration); + // Clone name to remove leading trivia. + const name = getSynthesizedClone(declaration.name); + const visibilityModifier = createVisibilityModifier(getModifierFlags(declaration)); + const modifiers = visibilityModifier ? createNodeArray([visibilityModifier]) : undefined; + const type = checker.getWidenedType(checker.getTypeOfSymbolAtLocation(symbol, enclosingDeclaration)); + const optional = !!(symbol.flags & SymbolFlags.Optional); switch (declaration.kind) { case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.PropertySignature: case SyntaxKind.PropertyDeclaration: - const typeString = checker.typeToString(type, enclosingDeclaration, TypeFormatFlags.None); - return `${visibility}${name}: ${typeString};${newlineChar}`; - + const typeNode = checker.typeToTypeNode(type, enclosingDeclaration); + const property = createProperty( + /*decorators*/undefined, + modifiers, + name, + optional ? createToken(SyntaxKind.QuestionToken) : undefined, + typeNode, + /*initializer*/ undefined); + return property; case SyntaxKind.MethodSignature: case SyntaxKind.MethodDeclaration: // The signature for the implementation appears as an entry in `signatures` iff @@ -52,109 +93,142 @@ namespace ts.codefix { // (eg: an abstract method or interface declaration), there is a 1-1 // correspondence of declarations and signatures. const signatures = checker.getSignaturesOfType(type, SignatureKind.Call); - if (!(signatures && signatures.length > 0)) { - return ""; + if (!some(signatures)) { + return undefined; } + if (declarations.length === 1) { Debug.assert(signatures.length === 1); - const sigString = checker.signatureToString(signatures[0], enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call); - return getStubbedMethod(visibility, name, sigString, newlineChar); + const signature = signatures[0]; + return signatureToMethodDeclaration(signature, enclosingDeclaration, createStubbedMethodBody()); } - let result = ""; + const signatureDeclarations: MethodDeclaration[] = []; for (let i = 0; i < signatures.length; i++) { - const sigString = checker.signatureToString(signatures[i], enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call); - result += `${visibility}${name}${sigString};${newlineChar}`; + const signature = signatures[i]; + const methodDeclaration = signatureToMethodDeclaration(signature, enclosingDeclaration); + if (methodDeclaration) { + signatureDeclarations.push(methodDeclaration); + } } - // If there is a declaration with a body, it is the last declaration, - // and it isn't caught by `getSignaturesOfType`. - let bodySig: Signature | undefined = undefined; if (declarations.length > signatures.length) { - bodySig = checker.getSignatureFromDeclaration(declarations[declarations.length - 1] as SignatureDeclaration); + const signature = checker.getSignatureFromDeclaration(declarations[declarations.length - 1] as SignatureDeclaration); + const methodDeclaration = signatureToMethodDeclaration(signature, enclosingDeclaration, createStubbedMethodBody()); + if (methodDeclaration) { + signatureDeclarations.push(methodDeclaration); + } } else { Debug.assert(declarations.length === signatures.length); - bodySig = createBodySignatureWithAnyTypes(signatures, enclosingDeclaration, checker); + const methodImplementingSignatures = createMethodImplementingSignatures(signatures, name, optional, modifiers); + signatureDeclarations.push(methodImplementingSignatures); } - const sigString = checker.signatureToString(bodySig, enclosingDeclaration, TypeFormatFlags.SuppressAnyReturnType, SignatureKind.Call); - result += getStubbedMethod(visibility, name, sigString, newlineChar); - - return result; + return signatureDeclarations; default: - return ""; + return undefined; + } + + function signatureToMethodDeclaration(signature: Signature, enclosingDeclaration: Node, body?: Block) { + const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration); + if (signatureDeclaration) { + signatureDeclaration.decorators = undefined; + signatureDeclaration.modifiers = modifiers; + signatureDeclaration.name = name; + signatureDeclaration.questionToken = optional ? createToken(SyntaxKind.QuestionToken) : undefined; + signatureDeclaration.body = body; + } + return signatureDeclaration; } } - function createBodySignatureWithAnyTypes(signatures: Signature[], enclosingDeclaration: ClassLikeDeclaration, checker: TypeChecker): Signature { - const newSignatureDeclaration = createNode(SyntaxKind.CallSignature) as SignatureDeclaration; - newSignatureDeclaration.parent = enclosingDeclaration; - newSignatureDeclaration.name = signatures[0].getDeclaration().name; - - let maxNonRestArgs = -1; - let maxArgsIndex = 0; + function createMethodImplementingSignatures(signatures: Signature[], name: PropertyName, optional: boolean, modifiers: Modifier[] | undefined): MethodDeclaration { + /** This is *a* signature with the maximal number of arguments, + * such that if there is a "maximal" signature without rest arguments, + * this is one of them. + */ + let maxArgsSignature = signatures[0]; let minArgumentCount = signatures[0].minArgumentCount; - let hasRestParameter = false; + let someSigHasRestParameter = false; for (let i = 0; i < signatures.length; i++) { const sig = signatures[i]; minArgumentCount = Math.min(sig.minArgumentCount, minArgumentCount); - hasRestParameter = hasRestParameter || sig.hasRestParameter; - const nonRestLength = sig.parameters.length - (sig.hasRestParameter ? 1 : 0); - if (nonRestLength > maxNonRestArgs) { - maxNonRestArgs = nonRestLength; - maxArgsIndex = i; + if (sig.hasRestParameter) { + someSigHasRestParameter = true; + } + if (sig.parameters.length >= maxArgsSignature.parameters.length && (!sig.hasRestParameter || maxArgsSignature.hasRestParameter)) { + maxArgsSignature = sig; } } - const maxArgsParameterSymbolNames = signatures[maxArgsIndex].getParameters().map(symbol => symbol.getName()); + const maxNonRestArgs = maxArgsSignature.parameters.length - (maxArgsSignature.hasRestParameter ? 1 : 0); + const maxArgsParameterSymbolNames = maxArgsSignature.parameters.map(symbol => symbol.getName()); - const optionalToken = createToken(SyntaxKind.QuestionToken); - - newSignatureDeclaration.parameters = createNodeArray(); + const parameters: ParameterDeclaration[] = []; for (let i = 0; i < maxNonRestArgs; i++) { - const newParameter = createParameterDeclarationWithoutType(i, minArgumentCount, newSignatureDeclaration); - newSignatureDeclaration.parameters.push(newParameter); + const anyType = createKeywordTypeNode(SyntaxKind.AnyKeyword); + const newParameter = createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + maxArgsParameterSymbolNames[i], + /*questionToken*/ i >= minArgumentCount ? createToken(SyntaxKind.QuestionToken) : undefined, + anyType, + /*initializer*/ undefined); + parameters.push(newParameter); } - if (hasRestParameter) { - const restParameter = createParameterDeclarationWithoutType(maxNonRestArgs, minArgumentCount, newSignatureDeclaration); - restParameter.dotDotDotToken = createToken(SyntaxKind.DotDotDotToken); - newSignatureDeclaration.parameters.push(restParameter); + if (someSigHasRestParameter) { + const anyArrayType = createArrayTypeNode(createKeywordTypeNode(SyntaxKind.AnyKeyword)); + const restParameter = createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + createToken(SyntaxKind.DotDotDotToken), + maxArgsParameterSymbolNames[maxNonRestArgs] || "rest", + /*questionToken*/ maxNonRestArgs >= minArgumentCount ? createToken(SyntaxKind.QuestionToken) : undefined, + anyArrayType, + /*initializer*/ undefined); + parameters.push(restParameter); } - return checker.getSignatureFromDeclaration(newSignatureDeclaration); - - function createParameterDeclarationWithoutType(index: number, minArgCount: number, enclosingSignatureDeclaration: SignatureDeclaration): ParameterDeclaration { - const newParameter = createNode(SyntaxKind.Parameter) as ParameterDeclaration; - - newParameter.symbol = new SymbolConstructor(SymbolFlags.FunctionScopedVariable, maxArgsParameterSymbolNames[index] || "rest"); - newParameter.symbol.valueDeclaration = newParameter; - newParameter.symbol.declarations = [newParameter]; - newParameter.parent = enclosingSignatureDeclaration; - if (index >= minArgCount) { - newParameter.questionToken = optionalToken; - } - - return newParameter; - } + return createStubbedMethod( + modifiers, + name, + optional, + /*typeParameters*/undefined, + parameters, + /*returnType*/ undefined); } - export function getStubbedMethod(visibility: string, name: string, sigString = "()", newlineChar: string): string { - return `${visibility}${name}${sigString}${getMethodBodyStub(newlineChar)}`; + export function createStubbedMethod(modifiers: Modifier[], name: PropertyName, optional: boolean, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], returnType: TypeNode | undefined) { + return createMethodDeclaration( + /*decorators*/ undefined, + modifiers, + /*asteriskToken*/ undefined, + name, + optional ? createToken(SyntaxKind.QuestionToken) : undefined, + typeParameters, + parameters, + returnType, + createStubbedMethodBody()); } - function getMethodBodyStub(newlineChar: string) { - return ` {${newlineChar}throw new Error('Method not implemented.');${newlineChar}}${newlineChar}`; + function createStubbedMethodBody() { + return createBlock( + [createThrow( + createNew( + createIdentifier("Error"), + /*typeArguments*/undefined, + [createLiteral("Method not implemented.")]))], + /*multiline*/true); } - function getVisibilityPrefixWithSpace(flags: ModifierFlags): string { + function createVisibilityModifier(flags: ModifierFlags) { if (flags & ModifierFlags.Public) { - return "public "; + return createToken(SyntaxKind.PublicKeyword); } else if (flags & ModifierFlags.Protected) { - return "protected "; + return createToken(SyntaxKind.ProtectedKeyword); } - return ""; + return undefined; } - - const SymbolConstructor = objectAllocator.getSymbolConstructor(); } \ No newline at end of file diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 424399db72..afc9892749 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -274,9 +274,9 @@ namespace ts.textChanges { } /** - * This function should be used to insert nodes in lists when nodes don't carry separators as the part of the node range, - * i.e. arguments in arguments lists, parameters in parameter lists etc. Statements or class elements are different in sense that - * for them separators are treated as the part of the node. + * This function should be used to insert nodes in lists when nodes don't carry separators as the part of the node range, + * i.e. arguments in arguments lists, parameters in parameter lists etc. + * Note that separators are part of the node in statements and class elements. */ public insertNodeInListAfter(sourceFile: SourceFile, after: Node, newNode: Node) { const containingList = formatting.SmartIndenter.getContainingList(after, sourceFile); @@ -484,7 +484,7 @@ namespace ts.textChanges { private static normalize(changes: Change[]) { // order changes by start position const normalized = stableSort(changes, (a, b) => a.range.pos - b.range.pos); - // verify that end position of the change is less than start position of the next change + // verify that change intervals do not overlap, except possibly at end points. for (let i = 0; i < normalized.length - 2; i++) { Debug.assert(normalized[i].range.end <= normalized[i + 1].range.pos); } @@ -549,7 +549,7 @@ namespace ts.textChanges { }; function assignPositionsToNode(node: Node): Node { - const visited = visitEachChild(node, assignPositionsToNode, nullTransformationContext, assignPositionsToNodeArray); + const visited = visitEachChild(node, assignPositionsToNode, nullTransformationContext, assignPositionsToNodeArray, assignPositionsToNode); // create proxy node for non synthesized nodes const newNode = nodeIsSynthesized(visited) ? visited diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 1b32b3bbad..92b6365657 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1384,4 +1384,8 @@ namespace ts { // First token is the open curly, this is where we want to put the 'super' call. return constructor.body.getFirstToken(sourceFile); } + + export function getOpenBraceOfClassLike(declaration: ClassLikeDeclaration, sourceFile: SourceFile) { + return getTokenAtPosition(sourceFile, declaration.members.pos - 1); + } } diff --git a/tests/cases/fourslash/codeFixClassExprClassImplementClassFunctionVoidInferred.ts b/tests/cases/fourslash/codeFixClassExprClassImplementClassFunctionVoidInferred.ts index bdf8ea3bd2..2568111fb9 100644 --- a/tests/cases/fourslash/codeFixClassExprClassImplementClassFunctionVoidInferred.ts +++ b/tests/cases/fourslash/codeFixClassExprClassImplementClassFunctionVoidInferred.ts @@ -8,6 +8,6 @@ verify.rangeAfterCodeFix(` f(): void{ - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts b/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts index 4bddfb799f..bc437c93bc 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts @@ -28,4 +28,4 @@ verify.rangeAfterCodeFix(` e: this; f: A; g: string; -`); \ No newline at end of file +`); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts index 344a7ee3f2..7e51e2216d 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts @@ -5,6 +5,7 @@ //// abstract f(a: number, b: string): this; //// abstract f(a: string, b: number): Function; //// abstract f(a: string): Function; +//// abstract foo(): number; //// } //// //// class C extends A {[| |]} @@ -15,6 +16,9 @@ verify.rangeAfterCodeFix(` f(a: string, b: number): Function; f(a: string): Function; f(a: any, b?: any) { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); + } + foo(): number { + throw new Error("Method not implemented."); } `); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethodThis.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethodThis.ts index 55b3ad4b77..a462ac9812 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethodThis.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethodThis.ts @@ -8,6 +8,6 @@ verify.rangeAfterCodeFix(` f(): this { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateNumber.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateNumber.ts index bdf198b89e..c1a55e8803 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateNumber.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateNumber.ts @@ -4,10 +4,9 @@ //// abstract f(x: T): T; //// } //// -//// class C extends A {[| -//// |]} +//// class C extends A {[| |]} verify.rangeAfterCodeFix(`f(x: number): number{ - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateU.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateU.ts index 647a533c1c..c2ec5ff803 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateU.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateU.ts @@ -4,10 +4,9 @@ //// abstract f(x: T): T; //// } //// -//// class C extends A {[| -//// |]} +//// class C extends A {[| |]} verify.rangeAfterCodeFix(`f(x: U): U{ - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractPrivateProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractPrivateProperty.ts index d395272b36..b8f6b3d81e 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractPrivateProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractPrivateProperty.ts @@ -4,8 +4,7 @@ //// private abstract x: number; //// } //// -//// class C extends A {[| -//// |]} +//// class C extends A {[| |]} // We don't know how to fix this problem. We can: // 1) Make x protected, and then insert. diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts index b7300acf5a..701903d677 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts @@ -4,17 +4,12 @@ //// abstract x: number; //// abstract y: this; //// abstract z: A; -//// abstract foo(): number; //// } //// -//// class C extends A {[| -//// |]} +//// class C extends A {[| |]} verify.rangeAfterCodeFix(` x: number; y: this; z: A; - foo(): number { - throw new Error('Method not implemented.'); - } `); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts index 6d0571fce6..d2532ef16d 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts @@ -4,8 +4,7 @@ //// protected abstract x: number; //// } //// -//// class C extends A {[| -//// |]} +//// class C extends A {[| |]} verify.rangeAfterCodeFix(` protected x: number; diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts index 917f75051f..495d661ec7 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts @@ -4,8 +4,8 @@ //// public abstract x: number; //// } //// -//// class C extends A {[| -//// |]} +//// class C extends A {[| |]} + verify.rangeAfterCodeFix(` public x: number; diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractSomePropertiesPresent.ts b/tests/cases/fourslash/codeFixClassExtendAbstractSomePropertiesPresent.ts index b6dd679bb7..fb66a04cd5 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractSomePropertiesPresent.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractSomePropertiesPresent.ts @@ -6,8 +6,8 @@ //// abstract z: number; //// } //// -//// class C extends A {[| |] -//// constructor(public x: number) { super(); } +//// class C extends A {[| +//// |]constructor(public x: number) { super(); } //// y: number; //// } diff --git a/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts b/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts index d9b9f57d4d..b8fdace9f7 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts @@ -8,6 +8,6 @@ verify.rangeAfterCodeFix(` f(): void{ - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); diff --git a/tests/cases/fourslash/codeFixClassImplementClassMemberAnonymousClass.ts b/tests/cases/fourslash/codeFixClassImplementClassMemberAnonymousClass.ts new file mode 100644 index 0000000000..ee8b8cafff --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementClassMemberAnonymousClass.ts @@ -0,0 +1,13 @@ +/// + +//// class A { +//// foo() { +//// return class { x: number; } +//// } +//// bar() { +//// return new class { x: number; } +//// } +//// } +//// class C implements A {[| |]} + +verify.not.codeFixAvailable(); diff --git a/tests/cases/fourslash/codeFixClassImplementClassMethodViaHeritage.ts b/tests/cases/fourslash/codeFixClassImplementClassMethodViaHeritage.ts index 4365a16053..57372dd7e8 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassMethodViaHeritage.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassMethodViaHeritage.ts @@ -8,11 +8,11 @@ //// //// } //// -//// class C3 implements C2 {[| +//// class C3 implements C2 {[| //// |]f2(){} //// } verify.rangeAfterCodeFix(`f1(): void{ - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); diff --git a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts index 82ecde6c41..2e8dc316b3 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts @@ -9,6 +9,6 @@ verify.rangeAfterCodeFix(` method(a: number, b: string): boolean; method(a: string | number, b?: string | number): boolean | Function { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); diff --git a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts index f187e0ca0f..463b10c450 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts @@ -13,6 +13,6 @@ verify.rangeAfterCodeFix(` method(a: string, b: number): Function; method(a: string): Function; method(a: string | number, b?: string | number): boolean | Function { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); diff --git a/tests/cases/fourslash/codeFixClassImplementClassPropertyTypeQuery.ts b/tests/cases/fourslash/codeFixClassImplementClassPropertyTypeQuery.ts new file mode 100644 index 0000000000..4a5663a8ba --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementClassPropertyTypeQuery.ts @@ -0,0 +1,10 @@ +/// + +//// class A { +//// A: typeof A; +//// } +//// class D implements A {[| |]} + +verify.rangeAfterCodeFix(` + A: typeof A; +`); diff --git a/tests/cases/fourslash/codeFixClassImplementDeepInheritance.ts b/tests/cases/fourslash/codeFixClassImplementDeepInheritance.ts index e8b199b21a..f11dc29395 100644 --- a/tests/cases/fourslash/codeFixClassImplementDeepInheritance.ts +++ b/tests/cases/fourslash/codeFixClassImplementDeepInheritance.ts @@ -24,8 +24,7 @@ //// } //// //// interface I6 extends C4 {} -//// class C5 implements I6 {[| -//// |]} +//// class C5 implements I6 {[| |]} /** diff --git a/tests/cases/fourslash/codeFixClassImplementInterface36.ts b/tests/cases/fourslash/codeFixClassImplementInterface36.ts deleted file mode 100644 index f9fc48991f..0000000000 --- a/tests/cases/fourslash/codeFixClassImplementInterface36.ts +++ /dev/null @@ -1,20 +0,0 @@ -/// - -//// abstract class C1 { -//// -//// } -//// -//// abstract class C2 { -//// abstract f1(); -//// } -//// -//// interface I1 extends C1, C2 {} -//// -//// class C3 implements I1 {[| -//// -//// |]} - -verify.rangeAfterCodeFix(`f1(){ - throw new Error('Method not implemented.'); -} -`); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceArrayTuple.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceArrayTuple.ts new file mode 100644 index 0000000000..4550e5ca31 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceArrayTuple.ts @@ -0,0 +1,15 @@ +/// + +//// interface I { +//// x: number[]; +//// y: Array; +//// z: [number, string, I]; +//// } +//// +//// class C implements I {[| |]} + +verify.rangeAfterCodeFix(` +x: number[]; +y: number[]; +z: [number, string, I]; +`); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceCallSignature.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceCallSignature.ts new file mode 100644 index 0000000000..4b47268396 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceCallSignature.ts @@ -0,0 +1,10 @@ +/// + +//// interface I { +//// (x: number, b: string): number; +//// } +//// class C implements I {[| |]} + +verify.not.codeFixAvailable(); + + diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts new file mode 100644 index 0000000000..f082074f16 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts @@ -0,0 +1,27 @@ +/// + +// @lib: es2017 + +//// namespace N { +//// /**enum prefix */ +//// export enum /**enum identifier prefix */ E /**open-brace prefix*/ { +//// /* literal prefix */ a /** comma prefix */, +//// /* literal prefix */ b /** comma prefix */, +//// /* literal prefix */ c +//// /** close brace prefix */ } +//// /** interface prefix */ +//// export interface /**interface name prefix */ I /**open-brace prefix*/ { +//// /** property prefix */ a /** colon prefix */: /** enum literal prefix 1*/ E /** dot prefix */. /** enum literal prefix 2*/a; +//// /** property prefix */ b /** colon prefix */: /** enum prefix */ E; +//// /**method signature prefix */foo /**open angle prefix */< /**type parameter name prefix */ X /** closing angle prefix */> /**open paren prefix */(/** parameter prefix */ a/** colon prefix */: /** parameter type prefix */ X /** close paren prefix */) /** colon prefix */: /** return type prefix */ string /** semicolon prefix */; +//// /**close-brace prefix*/ } +//// /**close-brace prefix*/ } +//// class C implements N.I {[| |]} + +verify.rangeAfterCodeFix(` + a: N.E.a; + b: N.E; + foo(a: X): string { + throw new Error("Method not implemented."); + } +`); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts index 341b4e5981..7d77c5b9af 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts @@ -7,15 +7,15 @@ //// [2]: boolean; //// } //// -//// class C implements I {[| |]} +//// class C implements I {[| |]} verify.rangeAfterCodeFix(` ["foo"](o: any): boolean { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } ["x"]: boolean; [1](): string { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } [2]: boolean; `); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts index 7c1156acd7..31fc167bb9 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts @@ -17,35 +17,34 @@ //// [Symbol.toStringTag]: string; //// [Symbol.unscopables]: any; //// } -//// //// class C implements I {[| |]} verify.rangeAfterCodeFix(` [Symbol.hasInstance](o: any): boolean { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } [Symbol.isConcatSpreadable]: boolean; [Symbol.iterator]() { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } [Symbol.match]: boolean; [Symbol.replace](...args: {}) { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } [Symbol.search](str: string): number { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } [Symbol.species](): number { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } [Symbol.split](str: string, limit?: number): {} { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } [Symbol.toPrimitive](hint: "number"): number; [Symbol.toPrimitive](hint: "default"): number; [Symbol.toPrimitive](hint: "string"): string; [Symbol.toPrimitive](hint: any) { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } [Symbol.toStringTag]: string; [Symbol.unscopables]: any; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceConstructSignature.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceConstructSignature.ts new file mode 100644 index 0000000000..1700f7ec88 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceConstructSignature.ts @@ -0,0 +1,10 @@ +/// + +//// interface I { +//// new (x: number, b: string); +//// } +//// class C implements I {[| |]} + +verify.not.codeFixAvailable(); + + diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceDuplicateMember1.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceDuplicateMember1.ts index a2154de567..bea14ae404 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceDuplicateMember1.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceDuplicateMember1.ts @@ -7,7 +7,6 @@ //// x: number; //// } //// -//// class C implements I1,I2 {[| -//// |]} +//// class C implements I1,I2 {[| |]} verify.codeFixAvailable(); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceEmptyTypeLiteral.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceEmptyTypeLiteral.ts new file mode 100644 index 0000000000..9805901709 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceEmptyTypeLiteral.ts @@ -0,0 +1,13 @@ +/// + +//// interface I { +//// x: {}; +//// } +//// +//// class C implements I {[| +//// |]constructor() { } +//// } + +verify.rangeAfterCodeFix(` +x: {}; +`); diff --git a/tests/cases/fourslash/codeFixClassImplementInterface39.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceInNamespace.ts similarity index 67% rename from tests/cases/fourslash/codeFixClassImplementInterface39.ts rename to tests/cases/fourslash/codeFixClassImplementInterfaceInNamespace.ts index f81dea4978..e4e1cf3608 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterface39.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceInNamespace.ts @@ -9,10 +9,9 @@ //// f1(); //// } //// -//// class C1 implements N1.I1 {[| -//// |]} +//// class C1 implements N1.I1 {[| |]} verify.rangeAfterCodeFix(`f1(): string{ - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts index 0f14344253..82cc53570f 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts @@ -3,9 +3,7 @@ //// interface I { //// [x: number]: I; //// } -//// -//// class C implements I {[| -//// |]} +//// class C implements I {[| |]} verify.rangeAfterCodeFix(` [x: number]: I; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexType.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexType.ts new file mode 100644 index 0000000000..ed5ea06ebb --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexType.ts @@ -0,0 +1,10 @@ +/// + +//// interface I { +//// x: keyof X; +//// } +//// class C implements I {[| |]} + +verify.rangeAfterCodeFix(` +x: keyof Y; +`); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts new file mode 100644 index 0000000000..c141592823 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts @@ -0,0 +1,13 @@ +/// + +//// abstract class C1 { } +//// abstract class C2 { +//// abstract f1(); +//// } +//// interface I1 extends C1, C2 { } +//// class C3 implements I1 {[| |]} + +verify.rangeAfterCodeFix(`f1(){ + throw new Error("Method not implemented."); +} +`); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMappedType.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMappedType.ts new file mode 100644 index 0000000000..64676bce86 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMappedType.ts @@ -0,0 +1,10 @@ +/// + +//// interface I { +//// x: { readonly [K in keyof X]: X[K] }; +//// } +//// class C implements I {[| |]} + +verify.rangeAfterCodeFix(` +x: { readonly [K in keyof X]: Y[K]; }; +`); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts new file mode 100644 index 0000000000..dbfd623be1 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts @@ -0,0 +1,58 @@ +/// + +// @lib: es2017 + +//// /** asdf */ +//// interface I { +//// 1; +//// 2; +//// 3; +//// 4; +//// 5; +//// 6; +//// 7; +//// 8; +//// 9; +//// 10; +//// 11; +//// 12; +//// 13; +//// 14; +//// 15; +//// 16; +//// 17; +//// 18; +//// 19; +//// 20; +//// 21; +//// 22; +//// /** a nice safe prime */ +//// 23; +//// } +//// class C implements I {[| |]} + +verify.rangeAfterCodeFix(` + 1: any; + 2: any; + 3: any; + 4: any; + 5: any; + 6: any; + 7: any; + 8: any; + 9: any; + 10: any; + 11: any; + 12: any; + 13: any; + 14: any; + 15: any; + 16: any; + 17: any; + 18: any; + 19: any; + 20: any; + 21: any; + 22: any; + 23: any; +`); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts index 95cc3476bf..b8225f31e0 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts @@ -4,11 +4,10 @@ //// f(x: number, y: this): I //// } //// -//// class C implements I {[| -//// |]} +//// class C implements I {[| |]} verify.rangeAfterCodeFix(` f(x: number,y: this): I { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodTypePredicate.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodTypePredicate.ts new file mode 100644 index 0000000000..2eb6c2fabc --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodTypePredicate.ts @@ -0,0 +1,16 @@ +/// + +//// interface I { +//// f(i: any): i is I; +//// f(): this is I; +//// } +//// +//// class C implements I {[| |]} + +verify.rangeAfterCodeFix(` +f(i: any): i is I; +f(): this is I; +f(i?: any) { + throw new Error("Method not implemented."); +} +`); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements1.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements1.ts index c8d61227c4..13d58d0ad6 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements1.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements1.ts @@ -8,12 +8,11 @@ //// } //// //// class C implements I1,I2 {[| -//// y: number; -//// |]} +//// |]y: number; +//// } verify.rangeAfterCodeFix(` x: number; -y: number; `); verify.not.codeFixAvailable(); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts index d29b23998b..e5100b88f6 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts @@ -8,12 +8,11 @@ //// } //// //// class C implements I1,I2 {[| -//// x: number; -//// |]} +//// |]x: number; +//// } verify.rangeAfterCodeFix(` y: number; -x: number; `); verify.not.codeFixAvailable(); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplementsIntersection1.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplementsIntersection1.ts index 321d41e463..cac1038eb1 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplementsIntersection1.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplementsIntersection1.ts @@ -7,7 +7,6 @@ //// x: string; //// } //// -//// class C implements I1,I2 {[| -//// |]} +//// class C implements I1,I2 {[| |]} verify.codeFixAvailable(); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleMembersAndPunctuation.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleMembersAndPunctuation.ts index beab89c06a..66cdb1f545 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleMembersAndPunctuation.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleMembersAndPunctuation.ts @@ -9,20 +9,19 @@ //// h(); //// } //// -//// class C1 implements I1 {[| -//// |]} +//// class C1 implements I1 {[| |]} verify.rangeAfterCodeFix(` x: number; y: number; z: number; f() { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } g() { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } h() { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignatures.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignatures.ts index bda58b3767..583dda1305 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignatures.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignatures.ts @@ -13,6 +13,6 @@ verify.rangeAfterCodeFix(` method(a: string, b: number): Function; method(a: string): Function; method(a: any, b?: any) { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest1.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest1.ts index 6e0a272a42..132c63ae0f 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest1.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest1.ts @@ -13,6 +13,6 @@ verify.rangeAfterCodeFix(` method(a: string, ...b: number[]): Function; method(a: string): Function; method(a: any, ...b?: any[]) { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest2.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest2.ts index 7ba7ae2c98..b4e9a8ff91 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest2.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest2.ts @@ -13,6 +13,6 @@ verify.rangeAfterCodeFix(` method(a: string, b: number): Function; method(a: string): Function; method(a: any, b?: any, ...rest?: any[]) { - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceNamespaceConflict.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceNamespaceConflict.ts index 4fea72aa48..1d71ce3c47 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceNamespaceConflict.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceNamespaceConflict.ts @@ -8,9 +8,7 @@ //// interface I1 { //// f1(); //// } -//// -//// class C1 implements N1.I1 {[| -//// |]} +//// class C1 implements N1.I1 {[| |]} verify.rangeAfterCodeFix(` x: number; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceProperty.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceProperty.ts new file mode 100644 index 0000000000..53cfce5ff6 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceProperty.ts @@ -0,0 +1,19 @@ +/// + +// @lib: es2017 + +//// enum E { a,b,c } +//// interface I { +//// x: E; +//// y: E.a +//// z: symbol; +//// w: object; +//// } +//// class C implements I {[| |]} + +verify.rangeAfterCodeFix(` + x: E; + y: E.a; + z: symbol; + w: object; +`); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfacePropertyFromParentConstructorFunction.ts b/tests/cases/fourslash/codeFixClassImplementInterfacePropertyFromParentConstructorFunction.ts index 2a4ca4df92..acda59513a 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfacePropertyFromParentConstructorFunction.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfacePropertyFromParentConstructorFunction.ts @@ -4,7 +4,7 @@ //// constructor(public x: number) { } //// } //// -//// class B implements A {[| |]} +//// class B implements A {[| |]} verify.not.codeFixAvailable(); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts b/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts new file mode 100644 index 0000000000..9438241005 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts @@ -0,0 +1,34 @@ +/// + +//// interface I { +//// a0: {}; +//// a1: { (b1: number, c1: string): number; }; +//// a2: (b2: number, c2: string) => number; +//// a3: { (b3: number, c3: string): number, x: number }; +//// +//// a4: { new (b1: number, c1: string): number; }; +//// a5: new (b2: number, c2: string) => number; +//// a6: { new (b3: number, c3: string): number, x: number }; +//// +//// a7: { foo(b7: number, c7: string): number }; +//// +//// a8: { (b81: number, c81: string): number, new (b82: number, c82: string): number; }; +//// +//// a9: { (b9: number, c9: string): number; [d9: number]: I }; +//// a10: { (b10: number, c10: string): number; [d10: string]: I }; +//// } +//// class C implements I {[| |]} + +verify.rangeAfterCodeFix(` + a0: {}; + a1: (b1: number, c1: string) => number; + a2: (b2: number, c2: string) => number; + a3: { (b3: number, c3: string): number; x: number; }; + a4: new (b1: number, c1: string) => number; + a5: new (b2: number, c2: string) => number; + a6: { new (b3: number, c3: string): number; x: number; }; + a7: { foo(b7: number, c7: string): number; }; + a8: { (b81: number, c81: string): number; new (b82: number, c82: string): number; }; + a9: { (b9: number, c9: string): number; [d9: number]: I; }; + a10: { (b10: number, c10: string): number; [d10: string]: I; }; +`); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceQualifiedName.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceQualifiedName.ts new file mode 100644 index 0000000000..e29f4501b1 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceQualifiedName.ts @@ -0,0 +1,12 @@ +/// + +//// namespace N { +//// export interface I { +//// y: I; +//// } +//// } +//// class C1 implements N.I {[| |]} + +verify.rangeAfterCodeFix(` +y: N.I; +`); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts index 6e0658019f..e667783b99 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceSomePropertiesPresent.ts @@ -6,8 +6,8 @@ //// z: number & { __iBrand: any }; //// } //// -//// class C implements I {[| |] -//// constructor(public x: number) { } +//// class C implements I {[| +//// |]constructor(public x: number) { } //// y: number; //// } diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts index f76bd5dae9..acccafe0fc 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts @@ -4,7 +4,7 @@ //// x: { y: T, z: T[] }; //// } //// -//// class C implements I {[| |]} +//// class C implements I {[| |]} verify.rangeAfterCodeFix(` x: { y: number; z: number[]; }; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts index af0e7a162e..a11101c7f4 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts @@ -4,7 +4,7 @@ //// x: T; //// } //// -//// class C implements I {[| |]} +//// class C implements I {[| |]} verify.rangeAfterCodeFix(` x: number; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts index 45fa3fd40c..298ee0704f 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts @@ -4,7 +4,7 @@ //// x: T; //// } //// -//// class C implements I {[| |]} +//// class C implements I {[| |]} verify.rangeAfterCodeFix(` x: T; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts index 16c35f4177..3f1da116f4 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts @@ -4,7 +4,7 @@ //// x: T; //// } //// -//// class C implements I {[| |]} +//// class C implements I {[| |]} verify.rangeAfterCodeFix(` x: U; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts index 4d6afaefed..9bcd4b8845 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts @@ -7,6 +7,6 @@ //// class C implements I {[| |]} verify.rangeAfterCodeFix(`f(x: T){ - throw new Error('Method not implemented.'); + throw new Error("Method not implemented."); } `); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixUndeclaredIndexSignatureNumericLiteral.ts b/tests/cases/fourslash/codeFixUndeclaredIndexSignatureNumericLiteral.ts index 2e49a8184e..0a2b0ee799 100644 --- a/tests/cases/fourslash/codeFixUndeclaredIndexSignatureNumericLiteral.ts +++ b/tests/cases/fourslash/codeFixUndeclaredIndexSignatureNumericLiteral.ts @@ -8,7 +8,7 @@ verify.rangeAfterCodeFix(` class A { - [name: string]: number; + [x: string]: number; constructor() { this.x = 10;