diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6c24def150..2471ebcb52 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2610,18 +2610,27 @@ namespace ts { /*initializer*/ undefined); const typeNode = typeToTypeNodeHelper(indexInfo.type); return createIndexSignatureDeclaration( - [indexingParameter], - typeNode, /*decorators*/ undefined, - indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : 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)); - const returnType = getReturnTypeOfSignature(signature); - const returnTypeNode = returnType && typeToTypeNodeHelper(returnType); + 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); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 4c5a4b4067..66b3eb713e 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -286,6 +286,10 @@ namespace ts { 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; @@ -312,6 +316,20 @@ namespace ts { : 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; @@ -455,21 +473,21 @@ namespace ts { : node; } - export function createIndexSignatureDeclaration(parameters: ParameterDeclaration[], type: TypeNode, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined): IndexSignatureDeclaration { + export function createIndexSignatureDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, parameters: ParameterDeclaration[], type: TypeNode): IndexSignatureDeclaration { const indexSignature = createSynthesizedNode(SyntaxKind.IndexSignature) as IndexSignatureDeclaration; - indexSignature.parameters = asNodeArray(parameters); - indexSignature.type = type; indexSignature.decorators = asNodeArray(decorators); indexSignature.modifiers = asNodeArray(modifiers); + indexSignature.parameters = createNodeArray(parameters); + indexSignature.type = type; return indexSignature; } - export function updateIndexSignatureDeclaration(node: IndexSignatureDeclaration, parameters: ParameterDeclaration[], type: TypeNode, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined) { + 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(parameters, type, decorators, modifiers), node) + ? updateNode(createIndexSignatureDeclaration(decorators, modifiers, parameters, type), node) : node; } @@ -542,7 +560,7 @@ namespace ts { node.name = asName(name); node.questionToken = questionToken; node.typeParameters = asNodeArray(typeParameters); - node.parameters = asNodeArray(parameters); + node.parameters = createNodeArray(parameters); node.type = type; node.body = body; return node; @@ -2052,7 +2070,8 @@ namespace ts { function asName(name: string | BindingName): BindingName; function asName(name: string | PropertyName): PropertyName; function asName(name: string | EntityName): EntityName; - function asName(name: string | Identifier | BindingName | PropertyName | QualifiedName) { + function asName(name: string | Identifier | ThisTypeNode): Identifier | ThisTypeNode; + function asName(name: string | Identifier | BindingName | PropertyName | QualifiedName | ThisTypeNode) { return typeof name === "string" ? createIdentifier(name) : name; } @@ -2060,7 +2079,7 @@ namespace ts { return typeof value === "string" || typeof value === "number" ? createLiteral(value) : value; } - export function asNodeArray(array: T[] | undefined): NodeArray | undefined { + function asNodeArray(array: T[] | undefined): NodeArray | undefined { return array ? createNodeArray(array) : undefined; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 45a4de01c9..038f8bf316 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1042,7 +1042,7 @@ namespace ts { kind: SyntaxKind.TrueKeyword | SyntaxKind.FalseKeyword; } - export interface ThisExpression extends PrimaryExpression, TypeNode { + export interface ThisExpression extends PrimaryExpression, KeywordTypeNode { kind: SyntaxKind.ThisKeyword; } @@ -3248,12 +3248,6 @@ namespace ts { declaration?: SignatureDeclaration; } - export interface SignatureParts { - typeParameters: TypeParameterDeclaration[] | undefined; - parameters: ParameterDeclaration[]; - type: TypeNode; - } - /* @internal */ export interface TypeMapper { (t: TypeParameter): Type; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f4f288d0cb..33cb0d8f48 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3735,7 +3735,7 @@ namespace ts { * of a TypeNode. */ export function isTypeNode(node: Node): node is TypeNode { - return node && isTypeNodeKind(node.kind); + return isTypeNodeKind(node.kind); } // Binding patterns diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index b8eb0210d0..c110ea4227 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -3,26 +3,6 @@ /// namespace ts { - export const nullTransformationContext: TransformationContext = { - enableEmitNotification: noop, - enableSubstitution: noop, - endLexicalEnvironment: () => undefined, - getCompilerOptions: notImplemented, - getEmitHost: notImplemented, - getEmitResolver: notImplemented, - hoistFunctionDeclaration: noop, - hoistVariableDeclaration: noop, - isEmitNotificationEnabled: notImplemented, - isSubstitutionEnabled: notImplemented, - onEmitNode: noop, - onSubstituteNode: notImplemented, - readEmitHelpers: notImplemented, - requestEmitHelper: noop, - resumeLexicalEnvironment: noop, - startLexicalEnvironment: noop, - suspendLexicalEnvironment: noop - }; - /** * Visits a Node using the supplied visitor, possibly returning a new Node in its place. * @@ -234,7 +214,7 @@ namespace ts { const kind = node.kind; // No need to visit nodes with no children. - if ((kind > SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken)) { + if ((kind > SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken) || kind === SyntaxKind.ThisType) { return node; } @@ -293,10 +273,10 @@ namespace ts { case SyntaxKind.IndexSignature: return updateIndexSignatureDeclaration(node, - visitParameterList((node).parameters, visitor, context, nodesVisitor), - visitNode((node).type, visitor, isTypeNode), nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier)); + nodesVisitor((node).modifiers, visitor, isModifier), + visitParameterList((node).parameters, visitor, context, nodesVisitor), + visitNode((node).type, visitor, isTypeNode)); case SyntaxKind.Parameter: return updateParameter(node, @@ -314,13 +294,16 @@ namespace ts { // Types - case SyntaxKind.TypePredicate: - throw new Error("reached unsupported type in visitor."); 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)); @@ -339,9 +322,8 @@ namespace ts { nodesVisitor((node).types, visitor, isTypeNode)); case SyntaxKind.ParenthesizedType: - throw new Error("reached unsupported type in visitor."); - case SyntaxKind.ThisType: - throw new Error("reached unsupported type in visitor."); + Debug.fail("not implemented."); + case SyntaxKind.TypeOperator: return updateTypeOperatorNode(node, visitNode((node).type, visitor, isTypeNode)); case SyntaxKind.IndexedAccessType: @@ -376,13 +358,6 @@ namespace ts { visitNode((node).type, visitor, isTypeNode), visitNode((node).initializer, visitor, isExpression)); - case SyntaxKind.IndexSignature: - return updateIndexSignatureDeclaration(node, - visitParameterList((node).parameters, visitor, context, nodesVisitor), - visitNode((node).type, visitor, isTypeNode), - nodesVisitor((node).decorators, visitor, isDecorator), - nodesVisitor((node).modifiers, visitor, isModifier)); - case SyntaxKind.PropertyDeclaration: return updateProperty(node, nodesVisitor((node).decorators, visitor, isDecorator), diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 4ef9fce067..bfb931e866 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -65,10 +65,10 @@ namespace ts.codefix { stringTypeNode, /*initializer*/ undefined); const indexSignature = createIndexSignatureDeclaration( - [indexingParameter], - typeNode, /*decorators*/undefined, - /*modifiers*/ undefined); + /*modifiers*/ undefined, + [indexingParameter], + typeNode); const indexSignatureChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context); indexSignatureChangeTracker.insertNodeAfter(sourceFile, openBrace, indexSignature, { suffix: context.newLineCharacter }); diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 49f95243c9..a7809cdd21 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -230,12 +230,4 @@ namespace ts.codefix { } return undefined; } - - function stripComments(node: Node): Node { - if (node === undefined) { - return node; - } - const strippedChildren = visitEachChild(node, stripComments, nullTransformationContext); - return strippedChildren === node ? getSynthesizedClone(strippedChildren) : strippedChildren; - } } \ No newline at end of file diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index f5b0ec678d..73d5fc61bc 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -528,6 +528,26 @@ namespace ts.textChanges { return skipTrivia(s, 0) === s.length; } + const nullTransformationContext: TransformationContext = { + enableEmitNotification: noop, + enableSubstitution: noop, + endLexicalEnvironment: () => undefined, + getCompilerOptions: notImplemented, + getEmitHost: notImplemented, + getEmitResolver: notImplemented, + hoistFunctionDeclaration: noop, + hoistVariableDeclaration: noop, + isEmitNotificationEnabled: notImplemented, + isSubstitutionEnabled: notImplemented, + onEmitNode: noop, + onSubstituteNode: notImplemented, + readEmitHelpers: notImplemented, + requestEmitHelper: noop, + resumeLexicalEnvironment: noop, + startLexicalEnvironment: noop, + suspendLexicalEnvironment: noop + }; + function assignPositionsToNode(node: Node): Node { const visited = visitEachChild(node, assignPositionsToNode, nullTransformationContext, assignPositionsToNodeArray, assignPositionsToNode); // create proxy node for non synthesized nodes 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."); +} +`);