From a3a6862d4355849c63f3c8751521791b4beebedf Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 14 Jul 2017 16:27:09 -0700 Subject: [PATCH 01/34] Add simple jsdoc code fix changes --- tests/cases/fourslash/codeFixChangeJSDocSyntax1.ts | 4 ++++ tests/cases/fourslash/codeFixChangeJSDocSyntax2.ts | 4 ++++ tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts | 4 ++++ 3 files changed, 12 insertions(+) create mode 100644 tests/cases/fourslash/codeFixChangeJSDocSyntax1.ts create mode 100644 tests/cases/fourslash/codeFixChangeJSDocSyntax2.ts create mode 100644 tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax1.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax1.ts new file mode 100644 index 00000000000..93107ef669b --- /dev/null +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax1.ts @@ -0,0 +1,4 @@ +/// +//// var x: [|?|] = 12; + +verify.rangeAfterCodeFix("any"); diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax2.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax2.ts new file mode 100644 index 00000000000..333b108538f --- /dev/null +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax2.ts @@ -0,0 +1,4 @@ +/// +//// var x: [|*|] = 12; + +verify.rangeAfterCodeFix("any"); diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts new file mode 100644 index 00000000000..d64ed7f1b0d --- /dev/null +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts @@ -0,0 +1,4 @@ +/// +//// var x: [|...number|] = 12; + +verify.rangeAfterCodeFix("number[]"); From efdba54deb3de6a1864a079c5bb623dde6736645 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 14 Jul 2017 16:27:36 -0700 Subject: [PATCH 02/34] Add codefix for jsdoc types in Typescript It only handles a few simple types right now, but the skeleton is there. --- .../codefixes/disableJsDiagnostics.ts | 4 +- src/services/codefixes/fixJSDocTypes.ts | 52 +++++++++++++++++++ src/services/codefixes/fixes.ts | 1 + 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 src/services/codefixes/fixJSDocTypes.ts diff --git a/src/services/codefixes/disableJsDiagnostics.ts b/src/services/codefixes/disableJsDiagnostics.ts index b5f9e5587a3..291dc61c32f 100644 --- a/src/services/codefixes/disableJsDiagnostics.ts +++ b/src/services/codefixes/disableJsDiagnostics.ts @@ -32,7 +32,7 @@ namespace ts.codefix { } } - // If all fails, add an extra new line immediatlly before the error span. + // If all fails, add an extra new line immediately before the error span. return { span: { start: position, length: 0 }, newText: `${position === startPosition ? "" : newLineCharacter}// @ts-ignore${newLineCharacter}` @@ -67,4 +67,4 @@ namespace ts.codefix { }] }]; } -} \ No newline at end of file +} diff --git a/src/services/codefixes/fixJSDocTypes.ts b/src/services/codefixes/fixJSDocTypes.ts new file mode 100644 index 00000000000..db70985dd51 --- /dev/null +++ b/src/services/codefixes/fixJSDocTypes.ts @@ -0,0 +1,52 @@ +/* @internal */ +namespace ts.codefix { + registerCodeFix({ + errorCodes: [Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments.code], + getCodeActions: getActionsForJSDocTypes + }); + + function getActionsForJSDocTypes(context: CodeFixContext): CodeAction[] | undefined { + const sourceFile = context.sourceFile; + + const node = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false); + if (node.kind !== SyntaxKind.VariableDeclaration) return; + + const type = (node as VariableDeclaration).type; + if (containsJSDocType(type)) { + const tsType = getTypeFromJSDocType(type); + return [{ + description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [getTextOfNode(type), tsType]), + changes: [{ + fileName: sourceFile.fileName, + textChanges: [{ + span: { start: type.getStart(), length: type.getWidth() }, + newText: tsType + }], + }], + }]; + } + } + + function containsJSDocType(type: TypeNode): boolean { + switch (type.kind) { + case SyntaxKind.JSDocUnknownType: + case SyntaxKind.JSDocAllType: + case SyntaxKind.JSDocVariadicType: + return true; + // TODO: Of course you can put JSDoc types inside normal types, like number?[] and so on + } + } + + function getTypeFromJSDocType(type: TypeNode): string { + switch (type.kind) { + case SyntaxKind.JSDocUnknownType: + case SyntaxKind.JSDocAllType: + return "any"; + case SyntaxKind.JSDocVariadicType: + // this will surely work! + return getTypeFromJSDocType((type as JSDocVariadicType).type) + "[]"; + // TODO: Of course you can put JSDoc types inside normal types, like number?[] and so on + } + return getTextOfNode(type); + } +} diff --git a/src/services/codefixes/fixes.ts b/src/services/codefixes/fixes.ts index c38820231b0..ef670c81ba9 100644 --- a/src/services/codefixes/fixes.ts +++ b/src/services/codefixes/fixes.ts @@ -7,6 +7,7 @@ /// /// /// +/// /// /// /// From dba552d07157aa5457fa1046389d6b420ade3d3e Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 17 Jul 2017 10:02:29 -0700 Subject: [PATCH 03/34] Transform trees rather than produce strings 1. Still pretty janky. 2. Type Reference code doesn't work yet. 3. Other transforms just aren't done. 4. Always replaces, even when there isn't any transformation of JSDoc types. (This probably isn't an issue since we wouldn't issue an error unless there were some JSDoc to change.) --- src/services/codefixes/fixJSDocTypes.ts | 52 ++++++++++++------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/services/codefixes/fixJSDocTypes.ts b/src/services/codefixes/fixJSDocTypes.ts index db70985dd51..4f89a171329 100644 --- a/src/services/codefixes/fixJSDocTypes.ts +++ b/src/services/codefixes/fixJSDocTypes.ts @@ -11,42 +11,40 @@ namespace ts.codefix { const node = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false); if (node.kind !== SyntaxKind.VariableDeclaration) return; - const type = (node as VariableDeclaration).type; - if (containsJSDocType(type)) { - const tsType = getTypeFromJSDocType(type); - return [{ - description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [getTextOfNode(type), tsType]), - changes: [{ - fileName: sourceFile.fileName, - textChanges: [{ - span: { start: type.getStart(), length: type.getWidth() }, - newText: tsType - }], - }], - }]; - } + const trk = textChanges.ChangeTracker.fromCodeFixContext(context); + const jsdocType = (node as VariableDeclaration).type; + // TODO: Only if get(jsdoctype) !== jsdoctype + trk.replaceNode(sourceFile, jsdocType, getTypeFromJSDocType(jsdocType)); + return [{ + // TODO: This seems like the LEAST SAFE way to get the new text + description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [getTextOfNode(jsdocType), trk.getChanges()[0].textChanges[0].newText]), + changes: trk.getChanges(), + }]; } - function containsJSDocType(type: TypeNode): boolean { + function getTypeFromJSDocType(type: TypeNode): TypeNode { switch (type.kind) { case SyntaxKind.JSDocUnknownType: case SyntaxKind.JSDocAllType: + return createToken(SyntaxKind.AnyKeyword) as TypeNode; case SyntaxKind.JSDocVariadicType: - return true; - // TODO: Of course you can put JSDoc types inside normal types, like number?[] and so on + return createArrayTypeNode(getTypeFromJSDocType((type as JSDocVariadicType).type)); + case SyntaxKind.ArrayType: + // TODO: Only create an error if the get(type.type) !== type.type. + return createArrayTypeNode(getTypeFromJSDocType((type as ArrayTypeNode).elementType)); + case SyntaxKind.TypeReference: + return getTypeReferenceFromJSDocType(type as TypeReferenceNode); + case SyntaxKind.Identifier: + return type; } + // TODO: Need to recur on all relevant nodes. Is a call to visit enough? + return type; } - function getTypeFromJSDocType(type: TypeNode): string { - switch (type.kind) { - case SyntaxKind.JSDocUnknownType: - case SyntaxKind.JSDocAllType: - return "any"; - case SyntaxKind.JSDocVariadicType: - // this will surely work! - return getTypeFromJSDocType((type as JSDocVariadicType).type) + "[]"; - // TODO: Of course you can put JSDoc types inside normal types, like number?[] and so on + function getTypeReferenceFromJSDocType(type: TypeReferenceNode) { + if (type.typeArguments && type.typeName.jsdocDotPos) { + return createTypeReferenceNode(type.typeName, map(type.typeArguments, getTypeFromJSDocType)); } - return getTextOfNode(type); + return getTypeFromJSDocType(type); } } From 3776b0b58b87e6f1f6523ef0e5720125db9e48e4 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 17 Jul 2017 10:33:04 -0700 Subject: [PATCH 04/34] Codefix for Array. -> Array --- src/services/codefixes/fixJSDocTypes.ts | 9 +++++---- tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts | 4 ++-- tests/cases/fourslash/codeFixChangeJSDocSyntax4.ts | 4 ++++ 3 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 tests/cases/fourslash/codeFixChangeJSDocSyntax4.ts diff --git a/src/services/codefixes/fixJSDocTypes.ts b/src/services/codefixes/fixJSDocTypes.ts index 4f89a171329..2ca8702047e 100644 --- a/src/services/codefixes/fixJSDocTypes.ts +++ b/src/services/codefixes/fixJSDocTypes.ts @@ -7,13 +7,14 @@ namespace ts.codefix { function getActionsForJSDocTypes(context: CodeFixContext): CodeAction[] | undefined { const sourceFile = context.sourceFile; - const node = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false); - if (node.kind !== SyntaxKind.VariableDeclaration) return; + const decl = ts.findAncestor(node, n => n.kind === SyntaxKind.VariableDeclaration); + if (!decl) return; + const jsdocType = (decl as VariableDeclaration).type; + + // TODO: Only if get(jsdoctype) !== jsdoctype const trk = textChanges.ChangeTracker.fromCodeFixContext(context); - const jsdocType = (node as VariableDeclaration).type; - // TODO: Only if get(jsdoctype) !== jsdoctype trk.replaceNode(sourceFile, jsdocType, getTypeFromJSDocType(jsdocType)); return [{ // TODO: This seems like the LEAST SAFE way to get the new text diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts index d64ed7f1b0d..f3b02cb84f1 100644 --- a/tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts @@ -1,4 +1,4 @@ /// -//// var x: [|...number|] = 12; +//// var x: [|......number[][]|] = 12; -verify.rangeAfterCodeFix("number[]"); +verify.rangeAfterCodeFix("number[][][][]"); diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax4.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax4.ts new file mode 100644 index 00000000000..8eb06c102dd --- /dev/null +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax4.ts @@ -0,0 +1,4 @@ +/// +//// var x: [|Array.|] = 12; + +verify.rangeAfterCodeFix("Array"); From f9e5576d582493a64b64c56d66049edf7842aa7d Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 17 Jul 2017 11:06:20 -0700 Subject: [PATCH 05/34] Codefix for ?! pre/postfix JSDoc types For ?, provide two code fixes, one for jsdoc/closure semantics (`?t -> t | null)` and one for flow semantics (`?t -> t | null | undefined`). The current way of doing this is the hackiest thing you can imagine, but it was easier than lifting everything into the list monad for a code fix that I might not actually keep. --- src/services/codefixes/fixJSDocTypes.ts | 69 ++++++++++++------- .../fourslash/codeFixChangeJSDocSyntax5.ts | 4 ++ .../fourslash/codeFixChangeJSDocSyntax6.ts | 4 ++ .../fourslash/codeFixChangeJSDocSyntax7.ts | 4 ++ 4 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 tests/cases/fourslash/codeFixChangeJSDocSyntax5.ts create mode 100644 tests/cases/fourslash/codeFixChangeJSDocSyntax6.ts create mode 100644 tests/cases/fourslash/codeFixChangeJSDocSyntax7.ts diff --git a/src/services/codefixes/fixJSDocTypes.ts b/src/services/codefixes/fixJSDocTypes.ts index 2ca8702047e..706fdd6c1be 100644 --- a/src/services/codefixes/fixJSDocTypes.ts +++ b/src/services/codefixes/fixJSDocTypes.ts @@ -11,41 +11,64 @@ namespace ts.codefix { const decl = ts.findAncestor(node, n => n.kind === SyntaxKind.VariableDeclaration); if (!decl) return; const jsdocType = (decl as VariableDeclaration).type; + let cheesyHacks = false; // TODO: Only if get(jsdoctype) !== jsdoctype + // TODO: Create cheesy hacks to support | null | undefined -- just flip a boolean and rerun with that boolean set const trk = textChanges.ChangeTracker.fromCodeFixContext(context); trk.replaceNode(sourceFile, jsdocType, getTypeFromJSDocType(jsdocType)); - return [{ + const changes = [{ // TODO: This seems like the LEAST SAFE way to get the new text description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [getTextOfNode(jsdocType), trk.getChanges()[0].textChanges[0].newText]), changes: trk.getChanges(), }]; - } - function getTypeFromJSDocType(type: TypeNode): TypeNode { - switch (type.kind) { - case SyntaxKind.JSDocUnknownType: - case SyntaxKind.JSDocAllType: - return createToken(SyntaxKind.AnyKeyword) as TypeNode; - case SyntaxKind.JSDocVariadicType: - return createArrayTypeNode(getTypeFromJSDocType((type as JSDocVariadicType).type)); - case SyntaxKind.ArrayType: - // TODO: Only create an error if the get(type.type) !== type.type. - return createArrayTypeNode(getTypeFromJSDocType((type as ArrayTypeNode).elementType)); - case SyntaxKind.TypeReference: - return getTypeReferenceFromJSDocType(type as TypeReferenceNode); - case SyntaxKind.Identifier: - return type; + if (cheesyHacks) { + const trk = textChanges.ChangeTracker.fromCodeFixContext(context); + trk.replaceNode(sourceFile, jsdocType, getTypeFromJSDocType(jsdocType)); + changes.push({ + // TODO: This seems like the LEAST SAFE way to get the new text + description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [getTextOfNode(jsdocType), trk.getChanges()[0].textChanges[0].newText]), + changes: trk.getChanges(), + }); } - // TODO: Need to recur on all relevant nodes. Is a call to visit enough? - return type; - } + return changes; - function getTypeReferenceFromJSDocType(type: TypeReferenceNode) { - if (type.typeArguments && type.typeName.jsdocDotPos) { - return createTypeReferenceNode(type.typeName, map(type.typeArguments, getTypeFromJSDocType)); + function getTypeFromJSDocType(type: TypeNode): TypeNode { + switch (type.kind) { + case SyntaxKind.JSDocUnknownType: + case SyntaxKind.JSDocAllType: + return createToken(SyntaxKind.AnyKeyword) as TypeNode; + case SyntaxKind.JSDocVariadicType: + return createArrayTypeNode(getTypeFromJSDocType((type as JSDocVariadicType).type)); + case SyntaxKind.JSDocNullableType: + if (cheesyHacks) { + return createUnionTypeNode([getTypeFromJSDocType((type as JSDocNullableType).type), createNull(), createToken(SyntaxKind.UndefinedKeyword) as TypeNode]); + } + else { + cheesyHacks = true; + return createUnionTypeNode([getTypeFromJSDocType((type as JSDocNullableType).type), createNull()]); + } + case SyntaxKind.JSDocNonNullableType: + return getTypeFromJSDocType((type as JSDocNullableType).type); + case SyntaxKind.ArrayType: + // TODO: Only create an error if the get(type.type) !== type.type. + return createArrayTypeNode(getTypeFromJSDocType((type as ArrayTypeNode).elementType)); + case SyntaxKind.TypeReference: + return getTypeReferenceFromJSDocType(type as TypeReferenceNode); + case SyntaxKind.Identifier: + return type; + } + // TODO: Need to recur on all relevant nodes. Is a call to visit enough? + return type; + } + + function getTypeReferenceFromJSDocType(type: TypeReferenceNode) { + if (type.typeArguments && type.typeName.jsdocDotPos) { + return createTypeReferenceNode(type.typeName, map(type.typeArguments, getTypeFromJSDocType)); + } + return getTypeFromJSDocType(type); } - return getTypeFromJSDocType(type); } } diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax5.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax5.ts new file mode 100644 index 00000000000..baf9a73976a --- /dev/null +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax5.ts @@ -0,0 +1,4 @@ +/// +//// var x: [|?number|] = 12; + +verify.rangeAfterCodeFix("number | null", /*includeWhiteSpace*/ false, /*errorCode*/ 8020, 0); diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax6.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax6.ts new file mode 100644 index 00000000000..aab10a3dfea --- /dev/null +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax6.ts @@ -0,0 +1,4 @@ +/// +//// var x: [|number?|] = 12; + +verify.rangeAfterCodeFix("number | null | undefined", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 1); diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax7.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax7.ts new file mode 100644 index 00000000000..c80d08b3bac --- /dev/null +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax7.ts @@ -0,0 +1,4 @@ +/// +//// var x: [|!number|] = 12; + +verify.rangeAfterCodeFix("number"); From cbe7b4dba34c4b560a2d3404019d27baea0b3037 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 17 Jul 2017 14:11:06 -0700 Subject: [PATCH 06/34] Update jsdoc codefix tests; test function(...) syntax --- tests/cases/fourslash/codeFixChangeJSDocSyntax4.ts | 2 +- tests/cases/fourslash/codeFixChangeJSDocSyntax5.ts | 1 + tests/cases/fourslash/codeFixChangeJSDocSyntax6.ts | 1 + tests/cases/fourslash/codeFixChangeJSDocSyntax8.ts | 4 ++++ tests/cases/fourslash/codeFixChangeJSDocSyntax9.ts | 4 ++++ 5 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/codeFixChangeJSDocSyntax8.ts create mode 100644 tests/cases/fourslash/codeFixChangeJSDocSyntax9.ts diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax4.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax4.ts index 8eb06c102dd..e9522331d38 100644 --- a/tests/cases/fourslash/codeFixChangeJSDocSyntax4.ts +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax4.ts @@ -1,4 +1,4 @@ /// //// var x: [|Array.|] = 12; -verify.rangeAfterCodeFix("Array"); +verify.rangeAfterCodeFix("number[]"); diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax5.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax5.ts index baf9a73976a..6f46f3082e1 100644 --- a/tests/cases/fourslash/codeFixChangeJSDocSyntax5.ts +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax5.ts @@ -1,3 +1,4 @@ +// @strict: true /// //// var x: [|?number|] = 12; diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax6.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax6.ts index aab10a3dfea..8af9f09d99d 100644 --- a/tests/cases/fourslash/codeFixChangeJSDocSyntax6.ts +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax6.ts @@ -1,3 +1,4 @@ +// @strict: true /// //// var x: [|number?|] = 12; diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax8.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax8.ts new file mode 100644 index 00000000000..0fa7ddf229c --- /dev/null +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax8.ts @@ -0,0 +1,4 @@ +/// +//// var x: [|function(this: number, number): string|] = 12; + +verify.rangeAfterCodeFix("(this: number, arg1: number) => string"); diff --git a/tests/cases/fourslash/codeFixChangeJSDocSyntax9.ts b/tests/cases/fourslash/codeFixChangeJSDocSyntax9.ts new file mode 100644 index 00000000000..061ded158ea --- /dev/null +++ b/tests/cases/fourslash/codeFixChangeJSDocSyntax9.ts @@ -0,0 +1,4 @@ +/// +//// var x: [|function(new: number)|] = 12; + +verify.rangeAfterCodeFix("new () => number"); From b13de0547e9aa46d0dacc8666bd9680c0809ea49 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 17 Jul 2017 14:11:35 -0700 Subject: [PATCH 07/34] JSDoc codefix:getTypeFromTypeNode >>> typeToString Instead of trying to walk the type structure in the codefix, I changed to call getTypeFromTypeNode in the checker and then calling typeToString. Together, these two functions normalise out JSDoc. Note that you only get `T | null` for `?T` if you have --strict on. This is technically correct -- adding a null union does nothing without strict -- but it would still serve as documentation. --- src/compiler/checker.ts | 1 + src/compiler/types.ts | 1 + src/services/codefixes/fixJSDocTypes.ts | 80 +++++++------------------ 3 files changed, 25 insertions(+), 57 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5d3300ba19b..35d7307d4bd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -117,6 +117,7 @@ namespace ts { }, getParameterType: getTypeAtPosition, getReturnTypeOfSignature, + getNullableType, getNonNullableType, typeToTypeNode: nodeBuilder.typeToTypeNode, indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6ba1591d54f..c3c57058284 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2520,6 +2520,7 @@ namespace ts { * Returns `any` if the index is not valid. */ /* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type; + getNullableType(type: Type, flags: TypeFlags): Type; getNonNullableType(type: Type): Type; /** Note that the resulting nodes cannot be checked. */ diff --git a/src/services/codefixes/fixJSDocTypes.ts b/src/services/codefixes/fixJSDocTypes.ts index 706fdd6c1be..d0adb9d98c3 100644 --- a/src/services/codefixes/fixJSDocTypes.ts +++ b/src/services/codefixes/fixJSDocTypes.ts @@ -10,65 +10,31 @@ namespace ts.codefix { const node = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false); const decl = ts.findAncestor(node, n => n.kind === SyntaxKind.VariableDeclaration); if (!decl) return; + const checker = context.program.getTypeChecker(); + const jsdocType = (decl as VariableDeclaration).type; - let cheesyHacks = false; - - // TODO: Only if get(jsdoctype) !== jsdoctype - // TODO: Create cheesy hacks to support | null | undefined -- just flip a boolean and rerun with that boolean set - - const trk = textChanges.ChangeTracker.fromCodeFixContext(context); - trk.replaceNode(sourceFile, jsdocType, getTypeFromJSDocType(jsdocType)); - const changes = [{ - // TODO: This seems like the LEAST SAFE way to get the new text - description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [getTextOfNode(jsdocType), trk.getChanges()[0].textChanges[0].newText]), - changes: trk.getChanges(), - }]; - - if (cheesyHacks) { - const trk = textChanges.ChangeTracker.fromCodeFixContext(context); - trk.replaceNode(sourceFile, jsdocType, getTypeFromJSDocType(jsdocType)); - changes.push({ - // TODO: This seems like the LEAST SAFE way to get the new text - description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [getTextOfNode(jsdocType), trk.getChanges()[0].textChanges[0].newText]), - changes: trk.getChanges(), - }); + const original = getTextOfNode(jsdocType); + const type = checker.getTypeFromTypeNode(jsdocType); + const actions = [createAction(jsdocType, sourceFile.fileName, original, checker.typeToString(type))]; + if (jsdocType.kind === SyntaxKind.JSDocNullableType) { + // for nullable types, suggest the flow-compatible `T | null | undefined` + // in addition to the jsdoc/closure-compatible `T | null` + const replacementWithUndefined = checker.typeToString(checker.getNullableType(type, TypeFlags.Undefined)); + actions.push(createAction(jsdocType, sourceFile.fileName, original, replacementWithUndefined)); } - return changes; + return actions; + } - function getTypeFromJSDocType(type: TypeNode): TypeNode { - switch (type.kind) { - case SyntaxKind.JSDocUnknownType: - case SyntaxKind.JSDocAllType: - return createToken(SyntaxKind.AnyKeyword) as TypeNode; - case SyntaxKind.JSDocVariadicType: - return createArrayTypeNode(getTypeFromJSDocType((type as JSDocVariadicType).type)); - case SyntaxKind.JSDocNullableType: - if (cheesyHacks) { - return createUnionTypeNode([getTypeFromJSDocType((type as JSDocNullableType).type), createNull(), createToken(SyntaxKind.UndefinedKeyword) as TypeNode]); - } - else { - cheesyHacks = true; - return createUnionTypeNode([getTypeFromJSDocType((type as JSDocNullableType).type), createNull()]); - } - case SyntaxKind.JSDocNonNullableType: - return getTypeFromJSDocType((type as JSDocNullableType).type); - case SyntaxKind.ArrayType: - // TODO: Only create an error if the get(type.type) !== type.type. - return createArrayTypeNode(getTypeFromJSDocType((type as ArrayTypeNode).elementType)); - case SyntaxKind.TypeReference: - return getTypeReferenceFromJSDocType(type as TypeReferenceNode); - case SyntaxKind.Identifier: - return type; - } - // TODO: Need to recur on all relevant nodes. Is a call to visit enough? - return type; - } - - function getTypeReferenceFromJSDocType(type: TypeReferenceNode) { - if (type.typeArguments && type.typeName.jsdocDotPos) { - return createTypeReferenceNode(type.typeName, map(type.typeArguments, getTypeFromJSDocType)); - } - return getTypeFromJSDocType(type); - } + function createAction(declaration: TypeNode, fileName: string, original: string, replacement: string): CodeAction { + return { + description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [original, replacement]), + changes: [{ + fileName, + textChanges: [{ + span: { start: declaration.getStart(), length: declaration.getWidth() }, + newText: replacement + }] + }], + }; } } From 73cfa64f448c742d8216b7b74ee60457acf30b89 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 17 Jul 2017 14:47:10 -0700 Subject: [PATCH 08/34] Make sure not to truncate the stringified type from typeToString --- src/services/codefixes/fixJSDocTypes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/codefixes/fixJSDocTypes.ts b/src/services/codefixes/fixJSDocTypes.ts index d0adb9d98c3..249bc32dbf7 100644 --- a/src/services/codefixes/fixJSDocTypes.ts +++ b/src/services/codefixes/fixJSDocTypes.ts @@ -15,11 +15,11 @@ namespace ts.codefix { const jsdocType = (decl as VariableDeclaration).type; const original = getTextOfNode(jsdocType); const type = checker.getTypeFromTypeNode(jsdocType); - const actions = [createAction(jsdocType, sourceFile.fileName, original, checker.typeToString(type))]; + const actions = [createAction(jsdocType, sourceFile.fileName, original, checker.typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTruncation))]; if (jsdocType.kind === SyntaxKind.JSDocNullableType) { // for nullable types, suggest the flow-compatible `T | null | undefined` // in addition to the jsdoc/closure-compatible `T | null` - const replacementWithUndefined = checker.typeToString(checker.getNullableType(type, TypeFlags.Undefined)); + const replacementWithUndefined = checker.typeToString(checker.getNullableType(type, TypeFlags.Undefined), /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTruncation); actions.push(createAction(jsdocType, sourceFile.fileName, original, replacementWithUndefined)); } return actions; From 08ae02263ae8b8c4d11034c3678c733c99561aa3 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 17 Jul 2017 15:59:18 -0700 Subject: [PATCH 09/34] Contextually type this in object literals in JS Previously, `this` would only get a contextual type inside object literals with `--noImplicitThis` turned on in Typescript files. --- src/compiler/checker.ts | 2 +- .../contextualThisTypeInJavascript.symbols | 25 +++++++++++++++++ .../contextualThisTypeInJavascript.types | 27 +++++++++++++++++++ .../contextualThisTypeInJavascript.ts | 12 +++++++++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/contextualThisTypeInJavascript.symbols create mode 100644 tests/baselines/reference/contextualThisTypeInJavascript.types create mode 100644 tests/cases/conformance/types/thisType/contextualThisTypeInJavascript.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dafa75372e1..d4c7cd7bd9b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12602,7 +12602,7 @@ namespace ts { } } } - if (noImplicitThis) { + if (noImplicitThis || isInJavaScriptFile(func)) { const containingLiteral = getContainingObjectLiteral(func); if (containingLiteral) { // We have an object literal method. Check if the containing object literal has a contextual type diff --git a/tests/baselines/reference/contextualThisTypeInJavascript.symbols b/tests/baselines/reference/contextualThisTypeInJavascript.symbols new file mode 100644 index 00000000000..6c146d654d0 --- /dev/null +++ b/tests/baselines/reference/contextualThisTypeInJavascript.symbols @@ -0,0 +1,25 @@ +=== tests/cases/conformance/types/thisType/context.js === +const obj = { +>obj : Symbol(obj, Decl(context.js, 0, 5)) + + prop: 2, +>prop : Symbol(prop, Decl(context.js, 0, 13)) + + method() { +>method : Symbol(method, Decl(context.js, 1, 12)) + + this; +>this : Symbol(obj, Decl(context.js, 0, 11)) + + this.prop; +>this.prop : Symbol(prop, Decl(context.js, 0, 13)) +>this : Symbol(obj, Decl(context.js, 0, 11)) +>prop : Symbol(prop, Decl(context.js, 0, 13)) + + this.method; +>this.method : Symbol(method, Decl(context.js, 1, 12)) +>this : Symbol(obj, Decl(context.js, 0, 11)) +>method : Symbol(method, Decl(context.js, 1, 12)) + } +} + diff --git a/tests/baselines/reference/contextualThisTypeInJavascript.types b/tests/baselines/reference/contextualThisTypeInJavascript.types new file mode 100644 index 00000000000..c3ec4123071 --- /dev/null +++ b/tests/baselines/reference/contextualThisTypeInJavascript.types @@ -0,0 +1,27 @@ +=== tests/cases/conformance/types/thisType/context.js === +const obj = { +>obj : { [x: string]: any; prop: number; method(): void; } +>{ prop: 2, method() { this; this.prop; this.method; }} : { [x: string]: any; prop: number; method(): void; } + + prop: 2, +>prop : number +>2 : 2 + + method() { +>method : () => void + + this; +>this : { [x: string]: any; prop: number; method(): void; } + + this.prop; +>this.prop : number +>this : { [x: string]: any; prop: number; method(): void; } +>prop : number + + this.method; +>this.method : () => void +>this : { [x: string]: any; prop: number; method(): void; } +>method : () => void + } +} + diff --git a/tests/cases/conformance/types/thisType/contextualThisTypeInJavascript.ts b/tests/cases/conformance/types/thisType/contextualThisTypeInJavascript.ts new file mode 100644 index 00000000000..8ccbc65f545 --- /dev/null +++ b/tests/cases/conformance/types/thisType/contextualThisTypeInJavascript.ts @@ -0,0 +1,12 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @Filename: context.js +const obj = { + prop: 2, + method() { + this; + this.prop; + this.method; + } +} From 10a91c54269db9e85315dc486f178e18182c800b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 17 Jul 2017 16:14:42 -0700 Subject: [PATCH 10/34] JSDoc:Object creates index signature And `Object` creates a numeric index signature. Other uses still create `any` as before. --- src/compiler/checker.ts | 13 +++++++++++-- .../baselines/reference/jsdocIndexSignature.symbols | 13 +++++++++++++ tests/baselines/reference/jsdocIndexSignature.types | 13 +++++++++++++ .../cases/conformance/jsdoc/jsdocIndexSignature.ts | 10 ++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/jsdocIndexSignature.symbols create mode 100644 tests/baselines/reference/jsdocIndexSignature.types create mode 100644 tests/cases/conformance/jsdoc/jsdocIndexSignature.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dafa75372e1..8dd81d1b623 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6872,6 +6872,17 @@ namespace ts { function getPrimitiveTypeFromJSDocTypeReference(node: TypeReferenceNode): Type { if (isIdentifier(node.typeName)) { + if (node.typeName.text === "Object") { + if (node.typeArguments && node.typeArguments.length === 2) { + const from = getTypeFromTypeNode(node.typeArguments[0]); + const to = getTypeFromTypeNode(node.typeArguments[1]); + let index = createIndexInfo(to, /*isReadonly*/ false); + if (from === stringType || from === numberType) { + return createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, from === stringType ? index : undefined, from === numberType ? index : undefined) + } + } + return anyType; + } switch (node.typeName.text) { case "String": return stringType; @@ -6885,8 +6896,6 @@ namespace ts { return undefinedType; case "Null": return nullType; - case "Object": - return anyType; case "Function": case "function": return globalFunctionType; diff --git a/tests/baselines/reference/jsdocIndexSignature.symbols b/tests/baselines/reference/jsdocIndexSignature.symbols new file mode 100644 index 00000000000..8814fc18b6f --- /dev/null +++ b/tests/baselines/reference/jsdocIndexSignature.symbols @@ -0,0 +1,13 @@ +=== tests/cases/conformance/jsdoc/indices.js === +/** @type {Object.} */ +var o1; +>o1 : Symbol(o1, Decl(indices.js, 1, 3)) + +/** @type {Object.} */ +var o2; +>o2 : Symbol(o2, Decl(indices.js, 3, 3)) + +/** @type {Object.} */ +var o3; +>o3 : Symbol(o3, Decl(indices.js, 5, 3)) + diff --git a/tests/baselines/reference/jsdocIndexSignature.types b/tests/baselines/reference/jsdocIndexSignature.types new file mode 100644 index 00000000000..4c1feaa2721 --- /dev/null +++ b/tests/baselines/reference/jsdocIndexSignature.types @@ -0,0 +1,13 @@ +=== tests/cases/conformance/jsdoc/indices.js === +/** @type {Object.} */ +var o1; +>o1 : { [x: string]: number; } + +/** @type {Object.} */ +var o2; +>o2 : { [x: number]: boolean; } + +/** @type {Object.} */ +var o3; +>o3 : any + diff --git a/tests/cases/conformance/jsdoc/jsdocIndexSignature.ts b/tests/cases/conformance/jsdoc/jsdocIndexSignature.ts new file mode 100644 index 00000000000..fdf9e06e61e --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocIndexSignature.ts @@ -0,0 +1,10 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @Filename: indices.js +/** @type {Object.} */ +var o1; +/** @type {Object.} */ +var o2; +/** @type {Object.} */ +var o3; From 1173dc104a57967dc6ccc48681deb8f561f098c7 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 17 Jul 2017 16:18:09 -0700 Subject: [PATCH 11/34] Improve naming and style a little --- src/compiler/checker.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8dd81d1b623..bc1ad88de2c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6874,11 +6874,11 @@ namespace ts { if (isIdentifier(node.typeName)) { if (node.typeName.text === "Object") { if (node.typeArguments && node.typeArguments.length === 2) { - const from = getTypeFromTypeNode(node.typeArguments[0]); - const to = getTypeFromTypeNode(node.typeArguments[1]); - let index = createIndexInfo(to, /*isReadonly*/ false); - if (from === stringType || from === numberType) { - return createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, from === stringType ? index : undefined, from === numberType ? index : undefined) + const indexed = getTypeFromTypeNode(node.typeArguments[0]); + const target = getTypeFromTypeNode(node.typeArguments[1]); + let index = createIndexInfo(target, /*isReadonly*/ false); + if (indexed === stringType || indexed === numberType) { + return createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, indexed === stringType && index, indexed === numberType && index) } } return anyType; From 3b7a07c4414fe113e5157eaa49df446dca1afbbd Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 17 Jul 2017 16:21:54 -0700 Subject: [PATCH 12/34] Fix lint --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bc1ad88de2c..dc935633542 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6876,9 +6876,9 @@ namespace ts { if (node.typeArguments && node.typeArguments.length === 2) { const indexed = getTypeFromTypeNode(node.typeArguments[0]); const target = getTypeFromTypeNode(node.typeArguments[1]); - let index = createIndexInfo(target, /*isReadonly*/ false); + const index = createIndexInfo(target, /*isReadonly*/ false); if (indexed === stringType || indexed === numberType) { - return createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, indexed === stringType && index, indexed === numberType && index) + return createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, indexed === stringType && index, indexed === numberType && index); } } return anyType; From 047ab9b0e351f230bb0140fb0fee27be5806b4cd Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 17 Jul 2017 16:23:42 -0700 Subject: [PATCH 13/34] Update name of `getIntendedTypeFromJSDocTypeReference` --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dc935633542..d8615c592bc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6870,7 +6870,7 @@ namespace ts { return node.flags & NodeFlags.JSDoc && node.kind === SyntaxKind.TypeReference; } - function getPrimitiveTypeFromJSDocTypeReference(node: TypeReferenceNode): Type { + function getIntendedTypeFromJSDocTypeReference(node: TypeReferenceNode): Type { if (isIdentifier(node.typeName)) { if (node.typeName.text === "Object") { if (node.typeArguments && node.typeArguments.length === 2) { @@ -6921,7 +6921,7 @@ namespace ts { let type: Type; let meaning = SymbolFlags.Type; if (isJSDocTypeReference(node)) { - type = getPrimitiveTypeFromJSDocTypeReference(node); + type = getIntendedTypeFromJSDocTypeReference(node); meaning |= SymbolFlags.Value; } if (!type) { From 8a1cd334515eafa96b1b864cbdcbc24161ea327b Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 17 Jul 2017 23:39:20 -0700 Subject: [PATCH 14/34] Use jsdoc casts (#17251) * Allow jsdoc casts of parenthesized expressions * Feedback from #17211 --- src/compiler/checker.ts | 26 ++- src/compiler/parser.ts | 2 +- src/compiler/utilities.ts | 3 +- .../reference/jsdocTypeTagCast.errors.txt | 129 +++++++++++++++ tests/baselines/reference/jsdocTypeTagCast.js | 151 ++++++++++++++++++ .../conformance/jsdoc/jsdocTypeTagCast.ts | 78 +++++++++ 6 files changed, 382 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/jsdocTypeTagCast.errors.txt create mode 100644 tests/baselines/reference/jsdocTypeTagCast.js create mode 100644 tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dafa75372e1..be14b7d95c9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16256,15 +16256,19 @@ namespace ts { } function checkAssertion(node: AssertionExpression) { - const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpression(node.expression))); + return checkAssertionWorker(node, node.type, node.expression); + } - checkSourceElement(node.type); - const targetType = getTypeFromTypeNode(node.type); + function checkAssertionWorker(errNode: Node, type: TypeNode, expression: UnaryExpression | Expression, checkMode?: CheckMode) { + const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpression(expression, checkMode))); + + checkSourceElement(type); + const targetType = getTypeFromTypeNode(type); if (produceDiagnostics && targetType !== unknownType) { const widenedType = getWidenedType(exprType); if (!isTypeComparableTo(targetType, widenedType)) { - checkTypeComparableTo(exprType, targetType, node, Diagnostics.Type_0_cannot_be_converted_to_type_1); + checkTypeComparableTo(exprType, targetType, errNode, Diagnostics.Type_0_cannot_be_converted_to_type_1); } } return targetType; @@ -17735,6 +17739,18 @@ namespace ts { return type; } + function checkParenthesizedExpression(node: ParenthesizedExpression, checkMode?: CheckMode): Type { + if (isInJavaScriptFile(node) && node.jsDoc) { + const typecasts = flatMap(node.jsDoc, doc => filter(doc.tags, tag => tag.kind === SyntaxKind.JSDocTypeTag)); + if (typecasts && typecasts.length) { + // We should have already issued an error if there were multiple type jsdocs + const cast = typecasts[0] as JSDocTypeTag; + return checkAssertionWorker(cast, cast.typeExpression.type, node.expression, checkMode); + } + } + return checkExpression(node.expression, checkMode); + } + function checkExpressionWorker(node: Expression, checkMode: CheckMode): Type { switch (node.kind) { case SyntaxKind.Identifier: @@ -17774,7 +17790,7 @@ namespace ts { case SyntaxKind.TaggedTemplateExpression: return checkTaggedTemplateExpression(node); case SyntaxKind.ParenthesizedExpression: - return checkExpression((node).expression, checkMode); + return checkParenthesizedExpression(node, checkMode); case SyntaxKind.ClassExpression: return checkClassExpression(node); case SyntaxKind.FunctionExpression: diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 20c9559a8f4..4613f49a749 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4342,7 +4342,7 @@ namespace ts { parseExpected(SyntaxKind.OpenParenToken); node.expression = allowInAnd(parseExpression); parseExpected(SyntaxKind.CloseParenToken); - return finishNode(node); + return addJSDocComment(finishNode(node)); } function parseSpreadElement(): Expression { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index b7fabdda381..ab0afb12820 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -640,7 +640,8 @@ namespace ts { const commentRanges = (node.kind === SyntaxKind.Parameter || node.kind === SyntaxKind.TypeParameter || node.kind === SyntaxKind.FunctionExpression || - node.kind === SyntaxKind.ArrowFunction) ? + node.kind === SyntaxKind.ArrowFunction || + node.kind === SyntaxKind.ParenthesizedExpression) ? concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) : getLeadingCommentRangesOfNodeFromText(node, text); // True if the comment starts with '/**' but not if it is '/**/' diff --git a/tests/baselines/reference/jsdocTypeTagCast.errors.txt b/tests/baselines/reference/jsdocTypeTagCast.errors.txt new file mode 100644 index 00000000000..f1f53ec1ecb --- /dev/null +++ b/tests/baselines/reference/jsdocTypeTagCast.errors.txt @@ -0,0 +1,129 @@ +tests/cases/conformance/jsdoc/b.js(4,13): error TS2352: Type 'number' cannot be converted to type 'string'. +tests/cases/conformance/jsdoc/b.js(45,16): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'. + Property 'p' is missing in type 'SomeOther'. +tests/cases/conformance/jsdoc/b.js(49,19): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'. + Property 'x' is missing in type 'SomeOther'. +tests/cases/conformance/jsdoc/b.js(51,17): error TS2352: Type 'SomeDerived' cannot be converted to type 'SomeOther'. + Property 'q' is missing in type 'SomeDerived'. +tests/cases/conformance/jsdoc/b.js(52,17): error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'. + Property 'q' is missing in type 'SomeBase'. +tests/cases/conformance/jsdoc/b.js(58,1): error TS2322: Type '{ p: string | number | undefined; }' is not assignable to type 'SomeBase'. + Types of property 'p' are incompatible. + Type 'string | number | undefined' is not assignable to type 'number'. + Type 'undefined' is not assignable to type 'number'. +tests/cases/conformance/jsdoc/b.js(66,8): error TS2352: Type 'boolean' cannot be converted to type 'string | number'. +tests/cases/conformance/jsdoc/b.js(66,15): error TS2304: Cannot find name 'numOrStr'. +tests/cases/conformance/jsdoc/b.js(66,24): error TS1005: '}' expected. +tests/cases/conformance/jsdoc/b.js(66,38): error TS2454: Variable 'numOrStr' is used before being assigned. +tests/cases/conformance/jsdoc/b.js(67,2): error TS2322: Type 'string | number' is not assignable to type 'string'. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/jsdoc/b.js(67,8): error TS2454: Variable 'numOrStr' is used before being assigned. + + +==== tests/cases/conformance/jsdoc/a.ts (0 errors) ==== + var W: string; + +==== tests/cases/conformance/jsdoc/b.js (12 errors) ==== + // @ts-check + var W = /** @type {string} */(/** @type {*} */ (4)); + + var W = /** @type {string} */(4); // Error + ~~~~~~~~~~~~~~ +!!! error TS2352: Type 'number' cannot be converted to type 'string'. + + /** @type {*} */ + var a; + + /** @type {string} */ + var s; + + var a = /** @type {*} */("" + 4); + var s = "" + /** @type {*} */(4); + + class SomeBase { + constructor() { + this.p = 42; + } + } + class SomeDerived extends SomeBase { + constructor() { + super(); + this.x = 42; + } + } + class SomeOther { + constructor() { + this.q = 42; + } + } + + function SomeFakeClass() { + /** @type {string|number} */ + this.p = "bar"; + } + + // Type assertion should check for assignability in either direction + var someBase = new SomeBase(); + var someDerived = new SomeDerived(); + var someOther = new SomeOther(); + var someFakeClass = new SomeFakeClass(); + + someBase = /** @type {SomeBase} */(someDerived); + someBase = /** @type {SomeBase} */(someBase); + someBase = /** @type {SomeBase} */(someOther); // Error + ~~~~~~~~~~~~~~~~ +!!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'. +!!! error TS2352: Property 'p' is missing in type 'SomeOther'. + + someDerived = /** @type {SomeDerived} */(someDerived); + someDerived = /** @type {SomeDerived} */(someBase); + someDerived = /** @type {SomeDerived} */(someOther); // Error + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'. +!!! error TS2352: Property 'x' is missing in type 'SomeOther'. + + someOther = /** @type {SomeOther} */(someDerived); // Error + ~~~~~~~~~~~~~~~~~ +!!! error TS2352: Type 'SomeDerived' cannot be converted to type 'SomeOther'. +!!! error TS2352: Property 'q' is missing in type 'SomeDerived'. + someOther = /** @type {SomeOther} */(someBase); // Error + ~~~~~~~~~~~~~~~~~ +!!! error TS2352: Type 'SomeBase' cannot be converted to type 'SomeOther'. +!!! error TS2352: Property 'q' is missing in type 'SomeBase'. + someOther = /** @type {SomeOther} */(someOther); + + someFakeClass = someBase; + someFakeClass = someDerived; + + someBase = someFakeClass; // Error + ~~~~~~~~ +!!! error TS2322: Type '{ p: string | number | undefined; }' is not assignable to type 'SomeBase'. +!!! error TS2322: Types of property 'p' are incompatible. +!!! error TS2322: Type 'string | number | undefined' is not assignable to type 'number'. +!!! error TS2322: Type 'undefined' is not assignable to type 'number'. + someBase = /** @type {SomeBase} */(someFakeClass); + + // Type assertion cannot be a type-predicate type + /** @type {number | string} */ + var numOrStr; + /** @type {string} */ + var str; + if(/** @type {numOrStr is string} */(numOrStr === undefined)) { // Error + ~~~~~~~~~~~~~~~ +!!! error TS2352: Type 'boolean' cannot be converted to type 'string | number'. + ~~~~~~~~ +!!! error TS2304: Cannot find name 'numOrStr'. + ~~ +!!! error TS1005: '}' expected. + ~~~~~~~~ +!!! error TS2454: Variable 'numOrStr' is used before being assigned. + str = numOrStr; // Error, no narrowing occurred + ~~~ +!!! error TS2322: Type 'string | number' is not assignable to type 'string'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + ~~~~~~~~ +!!! error TS2454: Variable 'numOrStr' is used before being assigned. + } + + + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocTypeTagCast.js b/tests/baselines/reference/jsdocTypeTagCast.js new file mode 100644 index 00000000000..c2df50cd6dd --- /dev/null +++ b/tests/baselines/reference/jsdocTypeTagCast.js @@ -0,0 +1,151 @@ +//// [tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts] //// + +//// [a.ts] +var W: string; + +//// [b.js] +// @ts-check +var W = /** @type {string} */(/** @type {*} */ (4)); + +var W = /** @type {string} */(4); // Error + +/** @type {*} */ +var a; + +/** @type {string} */ +var s; + +var a = /** @type {*} */("" + 4); +var s = "" + /** @type {*} */(4); + +class SomeBase { + constructor() { + this.p = 42; + } +} +class SomeDerived extends SomeBase { + constructor() { + super(); + this.x = 42; + } +} +class SomeOther { + constructor() { + this.q = 42; + } +} + +function SomeFakeClass() { + /** @type {string|number} */ + this.p = "bar"; +} + +// Type assertion should check for assignability in either direction +var someBase = new SomeBase(); +var someDerived = new SomeDerived(); +var someOther = new SomeOther(); +var someFakeClass = new SomeFakeClass(); + +someBase = /** @type {SomeBase} */(someDerived); +someBase = /** @type {SomeBase} */(someBase); +someBase = /** @type {SomeBase} */(someOther); // Error + +someDerived = /** @type {SomeDerived} */(someDerived); +someDerived = /** @type {SomeDerived} */(someBase); +someDerived = /** @type {SomeDerived} */(someOther); // Error + +someOther = /** @type {SomeOther} */(someDerived); // Error +someOther = /** @type {SomeOther} */(someBase); // Error +someOther = /** @type {SomeOther} */(someOther); + +someFakeClass = someBase; +someFakeClass = someDerived; + +someBase = someFakeClass; // Error +someBase = /** @type {SomeBase} */(someFakeClass); + +// Type assertion cannot be a type-predicate type +/** @type {number | string} */ +var numOrStr; +/** @type {string} */ +var str; +if(/** @type {numOrStr is string} */(numOrStr === undefined)) { // Error + str = numOrStr; // Error, no narrowing occurred +} + + + + +//// [a.js] +var W; +//// [b.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +// @ts-check +var W = ((4)); +var W = (4); // Error +/** @type {*} */ +var a; +/** @type {string} */ +var s; +var a = ("" + 4); +var s = "" + (4); +var SomeBase = (function () { + function SomeBase() { + this.p = 42; + } + return SomeBase; +}()); +var SomeDerived = (function (_super) { + __extends(SomeDerived, _super); + function SomeDerived() { + var _this = _super.call(this) || this; + _this.x = 42; + return _this; + } + return SomeDerived; +}(SomeBase)); +var SomeOther = (function () { + function SomeOther() { + this.q = 42; + } + return SomeOther; +}()); +function SomeFakeClass() { + /** @type {string|number} */ + this.p = "bar"; +} +// Type assertion should check for assignability in either direction +var someBase = new SomeBase(); +var someDerived = new SomeDerived(); +var someOther = new SomeOther(); +var someFakeClass = new SomeFakeClass(); +someBase = (someDerived); +someBase = (someBase); +someBase = (someOther); // Error +someDerived = (someDerived); +someDerived = (someBase); +someDerived = (someOther); // Error +someOther = (someDerived); // Error +someOther = (someBase); // Error +someOther = (someOther); +someFakeClass = someBase; +someFakeClass = someDerived; +someBase = someFakeClass; // Error +someBase = (someFakeClass); +// Type assertion cannot be a type-predicate type +/** @type {number | string} */ +var numOrStr; +/** @type {string} */ +var str; +if ((numOrStr === undefined)) { + str = numOrStr; // Error, no narrowing occurred +} diff --git a/tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts b/tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts new file mode 100644 index 00000000000..60ca43b0533 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocTypeTagCast.ts @@ -0,0 +1,78 @@ +// @allowJS: true +// @suppressOutputPathCheck: true +// @strictNullChecks: true + +// @filename: a.ts +var W: string; + +// @filename: b.js +// @ts-check +var W = /** @type {string} */(/** @type {*} */ (4)); + +var W = /** @type {string} */(4); // Error + +/** @type {*} */ +var a; + +/** @type {string} */ +var s; + +var a = /** @type {*} */("" + 4); +var s = "" + /** @type {*} */(4); + +class SomeBase { + constructor() { + this.p = 42; + } +} +class SomeDerived extends SomeBase { + constructor() { + super(); + this.x = 42; + } +} +class SomeOther { + constructor() { + this.q = 42; + } +} + +function SomeFakeClass() { + /** @type {string|number} */ + this.p = "bar"; +} + +// Type assertion should check for assignability in either direction +var someBase = new SomeBase(); +var someDerived = new SomeDerived(); +var someOther = new SomeOther(); +var someFakeClass = new SomeFakeClass(); + +someBase = /** @type {SomeBase} */(someDerived); +someBase = /** @type {SomeBase} */(someBase); +someBase = /** @type {SomeBase} */(someOther); // Error + +someDerived = /** @type {SomeDerived} */(someDerived); +someDerived = /** @type {SomeDerived} */(someBase); +someDerived = /** @type {SomeDerived} */(someOther); // Error + +someOther = /** @type {SomeOther} */(someDerived); // Error +someOther = /** @type {SomeOther} */(someBase); // Error +someOther = /** @type {SomeOther} */(someOther); + +someFakeClass = someBase; +someFakeClass = someDerived; + +someBase = someFakeClass; // Error +someBase = /** @type {SomeBase} */(someFakeClass); + +// Type assertion cannot be a type-predicate type +/** @type {number | string} */ +var numOrStr; +/** @type {string} */ +var str; +if(/** @type {numOrStr is string} */(numOrStr === undefined)) { // Error + str = numOrStr; // Error, no narrowing occurred +} + + From 65e8da134cb3c14e60297e271bfd5411f3cab71c Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 18 Jul 2017 08:48:12 -0700 Subject: [PATCH 15/34] Add jsdoc to getNullableType now that it's public --- src/compiler/checker.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 35d7307d4bd..35d63b7e2e7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9955,6 +9955,11 @@ namespace ts { neverType; } + /** + * Add undefined or null or both to a type if they are missing. + * @param type - type to add undefined and/or null to if not present + * @param flags - Either TypeFlags.Undefined or TypeFlags.Null, or both + */ function getNullableType(type: Type, flags: TypeFlags): Type { const missing = (flags & ~type.flags) & (TypeFlags.Undefined | TypeFlags.Null); return missing === 0 ? type : From 695514290f46a36714d324aba59d94e3cbb0e1eb Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 18 Jul 2017 09:12:25 -0700 Subject: [PATCH 16/34] Fix #17023 (#17180) * Fix #17023 * Be more general when handling matching references through binding elements * Better cache key, PR feedback * Deeper tests, better cache key handling --- src/compiler/binder.ts | 4 +- src/compiler/checker.ts | 55 +++++- .../reference/destructuringTypeGuardFlow.js | 57 +++++++ .../destructuringTypeGuardFlow.symbols | 143 ++++++++++++++++ .../destructuringTypeGuardFlow.types | 158 ++++++++++++++++++ .../compiler/destructuringTypeGuardFlow.ts | 36 ++++ 6 files changed, 447 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/destructuringTypeGuardFlow.js create mode 100644 tests/baselines/reference/destructuringTypeGuardFlow.symbols create mode 100644 tests/baselines/reference/destructuringTypeGuardFlow.types create mode 100644 tests/cases/compiler/destructuringTypeGuardFlow.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 803c9f60709..b35f7c0196d 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2061,8 +2061,10 @@ namespace ts { case SyntaxKind.Parameter: return bindParameter(node); case SyntaxKind.VariableDeclaration: + return bindVariableDeclarationOrBindingElement(node); case SyntaxKind.BindingElement: - return bindVariableDeclarationOrBindingElement(node); + node.flowNode = currentFlow; + return bindVariableDeclarationOrBindingElement(node); case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: return bindPropertyWorker(node as PropertyDeclaration | PropertySignature); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 11685c27184..61774bc0191 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4089,8 +4089,8 @@ namespace ts { /** Return the inferred type for a binding element */ function getTypeForBindingElement(declaration: BindingElement): Type { - const pattern = declaration.parent; - const parentType = getTypeForBindingElementParent(pattern.parent); + const pattern = declaration.parent; + const parentType = getTypeForBindingElementParent(pattern.parent); // If parent has the unknown (error) type, then so does this binding element if (parentType === unknownType) { return unknownType; @@ -4135,7 +4135,8 @@ namespace ts { // or otherwise the type of the string index signature. const text = getTextOfPropertyName(name); - type = getTypeOfPropertyOfType(parentType, text) || + const declaredType = getTypeOfPropertyOfType(parentType, text); + type = declaredType && getFlowTypeOfReference(declaration, declaredType) || isNumericLiteralName(text) && getIndexTypeOfType(parentType, IndexKind.Number) || getIndexTypeOfType(parentType, IndexKind.String); if (!type) { @@ -10671,7 +10672,7 @@ namespace ts { // The result is undefined if the reference isn't a dotted name. We prefix nodes // occurring in an apparent type position with '@' because the control flow type // of such nodes may be based on the apparent type instead of the declared type. - function getFlowCacheKey(node: Node): string { + function getFlowCacheKey(node: Node): string | undefined { if (node.kind === SyntaxKind.Identifier) { const symbol = getResolvedSymbol(node); return symbol !== unknownSymbol ? (isApparentTypePosition(node) ? "@" : "") + getSymbolId(symbol) : undefined; @@ -10681,7 +10682,14 @@ namespace ts { } if (node.kind === SyntaxKind.PropertyAccessExpression) { const key = getFlowCacheKey((node).expression); - return key && key + "." + (node).name.text; + return key && key + "." + unescapeLeadingUnderscores((node).name.text); + } + if (node.kind === SyntaxKind.BindingElement) { + const container = (node as BindingElement).parent.parent; + const key = container.kind === SyntaxKind.BindingElement ? getFlowCacheKey(container) : (container.initializer && getFlowCacheKey(container.initializer)); + const text = getBindingElementNameText(node as BindingElement); + const result = key && text && (key + "." + text); + return result; } return undefined; } @@ -10697,6 +10705,28 @@ namespace ts { return undefined; } + function getBindingElementNameText(element: BindingElement): string | undefined { + if (element.parent.kind === SyntaxKind.ObjectBindingPattern) { + const name = element.propertyName || element.name; + switch (name.kind) { + case SyntaxKind.Identifier: + return unescapeLeadingUnderscores(name.text); + case SyntaxKind.ComputedPropertyName: + if (isComputedNonLiteralName(name as PropertyName)) return undefined; + return (name.expression as LiteralExpression).text; + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + return name.text; + default: + // Per types, array and object binding patterns remain, however they should never be present if propertyName is not defined + Debug.fail("Unexpected name kind for binding element name"); + } + } + else { + return "" + element.parent.elements.indexOf(element); + } + } + function isMatchingReference(source: Node, target: Node): boolean { switch (source.kind) { case SyntaxKind.Identifier: @@ -10711,6 +10741,17 @@ namespace ts { return target.kind === SyntaxKind.PropertyAccessExpression && (source).name.text === (target).name.text && isMatchingReference((source).expression, (target).expression); + case SyntaxKind.BindingElement: + if (target.kind !== SyntaxKind.PropertyAccessExpression) return false; + const t = target as PropertyAccessExpression; + if (t.name.text !== getBindingElementNameText(source as BindingElement)) return false; + if (source.parent.parent.kind === SyntaxKind.BindingElement && isMatchingReference(source.parent.parent, t.expression)) { + return true; + } + if (source.parent.parent.kind === SyntaxKind.VariableDeclaration) { + const maybeId = (source.parent.parent as VariableDeclaration).initializer; + return maybeId && isMatchingReference(maybeId, t.expression); + } } return false; } @@ -11501,6 +11542,10 @@ namespace ts { const cache = flowLoopCaches[id] || (flowLoopCaches[id] = createMap()); if (!key) { key = getFlowCacheKey(reference); + // No cache key is generated when binding patterns are in unnarrowable situations + if (!key) { + return declaredType; + } } const cached = cache.get(key); if (cached) { diff --git a/tests/baselines/reference/destructuringTypeGuardFlow.js b/tests/baselines/reference/destructuringTypeGuardFlow.js new file mode 100644 index 00000000000..15997de7b44 --- /dev/null +++ b/tests/baselines/reference/destructuringTypeGuardFlow.js @@ -0,0 +1,57 @@ +//// [destructuringTypeGuardFlow.ts] +type foo = { + bar: number | null; + baz: string; + nested: { + a: number; + b: string | null; + } +}; + +const aFoo: foo = { bar: 3, baz: "b", nested: { a: 1, b: "y" } }; + +if (aFoo.bar && aFoo.nested.b) { + const { bar, baz, nested: {a, b: text} } = aFoo; + const right: number = aFoo.bar; + const wrong: number = bar; + const another: string = baz; + const aAgain: number = a; + const bAgain: string = text; +} + +type bar = { + elem1: number | null; + elem2: foo | null; +}; + +const bBar = { elem1: 7, elem2: aFoo }; + +if (bBar.elem2 && bBar.elem2.bar && bBar.elem2.nested.b) { + const { bar, baz, nested: {a, b: text} } = bBar.elem2; + const right: number = bBar.elem2.bar; + const wrong: number = bar; + const another: string = baz; + const aAgain: number = a; + const bAgain: string = text; +} + + +//// [destructuringTypeGuardFlow.js] +var aFoo = { bar: 3, baz: "b", nested: { a: 1, b: "y" } }; +if (aFoo.bar && aFoo.nested.b) { + var bar = aFoo.bar, baz = aFoo.baz, _a = aFoo.nested, a = _a.a, text = _a.b; + var right = aFoo.bar; + var wrong = bar; + var another = baz; + var aAgain = a; + var bAgain = text; +} +var bBar = { elem1: 7, elem2: aFoo }; +if (bBar.elem2 && bBar.elem2.bar && bBar.elem2.nested.b) { + var _b = bBar.elem2, bar = _b.bar, baz = _b.baz, _c = _b.nested, a = _c.a, text = _c.b; + var right = bBar.elem2.bar; + var wrong = bar; + var another = baz; + var aAgain = a; + var bAgain = text; +} diff --git a/tests/baselines/reference/destructuringTypeGuardFlow.symbols b/tests/baselines/reference/destructuringTypeGuardFlow.symbols new file mode 100644 index 00000000000..4c601acb0ae --- /dev/null +++ b/tests/baselines/reference/destructuringTypeGuardFlow.symbols @@ -0,0 +1,143 @@ +=== tests/cases/compiler/destructuringTypeGuardFlow.ts === +type foo = { +>foo : Symbol(foo, Decl(destructuringTypeGuardFlow.ts, 0, 0)) + + bar: number | null; +>bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 0, 12)) + + baz: string; +>baz : Symbol(baz, Decl(destructuringTypeGuardFlow.ts, 1, 21)) + + nested: { +>nested : Symbol(nested, Decl(destructuringTypeGuardFlow.ts, 2, 14)) + + a: number; +>a : Symbol(a, Decl(destructuringTypeGuardFlow.ts, 3, 11)) + + b: string | null; +>b : Symbol(b, Decl(destructuringTypeGuardFlow.ts, 4, 14)) + } +}; + +const aFoo: foo = { bar: 3, baz: "b", nested: { a: 1, b: "y" } }; +>aFoo : Symbol(aFoo, Decl(destructuringTypeGuardFlow.ts, 9, 5)) +>foo : Symbol(foo, Decl(destructuringTypeGuardFlow.ts, 0, 0)) +>bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 9, 19)) +>baz : Symbol(baz, Decl(destructuringTypeGuardFlow.ts, 9, 27)) +>nested : Symbol(nested, Decl(destructuringTypeGuardFlow.ts, 9, 37)) +>a : Symbol(a, Decl(destructuringTypeGuardFlow.ts, 9, 47)) +>b : Symbol(b, Decl(destructuringTypeGuardFlow.ts, 9, 53)) + +if (aFoo.bar && aFoo.nested.b) { +>aFoo.bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 0, 12)) +>aFoo : Symbol(aFoo, Decl(destructuringTypeGuardFlow.ts, 9, 5)) +>bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 0, 12)) +>aFoo.nested.b : Symbol(b, Decl(destructuringTypeGuardFlow.ts, 4, 14)) +>aFoo.nested : Symbol(nested, Decl(destructuringTypeGuardFlow.ts, 2, 14)) +>aFoo : Symbol(aFoo, Decl(destructuringTypeGuardFlow.ts, 9, 5)) +>nested : Symbol(nested, Decl(destructuringTypeGuardFlow.ts, 2, 14)) +>b : Symbol(b, Decl(destructuringTypeGuardFlow.ts, 4, 14)) + + const { bar, baz, nested: {a, b: text} } = aFoo; +>bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 12, 9)) +>baz : Symbol(baz, Decl(destructuringTypeGuardFlow.ts, 12, 14)) +>nested : Symbol(nested, Decl(destructuringTypeGuardFlow.ts, 2, 14)) +>a : Symbol(a, Decl(destructuringTypeGuardFlow.ts, 12, 29)) +>b : Symbol(b, Decl(destructuringTypeGuardFlow.ts, 4, 14)) +>text : Symbol(text, Decl(destructuringTypeGuardFlow.ts, 12, 31)) +>aFoo : Symbol(aFoo, Decl(destructuringTypeGuardFlow.ts, 9, 5)) + + const right: number = aFoo.bar; +>right : Symbol(right, Decl(destructuringTypeGuardFlow.ts, 13, 7)) +>aFoo.bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 0, 12)) +>aFoo : Symbol(aFoo, Decl(destructuringTypeGuardFlow.ts, 9, 5)) +>bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 0, 12)) + + const wrong: number = bar; +>wrong : Symbol(wrong, Decl(destructuringTypeGuardFlow.ts, 14, 7)) +>bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 12, 9)) + + const another: string = baz; +>another : Symbol(another, Decl(destructuringTypeGuardFlow.ts, 15, 7)) +>baz : Symbol(baz, Decl(destructuringTypeGuardFlow.ts, 12, 14)) + + const aAgain: number = a; +>aAgain : Symbol(aAgain, Decl(destructuringTypeGuardFlow.ts, 16, 7)) +>a : Symbol(a, Decl(destructuringTypeGuardFlow.ts, 12, 29)) + + const bAgain: string = text; +>bAgain : Symbol(bAgain, Decl(destructuringTypeGuardFlow.ts, 17, 7)) +>text : Symbol(text, Decl(destructuringTypeGuardFlow.ts, 12, 31)) +} + +type bar = { +>bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 18, 1)) + + elem1: number | null; +>elem1 : Symbol(elem1, Decl(destructuringTypeGuardFlow.ts, 20, 12)) + + elem2: foo | null; +>elem2 : Symbol(elem2, Decl(destructuringTypeGuardFlow.ts, 21, 23)) +>foo : Symbol(foo, Decl(destructuringTypeGuardFlow.ts, 0, 0)) + +}; + +const bBar = { elem1: 7, elem2: aFoo }; +>bBar : Symbol(bBar, Decl(destructuringTypeGuardFlow.ts, 25, 5)) +>elem1 : Symbol(elem1, Decl(destructuringTypeGuardFlow.ts, 25, 14)) +>elem2 : Symbol(elem2, Decl(destructuringTypeGuardFlow.ts, 25, 24)) +>aFoo : Symbol(aFoo, Decl(destructuringTypeGuardFlow.ts, 9, 5)) + +if (bBar.elem2 && bBar.elem2.bar && bBar.elem2.nested.b) { +>bBar.elem2 : Symbol(elem2, Decl(destructuringTypeGuardFlow.ts, 25, 24)) +>bBar : Symbol(bBar, Decl(destructuringTypeGuardFlow.ts, 25, 5)) +>elem2 : Symbol(elem2, Decl(destructuringTypeGuardFlow.ts, 25, 24)) +>bBar.elem2.bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 0, 12)) +>bBar.elem2 : Symbol(elem2, Decl(destructuringTypeGuardFlow.ts, 25, 24)) +>bBar : Symbol(bBar, Decl(destructuringTypeGuardFlow.ts, 25, 5)) +>elem2 : Symbol(elem2, Decl(destructuringTypeGuardFlow.ts, 25, 24)) +>bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 0, 12)) +>bBar.elem2.nested.b : Symbol(b, Decl(destructuringTypeGuardFlow.ts, 4, 14)) +>bBar.elem2.nested : Symbol(nested, Decl(destructuringTypeGuardFlow.ts, 2, 14)) +>bBar.elem2 : Symbol(elem2, Decl(destructuringTypeGuardFlow.ts, 25, 24)) +>bBar : Symbol(bBar, Decl(destructuringTypeGuardFlow.ts, 25, 5)) +>elem2 : Symbol(elem2, Decl(destructuringTypeGuardFlow.ts, 25, 24)) +>nested : Symbol(nested, Decl(destructuringTypeGuardFlow.ts, 2, 14)) +>b : Symbol(b, Decl(destructuringTypeGuardFlow.ts, 4, 14)) + + const { bar, baz, nested: {a, b: text} } = bBar.elem2; +>bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 28, 9)) +>baz : Symbol(baz, Decl(destructuringTypeGuardFlow.ts, 28, 14)) +>nested : Symbol(nested, Decl(destructuringTypeGuardFlow.ts, 2, 14)) +>a : Symbol(a, Decl(destructuringTypeGuardFlow.ts, 28, 29)) +>b : Symbol(b, Decl(destructuringTypeGuardFlow.ts, 4, 14)) +>text : Symbol(text, Decl(destructuringTypeGuardFlow.ts, 28, 31)) +>bBar.elem2 : Symbol(elem2, Decl(destructuringTypeGuardFlow.ts, 25, 24)) +>bBar : Symbol(bBar, Decl(destructuringTypeGuardFlow.ts, 25, 5)) +>elem2 : Symbol(elem2, Decl(destructuringTypeGuardFlow.ts, 25, 24)) + + const right: number = bBar.elem2.bar; +>right : Symbol(right, Decl(destructuringTypeGuardFlow.ts, 29, 7)) +>bBar.elem2.bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 0, 12)) +>bBar.elem2 : Symbol(elem2, Decl(destructuringTypeGuardFlow.ts, 25, 24)) +>bBar : Symbol(bBar, Decl(destructuringTypeGuardFlow.ts, 25, 5)) +>elem2 : Symbol(elem2, Decl(destructuringTypeGuardFlow.ts, 25, 24)) +>bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 0, 12)) + + const wrong: number = bar; +>wrong : Symbol(wrong, Decl(destructuringTypeGuardFlow.ts, 30, 7)) +>bar : Symbol(bar, Decl(destructuringTypeGuardFlow.ts, 28, 9)) + + const another: string = baz; +>another : Symbol(another, Decl(destructuringTypeGuardFlow.ts, 31, 7)) +>baz : Symbol(baz, Decl(destructuringTypeGuardFlow.ts, 28, 14)) + + const aAgain: number = a; +>aAgain : Symbol(aAgain, Decl(destructuringTypeGuardFlow.ts, 32, 7)) +>a : Symbol(a, Decl(destructuringTypeGuardFlow.ts, 28, 29)) + + const bAgain: string = text; +>bAgain : Symbol(bAgain, Decl(destructuringTypeGuardFlow.ts, 33, 7)) +>text : Symbol(text, Decl(destructuringTypeGuardFlow.ts, 28, 31)) +} + diff --git a/tests/baselines/reference/destructuringTypeGuardFlow.types b/tests/baselines/reference/destructuringTypeGuardFlow.types new file mode 100644 index 00000000000..e04dfa645c9 --- /dev/null +++ b/tests/baselines/reference/destructuringTypeGuardFlow.types @@ -0,0 +1,158 @@ +=== tests/cases/compiler/destructuringTypeGuardFlow.ts === +type foo = { +>foo : foo + + bar: number | null; +>bar : number | null +>null : null + + baz: string; +>baz : string + + nested: { +>nested : { a: number; b: string | null; } + + a: number; +>a : number + + b: string | null; +>b : string | null +>null : null + } +}; + +const aFoo: foo = { bar: 3, baz: "b", nested: { a: 1, b: "y" } }; +>aFoo : foo +>foo : foo +>{ bar: 3, baz: "b", nested: { a: 1, b: "y" } } : { bar: number; baz: string; nested: { a: number; b: string; }; } +>bar : number +>3 : 3 +>baz : string +>"b" : "b" +>nested : { a: number; b: string; } +>{ a: 1, b: "y" } : { a: number; b: string; } +>a : number +>1 : 1 +>b : string +>"y" : "y" + +if (aFoo.bar && aFoo.nested.b) { +>aFoo.bar && aFoo.nested.b : string | 0 | null +>aFoo.bar : number | null +>aFoo : foo +>bar : number | null +>aFoo.nested.b : string | null +>aFoo.nested : { a: number; b: string | null; } +>aFoo : foo +>nested : { a: number; b: string | null; } +>b : string | null + + const { bar, baz, nested: {a, b: text} } = aFoo; +>bar : number +>baz : string +>nested : any +>a : number +>b : any +>text : string +>aFoo : foo + + const right: number = aFoo.bar; +>right : number +>aFoo.bar : number +>aFoo : foo +>bar : number + + const wrong: number = bar; +>wrong : number +>bar : number + + const another: string = baz; +>another : string +>baz : string + + const aAgain: number = a; +>aAgain : number +>a : number + + const bAgain: string = text; +>bAgain : string +>text : string +} + +type bar = { +>bar : bar + + elem1: number | null; +>elem1 : number | null +>null : null + + elem2: foo | null; +>elem2 : foo | null +>foo : foo +>null : null + +}; + +const bBar = { elem1: 7, elem2: aFoo }; +>bBar : { elem1: number; elem2: foo; } +>{ elem1: 7, elem2: aFoo } : { elem1: number; elem2: foo; } +>elem1 : number +>7 : 7 +>elem2 : foo +>aFoo : foo + +if (bBar.elem2 && bBar.elem2.bar && bBar.elem2.nested.b) { +>bBar.elem2 && bBar.elem2.bar && bBar.elem2.nested.b : string | 0 | null +>bBar.elem2 && bBar.elem2.bar : number | null +>bBar.elem2 : foo +>bBar : { elem1: number; elem2: foo; } +>elem2 : foo +>bBar.elem2.bar : number | null +>bBar.elem2 : foo +>bBar : { elem1: number; elem2: foo; } +>elem2 : foo +>bar : number | null +>bBar.elem2.nested.b : string | null +>bBar.elem2.nested : { a: number; b: string | null; } +>bBar.elem2 : foo +>bBar : { elem1: number; elem2: foo; } +>elem2 : foo +>nested : { a: number; b: string | null; } +>b : string | null + + const { bar, baz, nested: {a, b: text} } = bBar.elem2; +>bar : number +>baz : string +>nested : any +>a : number +>b : any +>text : string +>bBar.elem2 : foo +>bBar : { elem1: number; elem2: foo; } +>elem2 : foo + + const right: number = bBar.elem2.bar; +>right : number +>bBar.elem2.bar : number +>bBar.elem2 : foo +>bBar : { elem1: number; elem2: foo; } +>elem2 : foo +>bar : number + + const wrong: number = bar; +>wrong : number +>bar : number + + const another: string = baz; +>another : string +>baz : string + + const aAgain: number = a; +>aAgain : number +>a : number + + const bAgain: string = text; +>bAgain : string +>text : string +} + diff --git a/tests/cases/compiler/destructuringTypeGuardFlow.ts b/tests/cases/compiler/destructuringTypeGuardFlow.ts new file mode 100644 index 00000000000..b0a0b18948d --- /dev/null +++ b/tests/cases/compiler/destructuringTypeGuardFlow.ts @@ -0,0 +1,36 @@ +// @strictNullChecks: true +type foo = { + bar: number | null; + baz: string; + nested: { + a: number; + b: string | null; + } +}; + +const aFoo: foo = { bar: 3, baz: "b", nested: { a: 1, b: "y" } }; + +if (aFoo.bar && aFoo.nested.b) { + const { bar, baz, nested: {a, b: text} } = aFoo; + const right: number = aFoo.bar; + const wrong: number = bar; + const another: string = baz; + const aAgain: number = a; + const bAgain: string = text; +} + +type bar = { + elem1: number | null; + elem2: foo | null; +}; + +const bBar = { elem1: 7, elem2: aFoo }; + +if (bBar.elem2 && bBar.elem2.bar && bBar.elem2.nested.b) { + const { bar, baz, nested: {a, b: text} } = bBar.elem2; + const right: number = bBar.elem2.bar; + const wrong: number = bar; + const another: string = baz; + const aAgain: number = a; + const bAgain: string = text; +} From 068b17a1b82b85bab2918725f39fc16c22b9b82f Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 18 Jul 2017 09:47:19 -0700 Subject: [PATCH 17/34] ParameterDeclaration: `name` may be undefined (#17074) --- src/compiler/types.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 244900de2d1..581e9161ed5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -673,7 +673,7 @@ namespace ts { kind: SyntaxKind.Parameter; parent?: SignatureDeclaration; dotDotDotToken?: DotDotDotToken; // Present on rest parameter - name: BindingName; // Declared parameter name + name?: BindingName; // Declared parameter name. Missing if this is a parameter in a JSDocFunctionType. questionToken?: QuestionToken; // Present on optional parameter type?: TypeNode; // Optional type annotation initializer?: Expression; // Optional initializer @@ -751,7 +751,7 @@ namespace ts { export interface VariableLikeDeclaration extends NamedDeclaration { propertyName?: PropertyName; dotDotDotToken?: DotDotDotToken; - name: DeclarationName; + name?: DeclarationName; // May be missing for ParameterDeclaration, see comment there questionToken?: QuestionToken; type?: TypeNode; initializer?: Expression; From 95f5bc1ee0637af0ad3c51946629e5d127df1ac6 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 18 Jul 2017 10:01:22 -0700 Subject: [PATCH 18/34] Add unknown property to test of contextual this type --- .../reference/contextualThisTypeInJavascript.symbols | 3 +++ .../reference/contextualThisTypeInJavascript.types | 7 ++++++- .../types/thisType/contextualThisTypeInJavascript.ts | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/baselines/reference/contextualThisTypeInJavascript.symbols b/tests/baselines/reference/contextualThisTypeInJavascript.symbols index 6c146d654d0..c1339014a88 100644 --- a/tests/baselines/reference/contextualThisTypeInJavascript.symbols +++ b/tests/baselines/reference/contextualThisTypeInJavascript.symbols @@ -20,6 +20,9 @@ const obj = { >this.method : Symbol(method, Decl(context.js, 1, 12)) >this : Symbol(obj, Decl(context.js, 0, 11)) >method : Symbol(method, Decl(context.js, 1, 12)) + + this.unknown; // ok, obj has a string indexer +>this : Symbol(obj, Decl(context.js, 0, 11)) } } diff --git a/tests/baselines/reference/contextualThisTypeInJavascript.types b/tests/baselines/reference/contextualThisTypeInJavascript.types index c3ec4123071..355b7295e6e 100644 --- a/tests/baselines/reference/contextualThisTypeInJavascript.types +++ b/tests/baselines/reference/contextualThisTypeInJavascript.types @@ -1,7 +1,7 @@ === tests/cases/conformance/types/thisType/context.js === const obj = { >obj : { [x: string]: any; prop: number; method(): void; } ->{ prop: 2, method() { this; this.prop; this.method; }} : { [x: string]: any; prop: number; method(): void; } +>{ prop: 2, method() { this; this.prop; this.method; this.unknown; // ok, obj has a string indexer }} : { [x: string]: any; prop: number; method(): void; } prop: 2, >prop : number @@ -22,6 +22,11 @@ const obj = { >this.method : () => void >this : { [x: string]: any; prop: number; method(): void; } >method : () => void + + this.unknown; // ok, obj has a string indexer +>this.unknown : any +>this : { [x: string]: any; prop: number; method(): void; } +>unknown : any } } diff --git a/tests/cases/conformance/types/thisType/contextualThisTypeInJavascript.ts b/tests/cases/conformance/types/thisType/contextualThisTypeInJavascript.ts index 8ccbc65f545..53e6c1bae7e 100644 --- a/tests/cases/conformance/types/thisType/contextualThisTypeInJavascript.ts +++ b/tests/cases/conformance/types/thisType/contextualThisTypeInJavascript.ts @@ -8,5 +8,6 @@ const obj = { this; this.prop; this.method; + this.unknown; // ok, obj has a string indexer } } From 0a8ddca775ddcf0ec11616e3deea732e348fe3e8 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 18 Jul 2017 10:22:52 -0700 Subject: [PATCH 19/34] getJSDocParameterTags: no need to handle JSDocFunctionType, just return undefined (#16837) * getJSDocParameterTags: no need to handle JSDocFunctionType, just return undefined * Fix type error --- src/compiler/utilities.ts | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ab0afb12820..a84e8eb8538 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1540,27 +1540,9 @@ namespace ts { } export function getJSDocParameterTags(param: ParameterDeclaration): JSDocParameterTag[] | undefined { - const func = param.parent; - const tags = getJSDocTags(func); - if (!tags) return undefined; - - if (!param.name) { - // this is an anonymous jsdoc param from a `function(type1, type2): type3` specification - const paramIndex = func.parameters.indexOf(param); - Debug.assert(paramIndex !== -1); - let curParamIndex = 0; - for (const tag of tags) { - if (isJSDocParameterTag(tag)) { - if (curParamIndex === paramIndex) { - return [tag]; - } - curParamIndex++; - } - } - } - else if (param.name.kind === SyntaxKind.Identifier) { - const name = (param.name as Identifier).text; - return tags.filter((tag): tag is JSDocParameterTag => isJSDocParameterTag(tag) && tag.name.text === name) as JSDocParameterTag[]; + if (param.name && isIdentifier(param.name)) { + const name = param.name.text; + return getJSDocTags(param.parent).filter((tag): tag is JSDocParameterTag => isJSDocParameterTag(tag) && tag.name.text === name) as JSDocParameterTag[]; } else { // TODO: it's a destructured parameter, so it should look up an "object type" series of multiple lines From 80b19a09a1b205631c5772dbcfb0000a31807b0e Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 18 Jul 2017 10:26:11 -0700 Subject: [PATCH 20/34] Introduce a ReadonlyMap interface and use it in core.ts (#17161) --- src/compiler/core.ts | 22 +++++++++++----------- src/compiler/types.ts | 28 ++++++++++++++++++---------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 6da3a5b77aa..fa54abd4038 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -585,7 +585,7 @@ namespace ts { return result; } - export function mapEntries(map: Map, f: (key: string, value: T) => [string, U]): Map { + export function mapEntries(map: ReadonlyMap, f: (key: string, value: T) => [string, U]): Map { if (!map) { return undefined; } @@ -998,9 +998,9 @@ namespace ts { * Calls `callback` for each entry in the map, returning the first truthy result. * Use `map.forEach` instead for normal iteration. */ - export function forEachEntry(map: UnderscoreEscapedMap, callback: (value: T, key: __String) => U | undefined): U | undefined; - export function forEachEntry(map: Map, callback: (value: T, key: string) => U | undefined): U | undefined; - export function forEachEntry(map: UnderscoreEscapedMap | Map, callback: (value: T, key: (string & __String)) => U | undefined): U | undefined { + export function forEachEntry(map: ReadonlyUnderscoreEscapedMap, callback: (value: T, key: __String) => U | undefined): U | undefined; + export function forEachEntry(map: ReadonlyMap, callback: (value: T, key: string) => U | undefined): U | undefined; + export function forEachEntry(map: ReadonlyUnderscoreEscapedMap | ReadonlyMap, callback: (value: T, key: (string & __String)) => U | undefined): U | undefined { const iterator = map.entries(); for (let { value: pair, done } = iterator.next(); !done; { value: pair, done } = iterator.next()) { const [key, value] = pair; @@ -1013,9 +1013,9 @@ namespace ts { } /** `forEachEntry` for just keys. */ - export function forEachKey(map: UnderscoreEscapedMap<{}>, callback: (key: __String) => T | undefined): T | undefined; - export function forEachKey(map: Map<{}>, callback: (key: string) => T | undefined): T | undefined; - export function forEachKey(map: UnderscoreEscapedMap<{}> | Map<{}>, callback: (key: string & __String) => T | undefined): T | undefined { + export function forEachKey(map: ReadonlyUnderscoreEscapedMap<{}>, callback: (key: __String) => T | undefined): T | undefined; + export function forEachKey(map: ReadonlyMap<{}>, callback: (key: string) => T | undefined): T | undefined; + export function forEachKey(map: ReadonlyUnderscoreEscapedMap<{}> | ReadonlyMap<{}>, callback: (key: string & __String) => T | undefined): T | undefined { const iterator = map.keys(); for (let { value: key, done } = iterator.next(); !done; { value: key, done } = iterator.next()) { const result = callback(key as string & __String); @@ -1027,8 +1027,8 @@ namespace ts { } /** Copy entries from `source` to `target`. */ - export function copyEntries(source: UnderscoreEscapedMap, target: UnderscoreEscapedMap): void; - export function copyEntries(source: Map, target: Map): void; + export function copyEntries(source: ReadonlyUnderscoreEscapedMap, target: UnderscoreEscapedMap): void; + export function copyEntries(source: ReadonlyMap, target: Map): void; export function copyEntries | Map>(source: U, target: U): void { (source as Map).forEach((value, key) => { (target as Map).set(key, value); @@ -1106,8 +1106,8 @@ namespace ts { } export function cloneMap(map: SymbolTable): SymbolTable; - export function cloneMap(map: Map): Map; - export function cloneMap(map: Map | SymbolTable): Map | SymbolTable { + export function cloneMap(map: ReadonlyMap): Map; + export function cloneMap(map: ReadonlyMap | SymbolTable): Map | SymbolTable { const clone = createMap(); copyEntries(map as Map, clone); return clone; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 581e9161ed5..9aa6c07816e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -8,13 +8,10 @@ namespace ts { [index: string]: T; } - /** ES6 Map interface. */ - export interface Map { + /** ES6 Map interface, only read methods included. */ + export interface ReadonlyMap { get(key: string): T | undefined; has(key: string): boolean; - set(key: string, value: T): this; - delete(key: string): boolean; - clear(): void; forEach(action: (value: T, key: string) => void): void; readonly size: number; keys(): Iterator; @@ -22,6 +19,13 @@ namespace ts { entries(): Iterator<[string, T]>; } + /** ES6 Map interface. */ + export interface Map extends ReadonlyMap { + set(key: string, value: T): this; + delete(key: string): boolean; + clear(): void; + } + /** ES6 Iterator type. */ export interface Iterator { next(): { value: T, done: false } | { value: never, done: true }; @@ -2983,13 +2987,10 @@ namespace ts { */ export type __String = (string & { __escapedIdentifier: void }) | (void & { __escapedIdentifier: void }) | InternalSymbolName; - /** EscapedStringMap based on ES6 Map interface. */ - export interface UnderscoreEscapedMap { + /** ReadonlyMap where keys are `__String`s. */ + export interface ReadonlyUnderscoreEscapedMap { get(key: __String): T | undefined; has(key: __String): boolean; - set(key: __String, value: T): this; - delete(key: __String): boolean; - clear(): void; forEach(action: (value: T, key: __String) => void): void; readonly size: number; keys(): Iterator<__String>; @@ -2997,6 +2998,13 @@ namespace ts { entries(): Iterator<[__String, T]>; } + /** Map where keys are `__String`s. */ + export interface UnderscoreEscapedMap extends ReadonlyUnderscoreEscapedMap { + set(key: __String, value: T): this; + delete(key: __String): boolean; + clear(): void; + } + /** SymbolTable based on ES6 Map interface. */ export type SymbolTable = UnderscoreEscapedMap; From 194c2bc2ca806f5f1014113329e33207f683037c Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 18 Jul 2017 10:38:21 -0700 Subject: [PATCH 21/34] Make NodeArray readonly (#17213) * Make NodeArray readonly * Fix bug: use emptyArray instead of undefined * Fix bug: Don't expose MutableNodeArray * Undo trailing whitespace changes --- src/compiler/binder.ts | 2 +- src/compiler/checker.ts | 56 +-- src/compiler/declarationEmitter.ts | 12 +- src/compiler/emitter.ts | 2 +- src/compiler/factory.ts | 438 +++++++++++++++------ src/compiler/parser.ts | 14 +- src/compiler/transformers/destructuring.ts | 2 +- src/compiler/transformers/es2015.ts | 4 +- src/compiler/transformers/esnext.ts | 2 +- src/compiler/transformers/generators.ts | 2 +- src/compiler/transformers/jsx.ts | 2 +- src/compiler/transformers/ts.ts | 20 +- src/compiler/types.ts | 5 +- src/compiler/utilities.ts | 4 +- src/compiler/visitor.ts | 12 +- src/harness/unittests/textChanges.ts | 6 +- src/services/codefixes/helpers.ts | 10 +- src/services/completions.ts | 34 +- src/services/documentHighlights.ts | 11 +- src/services/formatting/formatting.ts | 2 +- src/services/formatting/smartIndenter.ts | 4 +- src/services/goToDefinition.ts | 2 +- src/services/jsDoc.ts | 4 +- 23 files changed, 446 insertions(+), 204 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b35f7c0196d..b07812d75ed 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1327,7 +1327,7 @@ namespace ts { function bindInitializedVariableFlow(node: VariableDeclaration | ArrayBindingElement) { const name = !isOmittedExpression(node) ? node.name : undefined; if (isBindingPattern(name)) { - for (const child of name.elements) { + for (const child of name.elements) { bindInitializedVariableFlow(child); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 689893d70b5..5d01000f23b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2670,7 +2670,7 @@ namespace ts { entityName = nameIdentifier; } - let typeArgumentNodes: TypeNode[] | undefined; + let typeArgumentNodes: ReadonlyArray | undefined; if (typeArguments.length > 0) { const typeParameterCount = (type.target.typeParameters || emptyArray).length; typeArgumentNodes = mapToTypeNodes(typeArguments.slice(i, typeParameterCount), context); @@ -2907,7 +2907,7 @@ namespace ts { function createEntityNameFromSymbolChain(chain: Symbol[], index: number): EntityName { Debug.assert(chain && 0 <= index && index < chain.length); const symbol = chain[index]; - let typeParameterNodes: TypeNode[] | undefined; + let typeParameterNodes: ReadonlyArray | undefined; if (context.flags & NodeBuilderFlags.WriteTypeParametersInQualifiedName && index > 0) { const parentSymbol = chain[index - 1]; let typeParameters: TypeParameter[]; @@ -3667,7 +3667,7 @@ namespace ts { } } - function buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { + function buildDisplayForTypeParametersAndDelimiters(typeParameters: ReadonlyArray, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { if (typeParameters && typeParameters.length) { writePunctuation(writer, SyntaxKind.LessThanToken); buildDisplayForCommaSeparatedList(typeParameters, writer, p => buildTypeParameterDisplay(p, writer, enclosingDeclaration, flags, symbolStack)); @@ -3675,7 +3675,7 @@ namespace ts { } } - function buildDisplayForCommaSeparatedList(list: T[], writer: SymbolWriter, action: (item: T) => void) { + function buildDisplayForCommaSeparatedList(list: ReadonlyArray, writer: SymbolWriter, action: (item: T) => void) { for (let i = 0; i < list.length; i++) { if (i > 0) { writePunctuation(writer, SyntaxKind.CommaToken); @@ -3685,7 +3685,7 @@ namespace ts { } } - function buildDisplayForTypeArgumentsAndDelimiters(typeParameters: TypeParameter[], mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node) { + function buildDisplayForTypeArgumentsAndDelimiters(typeParameters: ReadonlyArray, mapper: TypeMapper, writer: SymbolWriter, enclosingDeclaration?: Node) { if (typeParameters && typeParameters.length) { writePunctuation(writer, SyntaxKind.LessThanToken); let flags = TypeFormatFlags.InFirstTypeArgument; @@ -4738,7 +4738,7 @@ namespace ts { // Appends the type parameters given by a list of declarations to a set of type parameters and returns the resulting set. // The function allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set // in-place and returns the same array. - function appendTypeParameters(typeParameters: TypeParameter[], declarations: TypeParameterDeclaration[]): TypeParameter[] { + function appendTypeParameters(typeParameters: TypeParameter[], declarations: ReadonlyArray): TypeParameter[] { for (const declaration of declarations) { const tp = getDeclaredTypeOfTypeParameter(getSymbolOfNode(declaration)); if (!typeParameters) { @@ -4825,14 +4825,14 @@ namespace ts { return getClassExtendsHeritageClauseElement(type.symbol.valueDeclaration); } - function getConstructorsForTypeArguments(type: Type, typeArgumentNodes: TypeNode[], location: Node): Signature[] { + function getConstructorsForTypeArguments(type: Type, typeArgumentNodes: ReadonlyArray, location: Node): Signature[] { const typeArgCount = length(typeArgumentNodes); const isJavaScript = isInJavaScriptFile(location); return filter(getSignaturesOfType(type, SignatureKind.Construct), sig => (isJavaScript || typeArgCount >= getMinTypeArgumentCount(sig.typeParameters)) && typeArgCount <= length(sig.typeParameters)); } - function getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: TypeNode[], location: Node): Signature[] { + function getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: ReadonlyArray, location: Node): Signature[] { const signatures = getConstructorsForTypeArguments(type, typeArgumentNodes, location); const typeArguments = map(typeArgumentNodes, getTypeFromTypeNode); return sameMap(signatures, sig => some(sig.typeParameters) ? getSignatureInstantiation(sig, typeArguments) : sig); @@ -14906,7 +14906,7 @@ namespace ts { } } - function getSpreadArgumentIndex(args: Expression[]): number { + function getSpreadArgumentIndex(args: ReadonlyArray): number { for (let i = 0; i < args.length; i++) { const arg = args[i]; if (arg && arg.kind === SyntaxKind.SpreadElement) { @@ -14916,7 +14916,7 @@ namespace ts { return -1; } - function hasCorrectArity(node: CallLikeExpression, args: Expression[], signature: Signature, signatureHelpTrailingComma = false) { + function hasCorrectArity(node: CallLikeExpression, args: ReadonlyArray, signature: Signature, signatureHelpTrailingComma = false) { let argCount: number; // Apparent number of arguments we will have in this call let typeArguments: NodeArray; // Type arguments (undefined if none) let callIsIncomplete: boolean; // In incomplete call we want to be lenient when we have too few arguments @@ -15027,7 +15027,7 @@ namespace ts { return getSignatureInstantiation(signature, getInferredTypes(context)); } - function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: Expression[], excludeArgument: boolean[], context: InferenceContext): Type[] { + function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray, excludeArgument: boolean[], context: InferenceContext): Type[] { // Clear out all the inference results from the last time inferTypeArguments was called on this context for (const inference of context.inferences) { // As an optimization, we don't have to clear (and later recompute) inferred types @@ -15115,7 +15115,7 @@ namespace ts { return getInferredTypes(context); } - function checkTypeArguments(signature: Signature, typeArgumentNodes: TypeNode[], typeArgumentTypes: Type[], reportErrors: boolean, headMessage?: DiagnosticMessage): boolean { + function checkTypeArguments(signature: Signature, typeArgumentNodes: ReadonlyArray, typeArgumentTypes: Type[], reportErrors: boolean, headMessage?: DiagnosticMessage): boolean { const typeParameters = signature.typeParameters; let typeArgumentsAreAssignable = true; let mapper: TypeMapper; @@ -15179,7 +15179,13 @@ namespace ts { return checkTypeRelatedTo(attributesType, paramType, relation, /*errorNode*/ undefined, headMessage); } - function checkApplicableSignature(node: CallLikeExpression, args: Expression[], signature: Signature, relation: Map, excludeArgument: boolean[], reportErrors: boolean) { + function checkApplicableSignature( + node: CallLikeExpression, + args: ReadonlyArray, + signature: Signature, + relation: Map, + excludeArgument: boolean[], + reportErrors: boolean) { if (isJsxOpeningLikeElement(node)) { return checkApplicableSignatureForJsxOpeningLikeElement(node, signature, relation); } @@ -15247,16 +15253,16 @@ namespace ts { * If 'node' is a Decorator, the argument list will be `undefined`, and its arguments and types * will be supplied from calls to `getEffectiveArgumentCount` and `getEffectiveArgumentType`. */ - function getEffectiveCallArguments(node: CallLikeExpression): Expression[] { - let args: Expression[]; + function getEffectiveCallArguments(node: CallLikeExpression): ReadonlyArray { if (node.kind === SyntaxKind.TaggedTemplateExpression) { const template = (node).template; - args = [undefined]; + const args: Expression[] = [undefined]; if (template.kind === SyntaxKind.TemplateExpression) { forEach((template).templateSpans, span => { args.push(span.expression); }); } + return args; } else if (node.kind === SyntaxKind.Decorator) { // For a decorator, we return undefined as we will determine @@ -15265,13 +15271,11 @@ namespace ts { return undefined; } else if (isJsxOpeningLikeElement(node)) { - args = node.attributes.properties.length > 0 ? [node.attributes] : emptyArray; + return node.attributes.properties.length > 0 ? [node.attributes] : emptyArray; } else { - args = node.arguments || emptyArray; + return node.arguments || emptyArray; } - - return args; } @@ -15288,7 +15292,7 @@ namespace ts { * us to match a property decorator. * Otherwise, the argument count is the length of the 'args' array. */ - function getEffectiveArgumentCount(node: CallLikeExpression, args: Expression[], signature: Signature) { + function getEffectiveArgumentCount(node: CallLikeExpression, args: ReadonlyArray, signature: Signature) { if (node.kind === SyntaxKind.Decorator) { switch (node.parent.kind) { case SyntaxKind.ClassDeclaration: @@ -15520,7 +15524,7 @@ namespace ts { /** * Gets the effective argument expression for an argument in a call expression. */ - function getEffectiveArgument(node: CallLikeExpression, args: Expression[], argIndex: number) { + function getEffectiveArgument(node: CallLikeExpression, args: ReadonlyArray, argIndex: number) { // For a decorator or the first argument of a tagged template expression we return undefined. if (node.kind === SyntaxKind.Decorator || (argIndex === 0 && node.kind === SyntaxKind.TaggedTemplateExpression)) { @@ -15552,7 +15556,7 @@ namespace ts { const isDecorator = node.kind === SyntaxKind.Decorator; const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node); - let typeArguments: TypeNode[]; + let typeArguments: ReadonlyArray; if (!isTaggedTemplate && !isDecorator && !isJsxOpeningOrSelfClosingElement) { typeArguments = (node).typeArguments; @@ -17065,7 +17069,7 @@ namespace ts { } /** Note: If property cannot be a SpreadAssignment, then allProperties does not need to be provided */ - function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike, allProperties?: ObjectLiteralElementLike[]) { + function checkObjectLiteralDestructuringPropertyAssignment(objectLiteralType: Type, property: ObjectLiteralElementLike, allProperties?: ReadonlyArray) { if (property.kind === SyntaxKind.PropertyAssignment || property.kind === SyntaxKind.ShorthandPropertyAssignment) { const name = (property).name; if (name.kind === SyntaxKind.ComputedPropertyName) { @@ -18509,7 +18513,7 @@ namespace ts { checkDecorators(node); } - function checkTypeArgumentConstraints(typeParameters: TypeParameter[], typeArgumentNodes: TypeNode[]): boolean { + function checkTypeArgumentConstraints(typeParameters: TypeParameter[], typeArgumentNodes: ReadonlyArray): boolean { const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters); let typeArguments: Type[]; let mapper: TypeMapper; @@ -20955,7 +20959,7 @@ namespace ts { /** * Check each type parameter and check that type parameters have no duplicate type parameter declarations */ - function checkTypeParameters(typeParameterDeclarations: TypeParameterDeclaration[]) { + function checkTypeParameters(typeParameterDeclarations: ReadonlyArray) { if (typeParameterDeclarations) { let seenDefault = false; for (let i = 0; i < typeParameterDeclarations.length; i++) { diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index 753080cc6a0..5f4b3175ca9 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -211,7 +211,7 @@ namespace ts { decreaseIndent = newWriter.decreaseIndent; } - function writeAsynchronousModuleElements(nodes: Node[]) { + function writeAsynchronousModuleElements(nodes: ReadonlyArray) { const oldWriter = writer; forEach(nodes, declaration => { let nodeToCheck: Node; @@ -374,13 +374,13 @@ namespace ts { } } - function emitLines(nodes: Node[]) { + function emitLines(nodes: ReadonlyArray) { for (const node of nodes) { emit(node); } } - function emitSeparatedList(nodes: Node[], separator: string, eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) { + function emitSeparatedList(nodes: ReadonlyArray, separator: string, eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) { let currentWriterPos = writer.getTextPos(); for (const node of nodes) { if (!canEmitFn || canEmitFn(node)) { @@ -393,7 +393,7 @@ namespace ts { } } - function emitCommaList(nodes: Node[], eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) { + function emitCommaList(nodes: ReadonlyArray, eachNodeEmitFn: (node: Node) => void, canEmitFn?: (node: Node) => boolean) { emitSeparatedList(nodes, ", ", eachNodeEmitFn, canEmitFn); } @@ -1007,7 +1007,7 @@ namespace ts { return node.parent.kind === SyntaxKind.MethodDeclaration && hasModifier(node.parent, ModifierFlags.Private); } - function emitTypeParameters(typeParameters: TypeParameterDeclaration[]) { + function emitTypeParameters(typeParameters: ReadonlyArray) { function emitTypeParameter(node: TypeParameterDeclaration) { increaseIndent(); emitJsDocComments(node); @@ -1109,7 +1109,7 @@ namespace ts { } } - function emitHeritageClause(typeReferences: ExpressionWithTypeArguments[], isImplementsList: boolean) { + function emitHeritageClause(typeReferences: ReadonlyArray, isImplementsList: boolean) { if (typeReferences) { write(isImplementsList ? " implements " : " extends "); emitCommaList(typeReferences, emitTypeOfTypeReference); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f8c3c6a12af..759fe9f0abc 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2228,7 +2228,7 @@ namespace ts { * Emits any prologue directives at the start of a Statement list, returning the * number of prologue directives written to the output. */ - function emitPrologueDirectives(statements: Node[], startWithNewLine?: boolean, seenPrologueDirectives?: Map): number { + function emitPrologueDirectives(statements: ReadonlyArray, startWithNewLine?: boolean, seenPrologueDirectives?: Map): number { for (let i = 0; i < statements.length; i++) { const statement = statements[i]; if (isPrologueDirective(statement)) { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index c51ebe9fd58..e58f30a215f 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -21,10 +21,12 @@ namespace ts { return updated; } + /* @internal */ export function createNodeArray(elements?: T[], hasTrailingComma?: boolean): MutableNodeArray; + export function createNodeArray(elements?: ReadonlyArray, hasTrailingComma?: boolean): NodeArray; /** * Make `elements` into a `NodeArray`. If `elements` is `undefined`, returns an empty `NodeArray`. */ - export function createNodeArray(elements?: T[], hasTrailingComma?: boolean): NodeArray { + export function createNodeArray(elements?: ReadonlyArray, hasTrailingComma?: boolean): NodeArray { if (elements) { if (isNodeArray(elements)) { return elements; @@ -109,8 +111,8 @@ namespace ts { export function createIdentifier(text: string): Identifier; /* @internal */ - export function createIdentifier(text: string, typeArguments: TypeNode[]): Identifier; - export function createIdentifier(text: string, typeArguments?: TypeNode[]): Identifier { + export function createIdentifier(text: string, typeArguments: ReadonlyArray): Identifier; + export function createIdentifier(text: string, typeArguments?: ReadonlyArray): Identifier { const node = createSynthesizedNode(SyntaxKind.Identifier); node.text = escapeLeadingUnderscores(text); node.originalKeywordKind = text ? stringToToken(text) : SyntaxKind.Unknown; @@ -244,7 +246,14 @@ namespace ts { : node; } - export function createParameter(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: string | BindingName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression) { + export function createParameter( + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + dotDotDotToken: DotDotDotToken | undefined, + name: string | BindingName, + questionToken?: QuestionToken, + type?: TypeNode, + initializer?: Expression) { const node = createSynthesizedNode(SyntaxKind.Parameter); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -256,7 +265,15 @@ namespace ts { return node; } - 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) { + export function updateParameter( + node: ParameterDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | 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 @@ -283,7 +300,12 @@ namespace ts { // Type Elements - export function createPropertySignature(modifiers: Modifier[] | undefined, name: PropertyName | string, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined): PropertySignature { + export function createPropertySignature( + modifiers: ReadonlyArray | undefined, + name: PropertyName | string, + questionToken: QuestionToken | undefined, + type: TypeNode | undefined, + initializer: Expression | undefined): PropertySignature { const node = createSynthesizedNode(SyntaxKind.PropertySignature) as PropertySignature; node.modifiers = asNodeArray(modifiers); node.name = asName(name); @@ -293,7 +315,13 @@ namespace ts { return node; } - export function updatePropertySignature(node: PropertySignature, modifiers: Modifier[] | undefined, name: PropertyName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined) { + export function updatePropertySignature( + node: PropertySignature, + modifiers: ReadonlyArray | undefined, + name: PropertyName, + questionToken: QuestionToken | undefined, + type: TypeNode | undefined, + initializer: Expression | undefined) { return node.modifiers !== modifiers || node.name !== name || node.questionToken !== questionToken @@ -303,7 +331,13 @@ namespace ts { : node; } - export function createProperty(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | PropertyName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined) { + export function createProperty( + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: string | PropertyName, + questionToken: QuestionToken | undefined, + type: TypeNode | undefined, + initializer: Expression | undefined) { const node = createSynthesizedNode(SyntaxKind.PropertyDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -314,7 +348,14 @@ namespace ts { return node; } - export function updateProperty(node: PropertyDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | PropertyName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined) { + export function updateProperty( + node: PropertyDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: string | PropertyName, + questionToken: QuestionToken | undefined, + type: TypeNode | undefined, + initializer: Expression | undefined) { return node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name @@ -325,7 +366,12 @@ namespace ts { : node; } - export function createMethodSignature(typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, name: string | PropertyName, questionToken: QuestionToken | undefined) { + export function createMethodSignature( + typeParameters: ReadonlyArray | undefined, + parameters: ReadonlyArray, + type: TypeNode | undefined, + name: string | PropertyName, + questionToken: QuestionToken | undefined) { const node = createSignatureDeclaration(SyntaxKind.MethodSignature, typeParameters, parameters, type) as MethodSignature; node.name = asName(name); node.questionToken = questionToken; @@ -342,7 +388,16 @@ namespace ts { : node; } - export function createMethod(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) { + export function createMethod( + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + asteriskToken: AsteriskToken | undefined, + name: string | PropertyName, + questionToken: QuestionToken | undefined, + typeParameters: ReadonlyArray | undefined, + parameters: ReadonlyArray, + type: TypeNode | undefined, + body: Block | undefined) { const node = createSynthesizedNode(SyntaxKind.MethodDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -356,7 +411,17 @@ namespace ts { return node; } - 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) { + export function updateMethod( + node: MethodDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + asteriskToken: AsteriskToken | undefined, + name: PropertyName, + questionToken: QuestionToken | undefined, + typeParameters: ReadonlyArray | undefined, + parameters: ReadonlyArray, + type: TypeNode | undefined, + body: Block | undefined) { return node.decorators !== decorators || node.modifiers !== modifiers || node.asteriskToken !== asteriskToken @@ -370,7 +435,7 @@ namespace ts { : node; } - export function createConstructor(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, parameters: ParameterDeclaration[], body: Block | undefined) { + export function createConstructor(decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, parameters: ReadonlyArray, body: Block | undefined) { const node = createSynthesizedNode(SyntaxKind.Constructor); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -381,7 +446,12 @@ namespace ts { return node; } - export function updateConstructor(node: ConstructorDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, parameters: ParameterDeclaration[], body: Block | undefined) { + export function updateConstructor( + node: ConstructorDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + parameters: ReadonlyArray, + body: Block | undefined) { return node.decorators !== decorators || node.modifiers !== modifiers || node.parameters !== parameters @@ -390,7 +460,13 @@ namespace ts { : node; } - export function createGetAccessor(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | PropertyName, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined) { + export function createGetAccessor( + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: string | PropertyName, + parameters: ReadonlyArray, + type: TypeNode | undefined, + body: Block | undefined) { const node = createSynthesizedNode(SyntaxKind.GetAccessor); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -402,7 +478,14 @@ namespace ts { return node; } - export function updateGetAccessor(node: GetAccessorDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: PropertyName, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined) { + export function updateGetAccessor( + node: GetAccessorDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: PropertyName, + parameters: ReadonlyArray, + type: TypeNode | undefined, + body: Block | undefined) { return node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name @@ -413,7 +496,12 @@ namespace ts { : node; } - export function createSetAccessor(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | PropertyName, parameters: ParameterDeclaration[], body: Block | undefined) { + export function createSetAccessor( + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: string | PropertyName, + parameters: ReadonlyArray, + body: Block | undefined) { const node = createSynthesizedNode(SyntaxKind.SetAccessor); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -424,7 +512,13 @@ namespace ts { return node; } - export function updateSetAccessor(node: SetAccessorDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: PropertyName, parameters: ParameterDeclaration[], body: Block | undefined) { + export function updateSetAccessor( + node: SetAccessorDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: PropertyName, + parameters: ReadonlyArray, + body: Block | undefined) { return node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name @@ -450,7 +544,11 @@ namespace ts { return updateSignatureDeclaration(node, typeParameters, parameters, type); } - export function createIndexSignature(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, parameters: ParameterDeclaration[], type: TypeNode): IndexSignatureDeclaration { + export function createIndexSignature( + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + parameters: ReadonlyArray, + type: TypeNode): IndexSignatureDeclaration { const node = createSynthesizedNode(SyntaxKind.IndexSignature) as IndexSignatureDeclaration; node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -459,7 +557,12 @@ namespace ts { return node; } - export function updateIndexSignature(node: IndexSignatureDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, parameters: ParameterDeclaration[], type: TypeNode) { + export function updateIndexSignature( + node: IndexSignatureDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + parameters: ReadonlyArray, + type: TypeNode) { return node.parameters !== parameters || node.type !== type || node.decorators !== decorators @@ -469,7 +572,7 @@ namespace ts { } /* @internal */ - export function createSignatureDeclaration(kind: SyntaxKind, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined) { + export function createSignatureDeclaration(kind: SyntaxKind, typeParameters: ReadonlyArray | undefined, parameters: ReadonlyArray, type: TypeNode | undefined) { const node = createSynthesizedNode(kind) as SignatureDeclaration; node.typeParameters = asNodeArray(typeParameters); node.parameters = asNodeArray(parameters); @@ -505,7 +608,7 @@ namespace ts { : node; } - export function createTypeReferenceNode(typeName: string | EntityName, typeArguments: TypeNode[] | undefined) { + export function createTypeReferenceNode(typeName: string | EntityName, typeArguments: ReadonlyArray | undefined) { const node = createSynthesizedNode(SyntaxKind.TypeReference) as TypeReferenceNode; node.typeName = asName(typeName); node.typeArguments = typeArguments && parenthesizeTypeParameters(typeArguments); @@ -547,7 +650,7 @@ namespace ts { : node; } - export function createTypeLiteralNode(members: TypeElement[]) { + export function createTypeLiteralNode(members: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.TypeLiteral) as TypeLiteralNode; node.members = createNodeArray(members); return node; @@ -571,13 +674,13 @@ namespace ts { : node; } - export function createTupleTypeNode(elementTypes: TypeNode[]) { + export function createTupleTypeNode(elementTypes: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.TupleType) as TupleTypeNode; node.elementTypes = createNodeArray(elementTypes); return node; } - export function updateTypleTypeNode(node: TupleTypeNode, elementTypes: TypeNode[]) { + export function updateTypleTypeNode(node: TupleTypeNode, elementTypes: ReadonlyArray) { return node.elementTypes !== elementTypes ? updateNode(createTupleTypeNode(elementTypes), node) : node; @@ -599,7 +702,7 @@ namespace ts { return updateUnionOrIntersectionTypeNode(node, types); } - export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: TypeNode[]) { + export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: ReadonlyArray) { const node = createSynthesizedNode(kind) as UnionTypeNode | IntersectionTypeNode; node.types = parenthesizeElementTypeMembers(types); return node; @@ -684,25 +787,25 @@ namespace ts { // Binding Patterns - export function createObjectBindingPattern(elements: BindingElement[]) { + export function createObjectBindingPattern(elements: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.ObjectBindingPattern); node.elements = createNodeArray(elements); return node; } - export function updateObjectBindingPattern(node: ObjectBindingPattern, elements: BindingElement[]) { + export function updateObjectBindingPattern(node: ObjectBindingPattern, elements: ReadonlyArray) { return node.elements !== elements ? updateNode(createObjectBindingPattern(elements), node) : node; } - export function createArrayBindingPattern(elements: ArrayBindingElement[]) { + export function createArrayBindingPattern(elements: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.ArrayBindingPattern); node.elements = createNodeArray(elements); return node; } - export function updateArrayBindingPattern(node: ArrayBindingPattern, elements: ArrayBindingElement[]) { + export function updateArrayBindingPattern(node: ArrayBindingPattern, elements: ReadonlyArray) { return node.elements !== elements ? updateNode(createArrayBindingPattern(elements), node) : node; @@ -728,27 +831,27 @@ namespace ts { // Expression - export function createArrayLiteral(elements?: Expression[], multiLine?: boolean) { + export function createArrayLiteral(elements?: ReadonlyArray, multiLine?: boolean) { const node = createSynthesizedNode(SyntaxKind.ArrayLiteralExpression); node.elements = parenthesizeListElements(createNodeArray(elements)); if (multiLine) node.multiLine = true; return node; } - export function updateArrayLiteral(node: ArrayLiteralExpression, elements: Expression[]) { + export function updateArrayLiteral(node: ArrayLiteralExpression, elements: ReadonlyArray) { return node.elements !== elements ? updateNode(createArrayLiteral(elements, node.multiLine), node) : node; } - export function createObjectLiteral(properties?: ObjectLiteralElementLike[], multiLine?: boolean) { + export function createObjectLiteral(properties?: ReadonlyArray, multiLine?: boolean) { const node = createSynthesizedNode(SyntaxKind.ObjectLiteralExpression); node.properties = createNodeArray(properties); if (multiLine) node.multiLine = true; return node; } - export function updateObjectLiteral(node: ObjectLiteralExpression, properties: ObjectLiteralElementLike[]) { + export function updateObjectLiteral(node: ObjectLiteralExpression, properties: ReadonlyArray) { return node.properties !== properties ? updateNode(createObjectLiteral(properties, node.multiLine), node) : node; @@ -785,7 +888,7 @@ namespace ts { : node; } - export function createCall(expression: Expression, typeArguments: TypeNode[] | undefined, argumentsArray: Expression[]) { + export function createCall(expression: Expression, typeArguments: ReadonlyArray | undefined, argumentsArray: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.CallExpression); node.expression = parenthesizeForAccess(expression); node.typeArguments = asNodeArray(typeArguments); @@ -793,7 +896,7 @@ namespace ts { return node; } - export function updateCall(node: CallExpression, expression: Expression, typeArguments: TypeNode[] | undefined, argumentsArray: Expression[]) { + export function updateCall(node: CallExpression, expression: Expression, typeArguments: ReadonlyArray | undefined, argumentsArray: ReadonlyArray) { return node.expression !== expression || node.typeArguments !== typeArguments || node.arguments !== argumentsArray @@ -801,7 +904,7 @@ namespace ts { : node; } - export function createNew(expression: Expression, typeArguments: TypeNode[] | undefined, argumentsArray: Expression[] | undefined) { + export function createNew(expression: Expression, typeArguments: ReadonlyArray | undefined, argumentsArray: ReadonlyArray | undefined) { const node = createSynthesizedNode(SyntaxKind.NewExpression); node.expression = parenthesizeForNew(expression); node.typeArguments = asNodeArray(typeArguments); @@ -809,7 +912,7 @@ namespace ts { return node; } - export function updateNew(node: NewExpression, expression: Expression, typeArguments: TypeNode[] | undefined, argumentsArray: Expression[] | undefined) { + export function updateNew(node: NewExpression, expression: Expression, typeArguments: ReadonlyArray | undefined, argumentsArray: ReadonlyArray | undefined) { return node.expression !== expression || node.typeArguments !== typeArguments || node.arguments !== argumentsArray @@ -857,7 +960,14 @@ namespace ts { : node; } - export function createFunctionExpression(modifiers: Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: string | Identifier | undefined, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block) { + export function createFunctionExpression( + modifiers: ReadonlyArray | undefined, + asteriskToken: AsteriskToken | undefined, + name: string | Identifier | undefined, + typeParameters: ReadonlyArray | undefined, + parameters: ReadonlyArray, + type: TypeNode | undefined, + body: Block) { const node = createSynthesizedNode(SyntaxKind.FunctionExpression); node.modifiers = asNodeArray(modifiers); node.asteriskToken = asteriskToken; @@ -869,7 +979,15 @@ namespace ts { return node; } - export function updateFunctionExpression(node: FunctionExpression, modifiers: Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: Identifier | undefined, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block) { + export function updateFunctionExpression( + node: FunctionExpression, + modifiers: ReadonlyArray | undefined, + asteriskToken: AsteriskToken | undefined, + name: Identifier | undefined, + typeParameters: ReadonlyArray | undefined, + parameters: ReadonlyArray, + type: TypeNode | undefined, + body: Block) { return node.name !== name || node.modifiers !== modifiers || node.asteriskToken !== asteriskToken @@ -881,7 +999,13 @@ namespace ts { : node; } - export function createArrowFunction(modifiers: Modifier[] | undefined, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, equalsGreaterThanToken: EqualsGreaterThanToken | undefined, body: ConciseBody) { + export function createArrowFunction( + modifiers: ReadonlyArray | undefined, + typeParameters: ReadonlyArray | undefined, + parameters: ReadonlyArray, + type: TypeNode | undefined, + equalsGreaterThanToken: EqualsGreaterThanToken | undefined, + body: ConciseBody) { const node = createSynthesizedNode(SyntaxKind.ArrowFunction); node.modifiers = asNodeArray(modifiers); node.typeParameters = asNodeArray(typeParameters); @@ -892,7 +1016,13 @@ namespace ts { return node; } - export function updateArrowFunction(node: ArrowFunction, modifiers: Modifier[] | undefined, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: ConciseBody) { + export function updateArrowFunction( + node: ArrowFunction, + modifiers: ReadonlyArray | undefined, + typeParameters: ReadonlyArray | undefined, + parameters: ReadonlyArray, + type: TypeNode | undefined, + body: ConciseBody) { return node.modifiers !== modifiers || node.typeParameters !== typeParameters || node.parameters !== parameters @@ -1013,14 +1143,14 @@ namespace ts { : node; } - export function createTemplateExpression(head: TemplateHead, templateSpans: TemplateSpan[]) { + export function createTemplateExpression(head: TemplateHead, templateSpans: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.TemplateExpression); node.head = head; node.templateSpans = createNodeArray(templateSpans); return node; } - export function updateTemplateExpression(node: TemplateExpression, head: TemplateHead, templateSpans: TemplateSpan[]) { + export function updateTemplateExpression(node: TemplateExpression, head: TemplateHead, templateSpans: ReadonlyArray) { return node.head !== head || node.templateSpans !== templateSpans ? updateNode(createTemplateExpression(head, templateSpans), node) @@ -1055,7 +1185,12 @@ namespace ts { : node; } - export function createClassExpression(modifiers: Modifier[] | undefined, name: string | Identifier | undefined, typeParameters: TypeParameterDeclaration[] | undefined, heritageClauses: HeritageClause[], members: ClassElement[]) { + export function createClassExpression( + modifiers: ReadonlyArray | undefined, + name: string | Identifier | undefined, + typeParameters: ReadonlyArray | undefined, + heritageClauses: ReadonlyArray, + members: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.ClassExpression); node.decorators = undefined; node.modifiers = asNodeArray(modifiers); @@ -1066,7 +1201,13 @@ namespace ts { return node; } - export function updateClassExpression(node: ClassExpression, modifiers: Modifier[] | undefined, name: Identifier | undefined, typeParameters: TypeParameterDeclaration[] | undefined, heritageClauses: HeritageClause[], members: ClassElement[]) { + export function updateClassExpression( + node: ClassExpression, + modifiers: ReadonlyArray | undefined, + name: Identifier | undefined, + typeParameters: ReadonlyArray | undefined, + heritageClauses: ReadonlyArray, + members: ReadonlyArray) { return node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters @@ -1080,14 +1221,14 @@ namespace ts { return createSynthesizedNode(SyntaxKind.OmittedExpression); } - export function createExpressionWithTypeArguments(typeArguments: TypeNode[], expression: Expression) { + export function createExpressionWithTypeArguments(typeArguments: ReadonlyArray, expression: Expression) { const node = createSynthesizedNode(SyntaxKind.ExpressionWithTypeArguments); node.expression = parenthesizeForAccess(expression); node.typeArguments = asNodeArray(typeArguments); return node; } - export function updateExpressionWithTypeArguments(node: ExpressionWithTypeArguments, typeArguments: TypeNode[], expression: Expression) { + export function updateExpressionWithTypeArguments(node: ExpressionWithTypeArguments, typeArguments: ReadonlyArray, expression: Expression) { return node.typeArguments !== typeArguments || node.expression !== expression ? updateNode(createExpressionWithTypeArguments(typeArguments, expression), node) @@ -1155,20 +1296,20 @@ namespace ts { // Element - export function createBlock(statements: Statement[], multiLine?: boolean): Block { + export function createBlock(statements: ReadonlyArray, multiLine?: boolean): Block { const block = createSynthesizedNode(SyntaxKind.Block); block.statements = createNodeArray(statements); if (multiLine) block.multiLine = multiLine; return block; } - export function updateBlock(node: Block, statements: Statement[]) { + export function updateBlock(node: Block, statements: ReadonlyArray) { return node.statements !== statements ? updateNode(createBlock(statements, node.multiLine), node) : node; } - export function createVariableStatement(modifiers: Modifier[] | undefined, declarationList: VariableDeclarationList | VariableDeclaration[]) { + export function createVariableStatement(modifiers: ReadonlyArray | undefined, declarationList: VariableDeclarationList | ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.VariableStatement); node.decorators = undefined; node.modifiers = asNodeArray(modifiers); @@ -1176,7 +1317,7 @@ namespace ts { return node; } - export function updateVariableStatement(node: VariableStatement, modifiers: Modifier[] | undefined, declarationList: VariableDeclarationList) { + export function updateVariableStatement(node: VariableStatement, modifiers: ReadonlyArray | undefined, declarationList: VariableDeclarationList) { return node.modifiers !== modifiers || node.declarationList !== declarationList ? updateNode(createVariableStatement(modifiers, declarationList), node) @@ -1421,20 +1562,28 @@ namespace ts { : node; } - export function createVariableDeclarationList(declarations: VariableDeclaration[], flags?: NodeFlags) { + export function createVariableDeclarationList(declarations: ReadonlyArray, flags?: NodeFlags) { const node = createSynthesizedNode(SyntaxKind.VariableDeclarationList); node.flags |= flags & NodeFlags.BlockScoped; node.declarations = createNodeArray(declarations); return node; } - export function updateVariableDeclarationList(node: VariableDeclarationList, declarations: VariableDeclaration[]) { + export function updateVariableDeclarationList(node: VariableDeclarationList, declarations: ReadonlyArray) { return node.declarations !== declarations ? updateNode(createVariableDeclarationList(declarations, node.flags), node) : node; } - 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) { + export function createFunctionDeclaration( + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + asteriskToken: AsteriskToken | undefined, + name: string | Identifier | undefined, + typeParameters: ReadonlyArray | undefined, + parameters: ReadonlyArray, + type: TypeNode | undefined, + body: Block | undefined) { const node = createSynthesizedNode(SyntaxKind.FunctionDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -1447,7 +1596,16 @@ namespace ts { return node; } - export function updateFunctionDeclaration(node: FunctionDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: Identifier | undefined, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined) { + export function updateFunctionDeclaration( + node: FunctionDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + asteriskToken: AsteriskToken | undefined, + name: Identifier | undefined, + typeParameters: ReadonlyArray | undefined, + parameters: ReadonlyArray, + type: TypeNode | undefined, + body: Block | undefined) { return node.decorators !== decorators || node.modifiers !== modifiers || node.asteriskToken !== asteriskToken @@ -1460,7 +1618,13 @@ namespace ts { : node; } - export function createClassDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | Identifier | undefined, typeParameters: TypeParameterDeclaration[] | undefined, heritageClauses: HeritageClause[], members: ClassElement[]) { + export function createClassDeclaration( + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: string | Identifier | undefined, + typeParameters: ReadonlyArray | undefined, + heritageClauses: ReadonlyArray, + members: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.ClassDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -1471,7 +1635,14 @@ namespace ts { return node; } - export function updateClassDeclaration(node: ClassDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: Identifier | undefined, typeParameters: TypeParameterDeclaration[] | undefined, heritageClauses: HeritageClause[], members: ClassElement[]) { + export function updateClassDeclaration( + node: ClassDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: Identifier | undefined, + typeParameters: ReadonlyArray | undefined, + heritageClauses: ReadonlyArray, + members: ReadonlyArray) { return node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name @@ -1482,7 +1653,13 @@ namespace ts { : node; } - export function createInterfaceDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | Identifier, typeParameters: TypeParameterDeclaration[] | undefined, heritageClauses: HeritageClause[] | undefined, members: TypeElement[]) { + export function createInterfaceDeclaration( + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: string | Identifier, + typeParameters: ReadonlyArray | undefined, + heritageClauses: ReadonlyArray | undefined, + members: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.InterfaceDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -1493,7 +1670,14 @@ namespace ts { return node; } - export function updateInterfaceDeclaration(node: InterfaceDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: Identifier, typeParameters: TypeParameterDeclaration[] | undefined, heritageClauses: HeritageClause[] | undefined, members: TypeElement[]) { + export function updateInterfaceDeclaration( + node: InterfaceDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: Identifier, + typeParameters: ReadonlyArray | undefined, + heritageClauses: ReadonlyArray | undefined, + members: ReadonlyArray) { return node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name @@ -1504,7 +1688,12 @@ namespace ts { : node; } - export function createTypeAliasDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | Identifier, typeParameters: TypeParameterDeclaration[] | undefined, type: TypeNode) { + export function createTypeAliasDeclaration( + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: string | Identifier, + typeParameters: ReadonlyArray | undefined, + type: TypeNode) { const node = createSynthesizedNode(SyntaxKind.TypeAliasDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -1514,7 +1703,13 @@ namespace ts { return node; } - export function updateTypeAliasDeclaration(node: TypeAliasDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: Identifier, typeParameters: TypeParameterDeclaration[] | undefined, type: TypeNode) { + export function updateTypeAliasDeclaration( + node: TypeAliasDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: Identifier, + typeParameters: ReadonlyArray | undefined, + type: TypeNode) { return node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name @@ -1524,7 +1719,11 @@ namespace ts { : node; } - export function createEnumDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | Identifier, members: EnumMember[]) { + export function createEnumDeclaration( + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: string | Identifier, + members: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.EnumDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -1533,7 +1732,12 @@ namespace ts { return node; } - export function updateEnumDeclaration(node: EnumDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: Identifier, members: EnumMember[]) { + export function updateEnumDeclaration( + node: EnumDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + name: Identifier, + members: ReadonlyArray) { return node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name @@ -1542,7 +1746,7 @@ namespace ts { : node; } - export function createModuleDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: ModuleName, body: ModuleBody | undefined, flags?: NodeFlags) { + export function createModuleDeclaration(decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, name: ModuleName, body: ModuleBody | undefined, flags?: NodeFlags) { const node = createSynthesizedNode(SyntaxKind.ModuleDeclaration); node.flags |= flags & (NodeFlags.Namespace | NodeFlags.NestedNamespace | NodeFlags.GlobalAugmentation); node.decorators = asNodeArray(decorators); @@ -1552,7 +1756,7 @@ namespace ts { return node; } - export function updateModuleDeclaration(node: ModuleDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: ModuleName, body: ModuleBody | undefined) { + export function updateModuleDeclaration(node: ModuleDeclaration, decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, name: ModuleName, body: ModuleBody | undefined) { return node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name @@ -1561,25 +1765,25 @@ namespace ts { : node; } - export function createModuleBlock(statements: Statement[]) { + export function createModuleBlock(statements: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.ModuleBlock); node.statements = createNodeArray(statements); return node; } - export function updateModuleBlock(node: ModuleBlock, statements: Statement[]) { + export function updateModuleBlock(node: ModuleBlock, statements: ReadonlyArray) { return node.statements !== statements ? updateNode(createModuleBlock(statements), node) : node; } - export function createCaseBlock(clauses: CaseOrDefaultClause[]): CaseBlock { + export function createCaseBlock(clauses: ReadonlyArray): CaseBlock { const node = createSynthesizedNode(SyntaxKind.CaseBlock); node.clauses = createNodeArray(clauses); return node; } - export function updateCaseBlock(node: CaseBlock, clauses: CaseOrDefaultClause[]) { + export function updateCaseBlock(node: CaseBlock, clauses: ReadonlyArray) { return node.clauses !== clauses ? updateNode(createCaseBlock(clauses), node) : node; @@ -1597,7 +1801,7 @@ namespace ts { : node; } - export function createImportEqualsDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: string | Identifier, moduleReference: ModuleReference) { + export function createImportEqualsDeclaration(decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, name: string | Identifier, moduleReference: ModuleReference) { const node = createSynthesizedNode(SyntaxKind.ImportEqualsDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -1606,7 +1810,7 @@ namespace ts { return node; } - export function updateImportEqualsDeclaration(node: ImportEqualsDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, name: Identifier, moduleReference: ModuleReference) { + export function updateImportEqualsDeclaration(node: ImportEqualsDeclaration, decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, name: Identifier, moduleReference: ModuleReference) { return node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name @@ -1615,7 +1819,11 @@ namespace ts { : node; } - export function createImportDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, importClause: ImportClause | undefined, moduleSpecifier?: Expression): ImportDeclaration { + export function createImportDeclaration( + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + importClause: ImportClause | undefined, + moduleSpecifier?: Expression): ImportDeclaration { const node = createSynthesizedNode(SyntaxKind.ImportDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -1624,7 +1832,12 @@ namespace ts { return node; } - export function updateImportDeclaration(node: ImportDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression | undefined) { + export function updateImportDeclaration( + node: ImportDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + importClause: ImportClause | undefined, + moduleSpecifier: Expression | undefined) { return node.decorators !== decorators || node.modifiers !== modifiers || node.importClause !== importClause @@ -1659,13 +1872,13 @@ namespace ts { : node; } - export function createNamedImports(elements: ImportSpecifier[]): NamedImports { + export function createNamedImports(elements: ReadonlyArray): NamedImports { const node = createSynthesizedNode(SyntaxKind.NamedImports); node.elements = createNodeArray(elements); return node; } - export function updateNamedImports(node: NamedImports, elements: ImportSpecifier[]) { + export function updateNamedImports(node: NamedImports, elements: ReadonlyArray) { return node.elements !== elements ? updateNode(createNamedImports(elements), node) : node; @@ -1685,7 +1898,7 @@ namespace ts { : node; } - export function createExportAssignment(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, isExportEquals: boolean, expression: Expression) { + export function createExportAssignment(decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, isExportEquals: boolean, expression: Expression) { const node = createSynthesizedNode(SyntaxKind.ExportAssignment); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -1694,7 +1907,7 @@ namespace ts { return node; } - export function updateExportAssignment(node: ExportAssignment, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, expression: Expression) { + export function updateExportAssignment(node: ExportAssignment, decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, expression: Expression) { return node.decorators !== decorators || node.modifiers !== modifiers || node.expression !== expression @@ -1702,7 +1915,7 @@ namespace ts { : node; } - export function createExportDeclaration(decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, exportClause: NamedExports | undefined, moduleSpecifier?: Expression) { + export function createExportDeclaration(decorators: ReadonlyArray | undefined, modifiers: ReadonlyArray | undefined, exportClause: NamedExports | undefined, moduleSpecifier?: Expression) { const node = createSynthesizedNode(SyntaxKind.ExportDeclaration); node.decorators = asNodeArray(decorators); node.modifiers = asNodeArray(modifiers); @@ -1711,7 +1924,12 @@ namespace ts { return node; } - export function updateExportDeclaration(node: ExportDeclaration, decorators: Decorator[] | undefined, modifiers: Modifier[] | undefined, exportClause: NamedExports | undefined, moduleSpecifier: Expression | undefined) { + export function updateExportDeclaration( + node: ExportDeclaration, + decorators: ReadonlyArray | undefined, + modifiers: ReadonlyArray | undefined, + exportClause: NamedExports | undefined, + moduleSpecifier: Expression | undefined) { return node.decorators !== decorators || node.modifiers !== modifiers || node.exportClause !== exportClause @@ -1720,13 +1938,13 @@ namespace ts { : node; } - export function createNamedExports(elements: ExportSpecifier[]) { + export function createNamedExports(elements: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.NamedExports); node.elements = createNodeArray(elements); return node; } - export function updateNamedExports(node: NamedExports, elements: ExportSpecifier[]) { + export function updateNamedExports(node: NamedExports, elements: ReadonlyArray) { return node.elements !== elements ? updateNode(createNamedExports(elements), node) : node; @@ -1762,7 +1980,7 @@ namespace ts { // JSX - export function createJsxElement(openingElement: JsxOpeningElement, children: JsxChild[], closingElement: JsxClosingElement) { + export function createJsxElement(openingElement: JsxOpeningElement, children: ReadonlyArray, closingElement: JsxClosingElement) { const node = createSynthesizedNode(SyntaxKind.JsxElement); node.openingElement = openingElement; node.children = createNodeArray(children); @@ -1770,7 +1988,7 @@ namespace ts { return node; } - export function updateJsxElement(node: JsxElement, openingElement: JsxOpeningElement, children: JsxChild[], closingElement: JsxClosingElement) { + export function updateJsxElement(node: JsxElement, openingElement: JsxOpeningElement, children: ReadonlyArray, closingElement: JsxClosingElement) { return node.openingElement !== openingElement || node.children !== children || node.closingElement !== closingElement @@ -1832,13 +2050,13 @@ namespace ts { : node; } - export function createJsxAttributes(properties: JsxAttributeLike[]) { + export function createJsxAttributes(properties: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.JsxAttributes); node.properties = createNodeArray(properties); return node; } - export function updateJsxAttributes(node: JsxAttributes, properties: JsxAttributeLike[]) { + export function updateJsxAttributes(node: JsxAttributes, properties: ReadonlyArray) { return node.properties !== properties ? updateNode(createJsxAttributes(properties), node) : node; @@ -1871,40 +2089,40 @@ namespace ts { // Clauses - export function createCaseClause(expression: Expression, statements: Statement[]) { + export function createCaseClause(expression: Expression, statements: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.CaseClause); node.expression = parenthesizeExpressionForList(expression); node.statements = createNodeArray(statements); return node; } - export function updateCaseClause(node: CaseClause, expression: Expression, statements: Statement[]) { + export function updateCaseClause(node: CaseClause, expression: Expression, statements: ReadonlyArray) { return node.expression !== expression || node.statements !== statements ? updateNode(createCaseClause(expression, statements), node) : node; } - export function createDefaultClause(statements: Statement[]) { + export function createDefaultClause(statements: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.DefaultClause); node.statements = createNodeArray(statements); return node; } - export function updateDefaultClause(node: DefaultClause, statements: Statement[]) { + export function updateDefaultClause(node: DefaultClause, statements: ReadonlyArray) { return node.statements !== statements ? updateNode(createDefaultClause(statements), node) : node; } - export function createHeritageClause(token: HeritageClause["token"], types: ExpressionWithTypeArguments[]) { + export function createHeritageClause(token: HeritageClause["token"], types: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.HeritageClause); node.token = token; node.types = createNodeArray(types); return node; } - export function updateHeritageClause(node: HeritageClause, types: ExpressionWithTypeArguments[]) { + export function updateHeritageClause(node: HeritageClause, types: ReadonlyArray) { return node.types !== types ? updateNode(createHeritageClause(node.token, types), node) : node; @@ -1985,7 +2203,7 @@ namespace ts { // Top-level nodes - export function updateSourceFileNode(node: SourceFile, statements: Statement[]) { + export function updateSourceFileNode(node: SourceFile, statements: ReadonlyArray) { if (node.statements !== statements) { const updated = createSynthesizedNode(SyntaxKind.SourceFile); updated.flags |= node.flags; @@ -2097,7 +2315,7 @@ namespace ts { return node; } - function flattenCommaElements(node: Expression): Expression | Expression[] { + function flattenCommaElements(node: Expression): Expression | ReadonlyArray { if (nodeIsSynthesized(node) && !isParseTreeNode(node) && !node.original && !node.emitNode && !node.id) { if (node.kind === SyntaxKind.CommaListExpression) { return (node).elements; @@ -2109,13 +2327,13 @@ namespace ts { return node; } - export function createCommaList(elements: Expression[]) { + export function createCommaList(elements: ReadonlyArray) { const node = createSynthesizedNode(SyntaxKind.CommaListExpression); node.elements = createNodeArray(sameFlatMap(elements, flattenCommaElements)); return node; } - export function updateCommaList(node: CommaListExpression, elements: Expression[]) { + export function updateCommaList(node: CommaListExpression, elements: ReadonlyArray) { return node.elements !== elements ? updateNode(createCommaList(elements), node) : node; @@ -2227,7 +2445,7 @@ namespace ts { return typeof value === "string" || typeof value === "number" ? createLiteral(value) : value; } - function asNodeArray(array: T[] | undefined): NodeArray | undefined { + function asNodeArray(array: ReadonlyArray | undefined): NodeArray | undefined { return array ? createNodeArray(array) : undefined; } @@ -2575,7 +2793,7 @@ namespace ts { } } - export function createFunctionCall(func: Expression, thisArg: Expression, argumentsList: Expression[], location?: TextRange) { + export function createFunctionCall(func: Expression, thisArg: Expression, argumentsList: ReadonlyArray, location?: TextRange) { return setTextRange( createCall( createPropertyAccess(func, "call"), @@ -3273,7 +3491,7 @@ namespace ts { * @param ensureUseStrict: boolean determining whether the function need to add prologue-directives * @param visitor: Optional callback used to visit any custom prologue directives. */ - export function addPrologue(target: Statement[], source: Statement[], ensureUseStrict?: boolean, visitor?: (node: Node) => VisitResult): number { + export function addPrologue(target: Statement[], source: ReadonlyArray, ensureUseStrict?: boolean, visitor?: (node: Node) => VisitResult): number { const offset = addStandardPrologue(target, source, ensureUseStrict); return addCustomPrologue(target, source, offset, visitor); } @@ -3284,7 +3502,7 @@ namespace ts { * This function needs to be called whenever we transform the statement * list of a source file, namespace, or function-like body. */ - export function addStandardPrologue(target: Statement[], source: Statement[], ensureUseStrict?: boolean): number { + export function addStandardPrologue(target: Statement[], source: ReadonlyArray, ensureUseStrict?: boolean): number { Debug.assert(target.length === 0, "Prologue directives should be at the first statement in the target statements array"); let foundUseStrict = false; let statementOffset = 0; @@ -3314,7 +3532,7 @@ namespace ts { * This function needs to be called whenever we transform the statement * list of a source file, namespace, or function-like body. */ - export function addCustomPrologue(target: Statement[], source: Statement[], statementOffset: number, visitor?: (node: Node) => VisitResult): number { + export function addCustomPrologue(target: Statement[], source: ReadonlyArray, statementOffset: number, visitor?: (node: Node) => VisitResult): number { const numStatements = source.length; while (statementOffset < numStatements) { const statement = source[statementOffset]; @@ -3329,7 +3547,7 @@ namespace ts { return statementOffset; } - export function startsWithUseStrict(statements: Statement[]) { + export function startsWithUseStrict(statements: ReadonlyArray) { const firstStatement = firstOrUndefined(statements); return firstStatement !== undefined && isPrologueDirective(firstStatement) @@ -3680,21 +3898,21 @@ namespace ts { return member; } - export function parenthesizeElementTypeMembers(members: TypeNode[]) { + export function parenthesizeElementTypeMembers(members: ReadonlyArray) { return createNodeArray(sameMap(members, parenthesizeElementTypeMember)); } - export function parenthesizeTypeParameters(typeParameters: TypeNode[]) { + export function parenthesizeTypeParameters(typeParameters: ReadonlyArray) { if (some(typeParameters)) { - const nodeArray = createNodeArray() as NodeArray; + const params: TypeNode[] = []; for (let i = 0; i < typeParameters.length; ++i) { const entry = typeParameters[i]; - nodeArray.push(i === 0 && isFunctionOrConstructorTypeNode(entry) && entry.typeParameters ? + params.push(i === 0 && isFunctionOrConstructorTypeNode(entry) && entry.typeParameters ? createParenthesizedType(entry) : entry); } - return nodeArray; + return createNodeArray(params); } } @@ -4122,18 +4340,18 @@ namespace ts { /** * Gets the elements of a BindingOrAssignmentPattern */ - export function getElementsOfBindingOrAssignmentPattern(name: BindingOrAssignmentPattern): BindingOrAssignmentElement[] { + export function getElementsOfBindingOrAssignmentPattern(name: BindingOrAssignmentPattern): ReadonlyArray { switch (name.kind) { case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: case SyntaxKind.ArrayLiteralExpression: // `a` in `{a}` // `a` in `[a]` - return name.elements; + return >name.elements; case SyntaxKind.ObjectLiteralExpression: // `a` in `{a}` - return name.properties; + return >name.properties; } } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 4613f49a749..0be7734968f 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1121,8 +1121,8 @@ namespace ts { new TokenConstructor(kind, pos, pos); } - function createNodeArray(elements?: T[], pos?: number): NodeArray { - const array = >(elements || []); + function createNodeArray(elements?: T[], pos?: number): MutableNodeArray { + const array = >(elements || []); if (!(pos >= 0)) { pos = getNodePos(); } @@ -5395,7 +5395,7 @@ namespace ts { } function parseDecorators(): NodeArray { - let decorators: NodeArray; + let decorators: NodeArray & Decorator[]; while (true) { const decoratorStart = getNodePos(); if (!parseOptional(SyntaxKind.AtToken)) { @@ -5426,7 +5426,7 @@ namespace ts { * In such situations, 'permitInvalidConstAsModifier' should be set to true. */ function parseModifiers(permitInvalidConstAsModifier?: boolean): NodeArray | undefined { - let modifiers: NodeArray | undefined; + let modifiers: MutableNodeArray | undefined; while (true) { const modifierStart = scanner.getStartPos(); const modifierKind = token(); @@ -6165,7 +6165,7 @@ namespace ts { Debug.assert(start <= end); Debug.assert(end <= content.length); - let tags: NodeArray; + let tags: MutableNodeArray; const comments: string[] = []; let result: JSDoc; @@ -6673,9 +6673,9 @@ namespace ts { const propertyTag = parseParameterOrPropertyTag(atToken, tagName, /*shouldParseParamTag*/ false) as JSDocPropertyTag; if (propertyTag) { if (!parentTag.jsDocPropertyTags) { - parentTag.jsDocPropertyTags = >[]; + parentTag.jsDocPropertyTags = >[]; } - parentTag.jsDocPropertyTags.push(propertyTag); + (parentTag.jsDocPropertyTags as MutableNodeArray).push(propertyTag); return true; } // Error parsing property tag diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index eca28ee814b..1f9b179dabb 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -492,7 +492,7 @@ namespace ts { /** Given value: o, propName: p, pattern: { a, b, ...p } from the original statement * `{ a, b, ...p } = o`, create `p = __rest(o, ["a", "b"]);` */ - function createRestCall(context: TransformationContext, value: Expression, elements: BindingOrAssignmentElement[], computedTempVariables: Expression[], location: TextRange): Expression { + function createRestCall(context: TransformationContext, value: Expression, elements: ReadonlyArray, computedTempVariables: ReadonlyArray, location: TextRange): Expression { context.requestEmitHelper(restHelper); const propertyNames: Expression[] = []; let computedTempVariableOffset = 0; diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 19f9d00c656..823b133a4cc 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -1963,7 +1963,7 @@ namespace ts { updated, setTextRange( createNodeArray( - prependCaptureNewTargetIfNeeded(updated.statements, node, /*copyOnWrite*/ true) + prependCaptureNewTargetIfNeeded(updated.statements as MutableNodeArray, node, /*copyOnWrite*/ true) ), /*location*/ updated.statements ) @@ -3199,7 +3199,7 @@ namespace ts { function addStatementToStartOfBlock(block: Block, statement: Statement): Block { const transformedStatements = visitNodes(block.statements, visitor, isStatement); - return updateBlock(block, [statement].concat(transformedStatements)); + return updateBlock(block, [statement, ...transformedStatements]); } /** diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 0b332c237f7..732a613f1b4 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -156,7 +156,7 @@ namespace ts { return visitEachChild(node, visitor, context); } - function chunkObjectLiteralElements(elements: ObjectLiteralElement[]): Expression[] { + function chunkObjectLiteralElements(elements: ReadonlyArray): Expression[] { let chunkObject: (ShorthandPropertyAssignment | PropertyAssignment)[]; const objects: Expression[] = []; for (const e of elements) { diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index a12a34c9537..b120f5c8227 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -1176,7 +1176,7 @@ namespace ts { return visitEachChild(node, visitor, context); } - function transformAndEmitStatements(statements: Statement[], start = 0) { + function transformAndEmitStatements(statements: ReadonlyArray, start = 0) { const numStatements = statements.length; for (let i = start; i < numStatements; i++) { transformAndEmitStatement(statements[i]); diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index ca22cb701d0..efdf38009e0 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -77,7 +77,7 @@ namespace ts { return visitJsxOpeningLikeElement(node, /*children*/ undefined, isChild, /*location*/ node); } - function visitJsxOpeningLikeElement(node: JsxOpeningLikeElement, children: JsxChild[], isChild: boolean, location: TextRange) { + function visitJsxOpeningLikeElement(node: JsxOpeningLikeElement, children: ReadonlyArray, isChild: boolean, location: TextRange) { const tagName = getTagName(node); let objectProperties: Expression; const attrs = node.attributes.properties; diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 5fd1b7b62da..4c20807fa86 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -522,7 +522,7 @@ namespace ts { return parameter.decorators !== undefined && parameter.decorators.length > 0; } - function getClassFacts(node: ClassDeclaration, staticProperties: PropertyDeclaration[]) { + function getClassFacts(node: ClassDeclaration, staticProperties: ReadonlyArray) { let facts = ClassFacts.None; if (some(staticProperties)) facts |= ClassFacts.HasStaticInitializedProperties; if (getClassExtendsHeritageClauseElement(node)) facts |= ClassFacts.HasExtendsClause; @@ -1051,7 +1051,7 @@ namespace ts { * * @param node The constructor node. */ - function getParametersWithPropertyAssignments(node: ConstructorDeclaration): ParameterDeclaration[] { + function getParametersWithPropertyAssignments(node: ConstructorDeclaration): ReadonlyArray { return filter(node.parameters, isParameterWithPropertyAssignment); } @@ -1104,7 +1104,7 @@ namespace ts { * @param node The class node. * @param isStatic A value indicating whether to get properties from the static or instance side of the class. */ - function getInitializedProperties(node: ClassExpression | ClassDeclaration, isStatic: boolean): PropertyDeclaration[] { + function getInitializedProperties(node: ClassExpression | ClassDeclaration, isStatic: boolean): ReadonlyArray { return filter(node.members, isStatic ? isStaticInitializedProperty : isInstanceInitializedProperty); } @@ -1144,7 +1144,7 @@ namespace ts { * @param properties An array of property declarations to transform. * @param receiver The receiver on which each property should be assigned. */ - function addInitializedPropertyStatements(statements: Statement[], properties: PropertyDeclaration[], receiver: LeftHandSideExpression) { + function addInitializedPropertyStatements(statements: Statement[], properties: ReadonlyArray, receiver: LeftHandSideExpression) { for (const property of properties) { const statement = createStatement(transformInitializedProperty(property, receiver)); setSourceMapRange(statement, moveRangePastModifiers(property)); @@ -1159,7 +1159,7 @@ namespace ts { * @param properties An array of property declarations to transform. * @param receiver The receiver on which each property should be assigned. */ - function generateInitializedPropertyExpressions(properties: PropertyDeclaration[], receiver: LeftHandSideExpression) { + function generateInitializedPropertyExpressions(properties: ReadonlyArray, receiver: LeftHandSideExpression) { const expressions: Expression[] = []; for (const property of properties) { const expression = transformInitializedProperty(property, receiver); @@ -1194,7 +1194,7 @@ namespace ts { * @param isStatic A value indicating whether to retrieve static or instance members of * the class. */ - function getDecoratedClassElements(node: ClassExpression | ClassDeclaration, isStatic: boolean): ClassElement[] { + function getDecoratedClassElements(node: ClassExpression | ClassDeclaration, isStatic: boolean): ReadonlyArray { return filter(node.members, isStatic ? isStaticDecoratedClassElement : isInstanceDecoratedClassElement); } @@ -1233,8 +1233,8 @@ namespace ts { * A structure describing the decorators for a class element. */ interface AllDecorators { - decorators: Decorator[]; - parameters?: Decorator[][]; + decorators: ReadonlyArray; + parameters?: ReadonlyArray>; } /** @@ -1244,7 +1244,7 @@ namespace ts { * @param node The function-like node. */ function getDecoratorsOfParameters(node: FunctionLikeDeclaration) { - let decorators: Decorator[][]; + let decorators: ReadonlyArray[]; if (node) { const parameters = node.parameters; for (let i = 0; i < parameters.length; i++) { @@ -1377,7 +1377,7 @@ namespace ts { const decoratorExpressions: Expression[] = []; addRange(decoratorExpressions, map(allDecorators.decorators, transformDecorator)); - addRange(decoratorExpressions, flatMap(allDecorators.parameters, transformDecoratorsOfParameter)); + addRange(decoratorExpressions, flatMap(allDecorators.parameters, transformDecoratorsOfParameter)); addTypeMetadata(node, container, decoratorExpressions); return decoratorExpressions; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9aa6c07816e..f1f79e57d94 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -523,7 +523,10 @@ namespace ts { /* @internal */ contextualMapper?: TypeMapper; // Mapper for contextual type } - export interface NodeArray extends Array, TextRange { + /* @internal */ + export type MutableNodeArray = NodeArray & T[]; + + export interface NodeArray extends ReadonlyArray, TextRange { hasTrailingComma?: boolean; /* @internal */ transformFlags?: TransformFlags; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a84e8eb8538..e001ec43f62 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2731,7 +2731,7 @@ namespace ts { * Gets the effective type parameters. If the node was parsed in a * JavaScript file, gets the type parameters from the `@template` tag from JSDoc. */ - export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): TypeParameterDeclaration[] { + export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): ReadonlyArray { if (node.typeParameters) { return node.typeParameters; } @@ -4739,7 +4739,7 @@ namespace ts { // Node Arrays /* @internal */ - export function isNodeArray(array: T[]): array is NodeArray { + export function isNodeArray(array: ReadonlyArray): array is NodeArray { return array.hasOwnProperty("pos") && array.hasOwnProperty("end"); } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 4dbfbf72e9f..1ce42199372 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -86,7 +86,7 @@ namespace ts { return nodes; } - let updated: NodeArray; + let updated: MutableNodeArray; // Ensure start and count have valid values const length = nodes.length; @@ -901,7 +901,7 @@ namespace ts { * * @param nodes The NodeArray. */ - function extractSingleNode(nodes: Node[]): Node { + function extractSingleNode(nodes: ReadonlyArray): Node { Debug.assert(nodes.length <= 1, "Too many nodes written to output."); return singleOrUndefined(nodes); } @@ -1421,13 +1421,13 @@ namespace ts { /** * Merges generated lexical declarations into a new statement list. */ - export function mergeLexicalEnvironment(statements: NodeArray, declarations: Statement[]): NodeArray; + export function mergeLexicalEnvironment(statements: NodeArray, declarations: ReadonlyArray): NodeArray; /** * Appends generated lexical declarations to an array of statements. */ - export function mergeLexicalEnvironment(statements: Statement[], declarations: Statement[]): Statement[]; - export function mergeLexicalEnvironment(statements: Statement[], declarations: Statement[]) { + export function mergeLexicalEnvironment(statements: Statement[], declarations: ReadonlyArray): Statement[]; + export function mergeLexicalEnvironment(statements: Statement[] | NodeArray, declarations: ReadonlyArray) { if (!some(declarations)) { return statements; } @@ -1442,7 +1442,7 @@ namespace ts { * * @param nodes The NodeArray. */ - export function liftToBlock(nodes: Node[]): Statement { + export function liftToBlock(nodes: ReadonlyArray): Statement { Debug.assert(every(nodes, isStatement), "Cannot lift nodes to a Block."); return singleOrUndefined(nodes) || createBlock(>nodes); } diff --git a/src/harness/unittests/textChanges.ts b/src/harness/unittests/textChanges.ts index 8ac74668023..3c9ebff68f9 100644 --- a/src/harness/unittests/textChanges.ts +++ b/src/harness/unittests/textChanges.ts @@ -67,12 +67,12 @@ namespace ts { } function flattenNodes(n: Node) { - const data: (Node | NodeArray)[] = []; + const data: (Node | NodeArray)[] = []; walk(n); return data; - function walk(n: Node | Node[]): void { - data.push(n); + function walk(n: Node | NodeArray): void { + data.push(n); return isArray(n) ? forEach(n, walk) : forEachChild(n, walk, walk); } } diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index b45d2448a0a..d1599dc4cac 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -186,7 +186,7 @@ namespace ts.codefix { return parameters; } - function createMethodImplementingSignatures(signatures: Signature[], name: PropertyName, optional: boolean, modifiers: Modifier[] | undefined): MethodDeclaration { + function createMethodImplementingSignatures(signatures: ReadonlyArray, name: PropertyName, optional: boolean, modifiers: ReadonlyArray | 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. @@ -231,7 +231,13 @@ namespace ts.codefix { /*returnType*/ undefined); } - export function createStubbedMethod(modifiers: Modifier[], name: PropertyName, optional: boolean, typeParameters: TypeParameterDeclaration[] | undefined, parameters: ParameterDeclaration[], returnType: TypeNode | undefined) { + export function createStubbedMethod( + modifiers: ReadonlyArray, + name: PropertyName, + optional: boolean, + typeParameters: ReadonlyArray | undefined, + parameters: ReadonlyArray, + returnType: TypeNode | undefined) { return createMethod( /*decorators*/ undefined, modifiers, diff --git a/src/services/completions.ts b/src/services/completions.ts index 12796a9b791..d7f0701caf2 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -963,7 +963,7 @@ namespace ts.Completions { isMemberCompletion = true; let typeMembers: Symbol[]; - let existingMembers: Declaration[]; + let existingMembers: ReadonlyArray; if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) { // We are completing on contextual types, but may also include properties @@ -1093,14 +1093,14 @@ namespace ts.Completions { } } const implementedInterfaceTypePropertySymbols = (classElementModifierFlags & ModifierFlags.Static) ? - undefined : - flatMap(implementsTypeNodes, typeNode => typeChecker.getPropertiesOfType(typeChecker.getTypeAtLocation(typeNode))); + emptyArray : + flatMap(implementsTypeNodes || emptyArray, typeNode => typeChecker.getPropertiesOfType(typeChecker.getTypeAtLocation(typeNode))); // List of property symbols of base type that are not private and already implemented symbols = filterClassMembersList( baseClassTypeToGetPropertiesFrom ? typeChecker.getPropertiesOfType(baseClassTypeToGetPropertiesFrom) : - undefined, + emptyArray, implementedInterfaceTypePropertySymbols, classLikeDeclaration.members, classElementModifierFlags); @@ -1443,7 +1443,7 @@ namespace ts.Completions { * @returns Symbols to be suggested at an import/export clause, barring those whose named imports/exports * do not occur at the current position and have not otherwise been typed. */ - function filterNamedImportOrExportCompletionItems(exportsOfModule: Symbol[], namedImportsOrExports: ImportOrExportSpecifier[]): Symbol[] { + function filterNamedImportOrExportCompletionItems(exportsOfModule: Symbol[], namedImportsOrExports: ReadonlyArray): Symbol[] { const existingImportsOrExports = createUnderscoreEscapedMap(); for (const element of namedImportsOrExports) { @@ -1469,7 +1469,7 @@ namespace ts.Completions { * @returns Symbols to be suggested in an object binding pattern or object literal expression, barring those whose declarations * do not occur at the current position and have not otherwise been typed. */ - function filterObjectMembersList(contextualMemberSymbols: Symbol[], existingMembers: Declaration[]): Symbol[] { + function filterObjectMembersList(contextualMemberSymbols: Symbol[], existingMembers: ReadonlyArray): Symbol[] { if (!existingMembers || existingMembers.length === 0) { return contextualMemberSymbols; } @@ -1518,7 +1518,11 @@ namespace ts.Completions { * * @returns Symbols to be suggested in an class element depending on existing memebers and symbol flags */ - function filterClassMembersList(baseSymbols: Symbol[], implementingTypeSymbols: Symbol[], existingMembers: ClassElement[], currentClassElementModifierFlags: ModifierFlags): Symbol[] { + function filterClassMembersList( + baseSymbols: ReadonlyArray, + implementingTypeSymbols: ReadonlyArray, + existingMembers: ReadonlyArray, + currentClassElementModifierFlags: ModifierFlags): Symbol[] { const existingMemberNames = createUnderscoreEscapedMap(); for (const m of existingMembers) { // Ignore omitted expressions for missing members @@ -1553,10 +1557,18 @@ namespace ts.Completions { } } - return concatenate( - filter(baseSymbols, baseProperty => isValidProperty(baseProperty, ModifierFlags.Private)), - filter(implementingTypeSymbols, implementingProperty => isValidProperty(implementingProperty, ModifierFlags.NonPublicAccessibilityModifier)) - ); + const result: Symbol[] = []; + addPropertySymbols(baseSymbols, ModifierFlags.Private); + addPropertySymbols(implementingTypeSymbols, ModifierFlags.NonPublicAccessibilityModifier); + return result; + + function addPropertySymbols(properties: ReadonlyArray, inValidModifierFlags: ModifierFlags) { + for (const property of properties) { + if (isValidProperty(property, inValidModifierFlags)) { + result.push(property); + } + } + } function isValidProperty(propertySymbol: Symbol, inValidModifierFlags: ModifierFlags) { return !existingMemberNames.get(propertySymbol.name) && diff --git a/src/services/documentHighlights.ts b/src/services/documentHighlights.ts index 0a2c8e726fc..eb1e2531143 100644 --- a/src/services/documentHighlights.ts +++ b/src/services/documentHighlights.ts @@ -298,21 +298,20 @@ namespace ts.DocumentHighlights { const keywords: Node[] = []; const modifierFlag: ModifierFlags = getFlagFromModifier(modifier); - let nodes: Node[]; + let nodes: ReadonlyArray; switch (container.kind) { case SyntaxKind.ModuleBlock: case SyntaxKind.SourceFile: // Container is either a class declaration or the declaration is a classDeclaration if (modifierFlag & ModifierFlags.Abstract) { - nodes = ((declaration).members).concat(declaration); + nodes = [...(declaration).members, declaration]; } else { nodes = (container).statements; } break; case SyntaxKind.Constructor: - nodes = ((container).parameters).concat( - (container.parent).members); + nodes = [...(container).parameters, ...(container.parent).members]; break; case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: @@ -326,11 +325,11 @@ namespace ts.DocumentHighlights { }); if (constructor) { - nodes = nodes.concat(constructor.parameters); + nodes = [...nodes, ...constructor.parameters]; } } else if (modifierFlag & ModifierFlags.Abstract) { - nodes = nodes.concat(container); + nodes = [...nodes, container]; } break; default: diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index b5ba809ec7d..ad9b2f179aa 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -1152,7 +1152,7 @@ namespace ts.formatting { } } - function getOpenTokenForList(node: Node, list: Node[]) { + function getOpenTokenForList(node: Node, list: ReadonlyArray) { switch (node.kind) { case SyntaxKind.Constructor: case SyntaxKind.FunctionDeclaration: diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index 18b0479c85a..a98986472f3 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -328,7 +328,7 @@ namespace ts.formatting { const containingList = getContainingList(node, sourceFile); return containingList ? getActualIndentationFromList(containingList) : Value.Unknown; - function getActualIndentationFromList(list: Node[]): number { + function getActualIndentationFromList(list: ReadonlyArray): number { const index = indexOf(list, node); return index !== -1 ? deriveActualIndentationFromList(list, index, sourceFile, options) : Value.Unknown; } @@ -378,7 +378,7 @@ namespace ts.formatting { } } - function deriveActualIndentationFromList(list: Node[], index: number, sourceFile: SourceFile, options: EditorSettings): number { + function deriveActualIndentationFromList(list: ReadonlyArray, index: number, sourceFile: SourceFile, options: EditorSettings): number { Debug.assert(index >= 0 && index < list.length); const node = list[index]; diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 0bed84a393b..b388a8551d2 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -191,7 +191,7 @@ namespace ts.GoToDefinition { return false; } - function tryAddSignature(signatureDeclarations: Declaration[] | undefined, selectConstructors: boolean, symbolKind: ScriptElementKind, symbolName: string, containerName: string, result: DefinitionInfo[]) { + function tryAddSignature(signatureDeclarations: ReadonlyArray | undefined, selectConstructors: boolean, symbolKind: ScriptElementKind, symbolName: string, containerName: string, result: DefinitionInfo[]) { if (!signatureDeclarations) { return false; } diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts index c5e437f20fc..070b96101e2 100644 --- a/src/services/jsDoc.ts +++ b/src/services/jsDoc.ts @@ -241,7 +241,7 @@ namespace ts.JsDoc { return { newText: result, caretOffset: preamble.length }; } - function getParametersForJsDocOwningNode(commentOwner: Node): ParameterDeclaration[] { + function getParametersForJsDocOwningNode(commentOwner: Node): ReadonlyArray { if (isFunctionLike(commentOwner)) { return commentOwner.parameters; } @@ -266,7 +266,7 @@ namespace ts.JsDoc { * @param rightHandSide the expression which may contain an appropriate set of parameters * @returns the parameters of a signature found on the RHS if one exists; otherwise 'emptyArray'. */ - function getParametersFromRightHandSideOfAssignment(rightHandSide: Expression): ParameterDeclaration[] { + function getParametersFromRightHandSideOfAssignment(rightHandSide: Expression): ReadonlyArray { while (rightHandSide.kind === SyntaxKind.ParenthesizedExpression) { rightHandSide = (rightHandSide).expression; } From 08a57d82cd4395ec3528f4adcd73c7d5258f7504 Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 18 Jul 2017 11:08:44 -0700 Subject: [PATCH 22/34] Add 'clear' helper (#17209) --- src/compiler/checker.ts | 8 ++++---- src/compiler/core.ts | 4 ++++ src/harness/unittests/tsserverProjectSystem.ts | 2 +- src/server/builder.ts | 2 +- src/server/scriptInfo.ts | 2 +- src/services/shims.ts | 2 +- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5d01000f23b..83f2fa179e4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22266,8 +22266,8 @@ namespace ts { // Grammar checking checkGrammarSourceFile(node); - potentialThisCollisions.length = 0; - potentialNewTargetCollisions.length = 0; + clear(potentialThisCollisions); + clear(potentialNewTargetCollisions); deferredNodes = []; deferredUnusedIdentifierNodes = produceDiagnostics && noUnusedIdentifiers ? [] : undefined; @@ -22293,12 +22293,12 @@ namespace ts { if (potentialThisCollisions.length) { forEach(potentialThisCollisions, checkIfThisIsCapturedInEnclosingScope); - potentialThisCollisions.length = 0; + clear(potentialThisCollisions); } if (potentialNewTargetCollisions.length) { forEach(potentialNewTargetCollisions, checkIfNewTargetIsCapturedInEnclosingScope); - potentialNewTargetCollisions.length = 0; + clear(potentialNewTargetCollisions); } links.flags |= NodeCheckFlags.TypeChecked; diff --git a/src/compiler/core.ts b/src/compiler/core.ts index fa54abd4038..cecf88f42ce 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -384,6 +384,10 @@ namespace ts { array.length = outIndex; } + export function clear(array: {}[]): void { + array.length = 0; + } + export function map(array: ReadonlyArray, f: (x: T, i: number) => U): U[] { let result: U[]; if (array) { diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 3dde03a092b..f11900aea11 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -561,7 +561,7 @@ namespace ts.projectSystem { } clearOutput() { - this.output.length = 0; + clear(this.output); } readonly readFile = (s: string) => (this.fs.get(this.toPath(s))).content; diff --git a/src/server/builder.ts b/src/server/builder.ts index bc86780fbbe..8a10682b4cc 100644 --- a/src/server/builder.ts +++ b/src/server/builder.ts @@ -223,7 +223,7 @@ namespace ts.server { for (const reference of this.references) { reference.removeReferencedBy(this); } - this.references = createSortedArray(); + clear(this.references); } } diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index 52107359a32..b611bcb70e0 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -242,7 +242,7 @@ namespace ts.server { // detach is unnecessary since we'll clean the list of containing projects anyways p.removeFile(this, /*detachFromProjects*/ false); } - this.containingProjects.length = 0; + clear(this.containingProjects); } getDefaultProject() { diff --git a/src/services/shims.ts b/src/services/shims.ts index 03965cb5d48..e86e9053e57 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -1176,7 +1176,7 @@ namespace ts { public close(): void { // Forget all the registered shims - this._shims = []; + clear(this._shims); this.documentRegistry = undefined; } From 8075353356d2713ac76a077bf1d8396edaea8cc8 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 18 Jul 2017 15:08:53 -0700 Subject: [PATCH 23/34] Appropriately parenthesize keyof and typeof queries in array types (#17272) * Appropriately parenthesize keyof and typeof queries when they are array types * Fix test and then the same bug in the symbol writer --- src/compiler/checker.ts | 8 ++++++- src/compiler/factory.ts | 11 +++++++++- src/compiler/types.ts | 1 + .../reference/aliasUsageInArray.types | 6 +++--- .../reference/arrayOfFunctionTypes3.types | 6 +++--- .../declarationEmitIndexTypeArray.types | 10 ++++----- .../keyofIsLiteralContexualType.errors.txt | 4 ++-- .../fourslash/typeOperatorNodeBuilding.ts | 21 +++++++++++++++++++ 8 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 tests/cases/fourslash/typeOperatorNodeBuilding.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 83f2fa179e4..70827becffe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3302,7 +3302,7 @@ namespace ts { function writeTypeReference(type: TypeReference, flags: TypeFormatFlags) { const typeArguments = type.typeArguments || emptyArray; if (type.target === globalArrayType && !(flags & TypeFormatFlags.WriteArrayAsGenericType)) { - writeType(typeArguments[0], TypeFormatFlags.InElementType); + writeType(typeArguments[0], TypeFormatFlags.InElementType | TypeFormatFlags.InArrayType); writePunctuation(writer, SyntaxKind.OpenBracketToken); writePunctuation(writer, SyntaxKind.CloseBracketToken); } @@ -3427,9 +3427,15 @@ namespace ts { } function writeTypeOfSymbol(type: ObjectType, typeFormatFlags?: TypeFormatFlags) { + if (typeFormatFlags & TypeFormatFlags.InArrayType) { + writePunctuation(writer, SyntaxKind.OpenParenToken); + } writeKeyword(writer, SyntaxKind.TypeOfKeyword); writeSpace(writer); buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Value, SymbolFormatFlags.None, typeFormatFlags); + if (typeFormatFlags & TypeFormatFlags.InArrayType) { + writePunctuation(writer, SyntaxKind.CloseParenToken); + } } function writePropertyWithModifiers(prop: Symbol) { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index e58f30a215f..d6d21254218 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -664,7 +664,7 @@ namespace ts { export function createArrayTypeNode(elementType: TypeNode) { const node = createSynthesizedNode(SyntaxKind.ArrayType) as ArrayTypeNode; - node.elementType = parenthesizeElementTypeMember(elementType); + node.elementType = parenthesizeArrayTypeMember(elementType); return node; } @@ -3898,6 +3898,15 @@ namespace ts { return member; } + export function parenthesizeArrayTypeMember(member: TypeNode) { + switch (member.kind) { + case SyntaxKind.TypeQuery: + case SyntaxKind.TypeOperator: + return createParenthesizedType(member); + } + return parenthesizeElementTypeMember(member); + } + export function parenthesizeElementTypeMembers(members: ReadonlyArray) { return createNodeArray(sameMap(members, parenthesizeElementTypeMember)); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f1f79e57d94..e16ee34b01a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2685,6 +2685,7 @@ namespace ts { SuppressAnyReturnType = 1 << 12, // If the return type is any-like, don't offer a return type. AddUndefined = 1 << 13, // Add undefined to types of initialized, non-optional parameters WriteClassExpressionAsTypeLiteral = 1 << 14, // Write a type literal instead of (Anonymous class) + InArrayType = 1 << 15, // Writing an array element type } export const enum SymbolFormatFlags { diff --git a/tests/baselines/reference/aliasUsageInArray.types b/tests/baselines/reference/aliasUsageInArray.types index 218df8e10ed..ca8f0e61f2f 100644 --- a/tests/baselines/reference/aliasUsageInArray.types +++ b/tests/baselines/reference/aliasUsageInArray.types @@ -18,13 +18,13 @@ interface IHasVisualizationModel { var xs: IHasVisualizationModel[] = [moduleA]; >xs : IHasVisualizationModel[] >IHasVisualizationModel : IHasVisualizationModel ->[moduleA] : typeof moduleA[] +>[moduleA] : (typeof moduleA)[] >moduleA : typeof moduleA var xs2: typeof moduleA[] = [moduleA]; ->xs2 : typeof moduleA[] +>xs2 : (typeof moduleA)[] >moduleA : typeof moduleA ->[moduleA] : typeof moduleA[] +>[moduleA] : (typeof moduleA)[] >moduleA : typeof moduleA === tests/cases/compiler/aliasUsageInArray_backbone.ts === diff --git a/tests/baselines/reference/arrayOfFunctionTypes3.types b/tests/baselines/reference/arrayOfFunctionTypes3.types index 124542f5814..c7251ea45d2 100644 --- a/tests/baselines/reference/arrayOfFunctionTypes3.types +++ b/tests/baselines/reference/arrayOfFunctionTypes3.types @@ -22,8 +22,8 @@ class C { >foo : string } var y = [C, C]; ->y : typeof C[] ->[C, C] : typeof C[] +>y : (typeof C)[] +>[C, C] : (typeof C)[] >C : typeof C >C : typeof C @@ -31,7 +31,7 @@ var r3 = new y[0](); >r3 : C >new y[0]() : C >y[0] : typeof C ->y : typeof C[] +>y : (typeof C)[] >0 : 0 var a: { (x: number): number; (x: string): string; }; diff --git a/tests/baselines/reference/declarationEmitIndexTypeArray.types b/tests/baselines/reference/declarationEmitIndexTypeArray.types index 67f8905639e..549c1efe5f3 100644 --- a/tests/baselines/reference/declarationEmitIndexTypeArray.types +++ b/tests/baselines/reference/declarationEmitIndexTypeArray.types @@ -1,16 +1,16 @@ === tests/cases/compiler/declarationEmitIndexTypeArray.ts === function doSomethingWithKeys(...keys: (keyof T)[]) { } ->doSomethingWithKeys : (...keys: keyof T[]) => void +>doSomethingWithKeys : (...keys: (keyof T)[]) => void >T : T ->keys : keyof T[] +>keys : (keyof T)[] >T : T const utilityFunctions = { ->utilityFunctions : { doSomethingWithKeys: (...keys: keyof T[]) => void; } ->{ doSomethingWithKeys} : { doSomethingWithKeys: (...keys: keyof T[]) => void; } +>utilityFunctions : { doSomethingWithKeys: (...keys: (keyof T)[]) => void; } +>{ doSomethingWithKeys} : { doSomethingWithKeys: (...keys: (keyof T)[]) => void; } doSomethingWithKeys ->doSomethingWithKeys : (...keys: keyof T[]) => void +>doSomethingWithKeys : (...keys: (keyof T)[]) => void }; diff --git a/tests/baselines/reference/keyofIsLiteralContexualType.errors.txt b/tests/baselines/reference/keyofIsLiteralContexualType.errors.txt index c19a13209c9..b578d002e05 100644 --- a/tests/baselines/reference/keyofIsLiteralContexualType.errors.txt +++ b/tests/baselines/reference/keyofIsLiteralContexualType.errors.txt @@ -1,4 +1,4 @@ -tests/cases/compiler/keyofIsLiteralContexualType.ts(5,9): error TS2322: Type '("a" | "b" | "c")[]' is not assignable to type 'keyof T[]'. +tests/cases/compiler/keyofIsLiteralContexualType.ts(5,9): error TS2322: Type '("a" | "b" | "c")[]' is not assignable to type '(keyof T)[]'. Type '"a" | "b" | "c"' is not assignable to type 'keyof T'. Type '"c"' is not assignable to type 'keyof T'. Type '"c"' is not assignable to type '"a" | "b"'. @@ -12,7 +12,7 @@ tests/cases/compiler/keyofIsLiteralContexualType.ts(13,11): error TS2339: Proper let a: (keyof T)[] = ["a", "b"]; let b: (keyof T)[] = ["a", "b", "c"]; ~ -!!! error TS2322: Type '("a" | "b" | "c")[]' is not assignable to type 'keyof T[]'. +!!! error TS2322: Type '("a" | "b" | "c")[]' is not assignable to type '(keyof T)[]'. !!! error TS2322: Type '"a" | "b" | "c"' is not assignable to type 'keyof T'. !!! error TS2322: Type '"c"' is not assignable to type 'keyof T'. !!! error TS2322: Type '"c"' is not assignable to type '"a" | "b"'. diff --git a/tests/cases/fourslash/typeOperatorNodeBuilding.ts b/tests/cases/fourslash/typeOperatorNodeBuilding.ts new file mode 100644 index 00000000000..dea274c03b0 --- /dev/null +++ b/tests/cases/fourslash/typeOperatorNodeBuilding.ts @@ -0,0 +1,21 @@ +/// + +// @Filename: keyof.ts +//// function doSomethingWithKeys(...keys: (keyof T)[]) { } +//// +//// const /*1*/utilityFunctions = { +//// doSomethingWithKeys +//// }; + +// @Filename: typeof.ts +//// class Foo { static a: number; } +//// function doSomethingWithTypes(...statics: (typeof Foo)[]) {} +//// +//// const /*2*/utilityFunctions = { +//// doSomethingWithTypes +//// }; + +verify.quickInfos({ + 1: "const utilityFunctions: {\n doSomethingWithKeys: (...keys: (keyof T)[]) => void;\n}", + 2: "const utilityFunctions: {\n doSomethingWithTypes: (...statics: (typeof Foo)[]) => void;\n}" +}); From ea0bce511b654385872c6254631561f31c41d82b Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 19 Jul 2017 07:52:18 -0700 Subject: [PATCH 24/34] MultiStepOperation: No need to create a 'NextStep' object, just use self as the NextStep (#17174) --- src/server/session.ts | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/server/session.ts b/src/server/session.ts index bd69640a4bb..074ba4d6ca1 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -162,19 +162,13 @@ namespace ts.server { * Represents operation that can schedule its next step to be executed later. * Scheduling is done via instance of NextStep. If on current step subsequent step was not scheduled - operation is assumed to be completed. */ - class MultistepOperation { + class MultistepOperation implements NextStep { private requestId: number; private timerHandle: any; private immediateId: any; private completed = true; - private readonly next: NextStep; - constructor(private readonly operationHost: MultistepOperationHost) { - this.next = { - immediate: action => this.immediate(action), - delay: (ms, action) => this.delay(ms, action) - }; - } + constructor(private readonly operationHost: MultistepOperationHost) {} public startNew(action: (next: NextStep) => void) { this.complete(); @@ -194,7 +188,7 @@ namespace ts.server { this.setImmediateId(undefined); } - private immediate(action: () => void) { + public immediate(action: () => void) { const requestId = this.requestId; Debug.assert(requestId === this.operationHost.getCurrentRequestId(), "immediate: incorrect request id"); this.setImmediateId(this.operationHost.getServerHost().setImmediate(() => { @@ -203,7 +197,7 @@ namespace ts.server { })); } - private delay(ms: number, action: () => void) { + public delay(ms: number, action: () => void) { const requestId = this.requestId; Debug.assert(requestId === this.operationHost.getCurrentRequestId(), "delay: incorrect request id"); this.setTimerHandle(this.operationHost.getServerHost().setTimeout(() => { @@ -219,7 +213,7 @@ namespace ts.server { stop = true; } else { - action(this.next); + action(this); } } catch (e) { From cfa94c2d349e9f1dd634a308850705214d81511e Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 19 Jul 2017 09:11:50 -0700 Subject: [PATCH 25/34] Binder: handle JS property assignment that comes after a method declaration with the same name (#16830) --- src/compiler/binder.ts | 24 +++++++------- ...yAssignedAfterMethodDeclaration.errors.txt | 13 ++++++++ ...PropertyAssignedAfterMethodDeclaration.txt | 16 ++++++++++ ...nedAfterMethodDeclaration_nonError.symbols | 15 +++++++++ ...ignedAfterMethodDeclaration_nonError.types | 18 +++++++++++ .../reference/multipleDeclarations.symbols | 32 ++++++++++--------- .../reference/multipleDeclarations.types | 20 ++++++------ ...sPropertyAssignedAfterMethodDeclaration.ts | 12 +++++++ ...AssignedAfterMethodDeclaration_nonError.ts | 12 +++++++ ...sPropertyAssignedAfterMethodDeclaration.ts | 17 ++++++++++ 10 files changed, 143 insertions(+), 36 deletions(-) create mode 100644 tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration.errors.txt create mode 100644 tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration.txt create mode 100644 tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration_nonError.symbols create mode 100644 tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration_nonError.types create mode 100644 tests/cases/compiler/jsPropertyAssignedAfterMethodDeclaration.ts create mode 100644 tests/cases/compiler/jsPropertyAssignedAfterMethodDeclaration_nonError.ts create mode 100644 tests/cases/fourslash/quickInfoJsPropertyAssignedAfterMethodDeclaration.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b07812d75ed..0b58f080fe2 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -308,7 +308,7 @@ namespace ts { * @param includes - The SymbolFlags that node has in addition to its declaration type (eg: export, ambient, etc.) * @param excludes - The flags which node cannot be declared alongside in a symbol table. Used to report forbidden declarations. */ - function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol { + function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags, isReplaceableByMethod?: boolean): Symbol { Debug.assert(!hasDynamicName(node)); const isDefaultExport = hasModifier(node, ModifierFlags.Default); @@ -345,15 +345,20 @@ namespace ts { // you have multiple 'vars' with the same name in the same container). In this case // just add this node into the declarations list of the symbol. symbol = symbolTable.get(name); - if (!symbol) { - symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name)); - } - if (name && (includes & SymbolFlags.Classifiable)) { + if (includes & SymbolFlags.Classifiable) { classifiableNames.set(name, true); } - if (symbol.flags & excludes) { + if (!symbol) { + symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name)); + if (isReplaceableByMethod) symbol.isReplaceableByMethod = true; + } + else if (isReplaceableByMethod && !symbol.isReplaceableByMethod) { + // A symbol already exists, so don't add this as a declaration. + return symbol; + } + else if (symbol.flags & excludes) { if (symbol.isReplaceableByMethod) { // Javascript constructor-declared symbols can be discarded in favor of // prototype symbols like methods. @@ -2344,11 +2349,8 @@ namespace ts { // this.foo assignment in a JavaScript class // Bind this property to the containing class const containingClass = container.parent; - const symbol = declareSymbol(hasModifier(container, ModifierFlags.Static) ? containingClass.symbol.exports : containingClass.symbol.members, containingClass.symbol, node, SymbolFlags.Property, SymbolFlags.None); - if (symbol) { - // symbols declared through 'this' property assignements can be overwritten by subsequent method declarations - (symbol as Symbol).isReplaceableByMethod = true; - } + const symbolTable = hasModifier(container, ModifierFlags.Static) ? containingClass.symbol.exports : containingClass.symbol.members; + declareSymbol(symbolTable, containingClass.symbol, node, SymbolFlags.Property, SymbolFlags.None, /*isReplaceableByMethod*/ true); break; } } diff --git a/tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration.errors.txt b/tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration.errors.txt new file mode 100644 index 00000000000..a2e0dd12d31 --- /dev/null +++ b/tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration.errors.txt @@ -0,0 +1,13 @@ +/a.js(4,9): error TS2322: Type '0' is not assignable to type '() => void'. + + +==== /a.js (1 errors) ==== + const o = { + a() { + // Should not be treated as a declaration. Should be an error. + this.a = 0; + ~~~~~~ +!!! error TS2322: Type '0' is not assignable to type '() => void'. + } + }; + \ No newline at end of file diff --git a/tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration.txt b/tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration.txt new file mode 100644 index 00000000000..a774e45ff66 --- /dev/null +++ b/tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration.txt @@ -0,0 +1,16 @@ +/a.js(4,9): error TS2322: Type '0' is not assignable to type '() => void'. + + +==== /a.js (1 errors) ==== + const o = { + a() { + // Should not be treated as a declaration. Should be an error. + this.a = 0; + ~~~~~~ +!!! error TS2322: Type '0' is not assignable to type '() => void'. + }, + b() { + this.b = () => {}; // OK + } + }; + \ No newline at end of file diff --git a/tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration_nonError.symbols b/tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration_nonError.symbols new file mode 100644 index 00000000000..af84dd7d5de --- /dev/null +++ b/tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration_nonError.symbols @@ -0,0 +1,15 @@ +=== /a.js === +const o = { +>o : Symbol(o, Decl(a.js, 0, 5)) + + a() { +>a : Symbol(a, Decl(a.js, 0, 11)) + + // Should not be treated as a declaration. + this.a = () => {}; +>this.a : Symbol(a, Decl(a.js, 0, 11)) +>this : Symbol(o, Decl(a.js, 0, 9)) +>a : Symbol(a, Decl(a.js, 0, 11)) + } +}; + diff --git a/tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration_nonError.types b/tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration_nonError.types new file mode 100644 index 00000000000..8fed2b36797 --- /dev/null +++ b/tests/baselines/reference/jsPropertyAssignedAfterMethodDeclaration_nonError.types @@ -0,0 +1,18 @@ +=== /a.js === +const o = { +>o : { [x: string]: any; a(): void; } +>{ a() { // Should not be treated as a declaration. this.a = () => {}; }} : { [x: string]: any; a(): void; } + + a() { +>a : () => void + + // Should not be treated as a declaration. + this.a = () => {}; +>this.a = () => {} : () => void +>this.a : () => void +>this : { [x: string]: any; a(): void; } +>a : () => void +>() => {} : () => void + } +}; + diff --git a/tests/baselines/reference/multipleDeclarations.symbols b/tests/baselines/reference/multipleDeclarations.symbols index 4fc8f8a5f28..3133d77056b 100644 --- a/tests/baselines/reference/multipleDeclarations.symbols +++ b/tests/baselines/reference/multipleDeclarations.symbols @@ -66,45 +66,47 @@ class Y { >Y : Symbol(Y, Decl(input.js, 19, 10)) mistake() { ->mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) +>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 29, 1)) } m() { ->m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) +>m : Symbol(Y.m, Decl(input.js, 22, 5)) } constructor() { this.m = this.m.bind(this); ->this.m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) +>this.m : Symbol(Y.m, Decl(input.js, 22, 5)) >this : Symbol(Y, Decl(input.js, 19, 10)) ->m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) ->this.m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) +>m : Symbol(Y.m, Decl(input.js, 22, 5)) +>this.m.bind : Symbol(Function.bind, Decl(lib.d.ts, --, --)) +>this.m : Symbol(Y.m, Decl(input.js, 22, 5)) >this : Symbol(Y, Decl(input.js, 19, 10)) ->m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) +>m : Symbol(Y.m, Decl(input.js, 22, 5)) +>bind : Symbol(Function.bind, Decl(lib.d.ts, --, --)) >this : Symbol(Y, Decl(input.js, 19, 10)) this.mistake = 'even more nonsense'; ->this.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) +>this.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 29, 1)) >this : Symbol(Y, Decl(input.js, 19, 10)) ->mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) +>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 29, 1)) } } Y.prototype.mistake = true; ->Y.prototype.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) ->Y.prototype : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) +>Y.prototype.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 29, 1)) +>Y.prototype : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 29, 1)) >Y : Symbol(Y, Decl(input.js, 19, 10)) >prototype : Symbol(Y.prototype) ->mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) +>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 29, 1)) let y = new Y(); >y : Symbol(y, Decl(input.js, 31, 3)) >Y : Symbol(Y, Decl(input.js, 19, 10)) y.m(); ->y.m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) +>y.m : Symbol(Y.m, Decl(input.js, 22, 5)) >y : Symbol(y, Decl(input.js, 31, 3)) ->m : Symbol(Y.m, Decl(input.js, 22, 5), Decl(input.js, 25, 19)) +>m : Symbol(Y.m, Decl(input.js, 22, 5)) y.mistake(); ->y.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) +>y.mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 29, 1)) >y : Symbol(y, Decl(input.js, 31, 3)) ->mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 26, 35), Decl(input.js, 29, 1)) +>mistake : Symbol(Y.mistake, Decl(input.js, 20, 9), Decl(input.js, 29, 1)) diff --git a/tests/baselines/reference/multipleDeclarations.types b/tests/baselines/reference/multipleDeclarations.types index b82cdbe05ed..39aa8035b26 100644 --- a/tests/baselines/reference/multipleDeclarations.types +++ b/tests/baselines/reference/multipleDeclarations.types @@ -87,20 +87,20 @@ class Y { >mistake : any } m() { ->m : any +>m : () => void } constructor() { this.m = this.m.bind(this); >this.m = this.m.bind(this) : any ->this.m : any +>this.m : () => void >this : this ->m : any +>m : () => void >this.m.bind(this) : any ->this.m.bind : any ->this.m : any +>this.m.bind : (this: Function, thisArg: any, ...argArray: any[]) => any +>this.m : () => void >this : this ->m : any ->bind : any +>m : () => void +>bind : (this: Function, thisArg: any, ...argArray: any[]) => any >this : this this.mistake = 'even more nonsense'; @@ -126,10 +126,10 @@ let y = new Y(); >Y : typeof Y y.m(); ->y.m() : any ->y.m : any +>y.m() : void +>y.m : () => void >y : Y ->m : any +>m : () => void y.mistake(); >y.mistake() : any diff --git a/tests/cases/compiler/jsPropertyAssignedAfterMethodDeclaration.ts b/tests/cases/compiler/jsPropertyAssignedAfterMethodDeclaration.ts new file mode 100644 index 00000000000..62a92c692b8 --- /dev/null +++ b/tests/cases/compiler/jsPropertyAssignedAfterMethodDeclaration.ts @@ -0,0 +1,12 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @noImplicitThis: true + +// @Filename: /a.js +const o = { + a() { + // Should not be treated as a declaration. Should be an error. + this.a = 0; + } +}; diff --git a/tests/cases/compiler/jsPropertyAssignedAfterMethodDeclaration_nonError.ts b/tests/cases/compiler/jsPropertyAssignedAfterMethodDeclaration_nonError.ts new file mode 100644 index 00000000000..8e3c23db09a --- /dev/null +++ b/tests/cases/compiler/jsPropertyAssignedAfterMethodDeclaration_nonError.ts @@ -0,0 +1,12 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @noImplicitThis: true + +// @Filename: /a.js +const o = { + a() { + // Should not be treated as a declaration. + this.a = () => {}; + } +}; diff --git a/tests/cases/fourslash/quickInfoJsPropertyAssignedAfterMethodDeclaration.ts b/tests/cases/fourslash/quickInfoJsPropertyAssignedAfterMethodDeclaration.ts new file mode 100644 index 00000000000..a1d3ae2d9e6 --- /dev/null +++ b/tests/cases/fourslash/quickInfoJsPropertyAssignedAfterMethodDeclaration.ts @@ -0,0 +1,17 @@ +/// + +// See also `jsPropertyAssignedAfterMethodDeclaration.ts` + +// @noLib: true +// @allowJs: true +// @noImplicitThis: true + +// @Filename: /a.js +////const o = { +//// test/*1*/() { +//// this./*2*/test = 0; +//// } +////}; + +verify.quickInfoAt("1", "(method) test(): void"); +verify.quickInfoAt("2", "(method) test(): void"); From d918b8ad4e341b984456df56979fb18c9e47aec7 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 19 Jul 2017 11:23:14 -0700 Subject: [PATCH 26/34] Remove duplicate helper (#17296) --- src/server/editorServices.ts | 8 ++++---- src/server/scriptInfo.ts | 2 +- src/server/utilities.ts | 18 ------------------ 3 files changed, 5 insertions(+), 23 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 43cb429cfc5..9783af68663 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -703,15 +703,15 @@ namespace ts.server { switch (project.projectKind) { case ProjectKind.External: - removeItemFromSet(this.externalProjects, project); + unorderedRemoveItem(this.externalProjects, project); this.projectToSizeMap.delete((project as ExternalProject).externalProjectName); break; case ProjectKind.Configured: - removeItemFromSet(this.configuredProjects, project); + unorderedRemoveItem(this.configuredProjects, project); this.projectToSizeMap.delete((project as ConfiguredProject).canonicalConfigFilePath); break; case ProjectKind.Inferred: - removeItemFromSet(this.inferredProjects, project); + unorderedRemoveItem(this.inferredProjects, project); break; } } @@ -790,7 +790,7 @@ namespace ts.server { // to the disk, and the server's version of the file can be out of sync. info.close(); - removeItemFromSet(this.openFiles, info); + unorderedRemoveItem(this.openFiles, info); // collect all projects that should be removed let projectsToRemove: Project[]; diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index b611bcb70e0..ce1de6f5040 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -232,7 +232,7 @@ namespace ts.server { } break; default: - removeItemFromSet(this.containingProjects, project); + unorderedRemoveItem(this.containingProjects, project); break; } } diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 30146c0a928..174bcc17dfc 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -103,24 +103,6 @@ namespace ts.server { } } - export function removeItemFromSet(items: T[], itemToRemove: T) { - if (items.length === 0) { - return; - } - const index = items.indexOf(itemToRemove); - if (index < 0) { - return; - } - if (index === items.length - 1) { - // last item - pop it - items.pop(); - } - else { - // non-last item - replace it with the last one - items[index] = items.pop(); - } - } - export type NormalizedPath = string & { __normalizedPathTag: any }; export function toNormalizedPath(fileName: string): NormalizedPath { From d99694614a1178e3665ddb8311203999eed022f0 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 19 Jul 2017 11:23:41 -0700 Subject: [PATCH 27/34] Simplify use of array helpers (#17301) --- src/server/editorServices.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 9783af68663..4a7fe91d450 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -239,10 +239,7 @@ namespace ts.server { const fileNamePropertyReader: FilePropertyReader = { getFileName: x => x, getScriptKind: _ => undefined, - hasMixedContent: (fileName, extraFileExtensions) => { - const mixedContentExtensions = map(filter(extraFileExtensions, item => item.isMixedContent), item => item.extension); - return forEach(mixedContentExtensions, extension => fileExtensionIs(fileName, extension)); - } + hasMixedContent: (fileName, extraFileExtensions) => some(extraFileExtensions, ext => ext.isMixedContent && fileExtensionIs(fileName, ext.extension)), }; const externalFilePropertyReader: FilePropertyReader = { From f37d9068ff14a2d8a4955d4a872b8ee33046d388 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 19 Jul 2017 14:47:25 -0700 Subject: [PATCH 28/34] Fix configure-nightly script to match new contents of core.ts (#17014) * Fix configureNightly script to match new contents of core.ts * Use ts.Debug.assert * Use a regexp for parsePackageJsonVersion --- scripts/configureNightly.ts | 58 ++++++++++++++++++++++--------------- src/compiler/core.ts | 2 ++ 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/scripts/configureNightly.ts b/scripts/configureNightly.ts index 640f330b376..778f5ce3727 100644 --- a/scripts/configureNightly.ts +++ b/scripts/configureNightly.ts @@ -19,47 +19,57 @@ function main(): void { // Acquire the version from the package.json file and modify it appropriately. const packageJsonFilePath = ts.normalizePath(sys.args[0]); - const packageJsonContents = sys.readFile(packageJsonFilePath); - const packageJsonValue: PackageJson = JSON.parse(packageJsonContents); + const packageJsonValue: PackageJson = JSON.parse(sys.readFile(packageJsonFilePath)); - const nightlyVersion = getNightlyVersionString(packageJsonValue.version); - - // Modify the package.json structure - packageJsonValue.version = nightlyVersion; + const { majorMinor, patch } = parsePackageJsonVersion(packageJsonValue.version); + const nightlyPatch = getNightlyPatch(patch); // Acquire and modify the source file that exposes the version string. const tsFilePath = ts.normalizePath(sys.args[1]); - const tsFileContents = sys.readFile(tsFilePath); - const versionAssignmentRegExp = /export\s+const\s+version\s+=\s+".*";/; - const modifiedTsFileContents = tsFileContents.replace(versionAssignmentRegExp, `export const version = "${nightlyVersion}";`); + const tsFileContents = ts.sys.readFile(tsFilePath); + const modifiedTsFileContents = updateTsFile(tsFilePath, tsFileContents, majorMinor, patch, nightlyPatch); // Ensure we are actually changing something - the user probably wants to know that the update failed. if (tsFileContents === modifiedTsFileContents) { let err = `\n '${tsFilePath}' was not updated while configuring for a nightly publish.\n `; - - if (tsFileContents.match(versionAssignmentRegExp)) { - err += `Ensure that you have not already run this script; otherwise, erase your changes using 'git checkout -- "${tsFilePath}"'.`; - } - else { - err += `The file seems to no longer have a string matching '${versionAssignmentRegExp}'.`; - } - + err += `Ensure that you have not already run this script; otherwise, erase your changes using 'git checkout -- "${tsFilePath}"'.`; throw err + "\n"; } // Finally write the changes to disk. + // Modify the package.json structure + packageJsonValue.version = `${majorMinor}.${nightlyPatch}`; sys.writeFile(packageJsonFilePath, JSON.stringify(packageJsonValue, /*replacer:*/ undefined, /*space:*/ 4)) sys.writeFile(tsFilePath, modifiedTsFileContents); } -function getNightlyVersionString(versionString: string): string { - // If the version string already contains "-nightly", - // then get the base string and update based on that. - const dashNightlyPos = versionString.indexOf("-dev"); - if (dashNightlyPos >= 0) { - versionString = versionString.slice(0, dashNightlyPos); +function updateTsFile(tsFilePath: string, tsFileContents: string, majorMinor: string, patch: string, nightlyPatch: string): string { + const majorMinorRgx = /export const versionMajorMinor = "(\d+\.\d+)"/; + const majorMinorMatch = majorMinorRgx.exec(tsFileContents); + ts.Debug.assert(majorMinorMatch !== null, "", () => `The file seems to no longer have a string matching '${majorMinorRgx}'.`); + const parsedMajorMinor = majorMinorMatch[1]; + ts.Debug.assert(parsedMajorMinor === majorMinor, "versionMajorMinor does not match.", () => `${tsFilePath}: '${parsedMajorMinor}'; package.json: '${majorMinor}'`); + + const versionRgx = /export const version = `\$\{versionMajorMinor\}\.(\d)`;/; + const patchMatch = versionRgx.exec(tsFileContents); + ts.Debug.assert(patchMatch !== null, "The file seems to no longer have a string matching", () => versionRgx.toString()); + const parsedPatch = patchMatch[1]; + if (parsedPatch !== patch) { + throw new Error(`patch does not match. ${tsFilePath}: '${parsedPatch}; package.json: '${patch}'`); } + return tsFileContents.replace(versionRgx, `export const version = \`\${versionMajorMinor}.${nightlyPatch}\`;`); +} + +function parsePackageJsonVersion(versionString: string): { majorMinor: string, patch: string } { + const versionRgx = /(\d+\.\d+)\.(\d+)($|\-)/; + const match = versionString.match(versionRgx); + ts.Debug.assert(match !== null, "package.json 'version' should match", () => versionRgx.toString()); + return { majorMinor: match[1], patch: match[2] }; +} + +/** e.g. 0-dev.20170707 */ +function getNightlyPatch(plainPatch: string): string { // We're going to append a representation of the current time at the end of the current version. // String.prototype.toISOString() returns a 24-character string formatted as 'YYYY-MM-DDTHH:mm:ss.sssZ', // but we'd prefer to just remove separators and limit ourselves to YYYYMMDD. @@ -67,7 +77,7 @@ function getNightlyVersionString(versionString: string): string { const now = new Date(); const timeStr = now.toISOString().replace(/:|T|\.|-/g, "").slice(0, 8); - return `${versionString}-dev.${timeStr}`; + return `${plainPatch}-dev.${timeStr}`; } main(); \ No newline at end of file diff --git a/src/compiler/core.ts b/src/compiler/core.ts index cecf88f42ce..a72a257a400 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -2,6 +2,8 @@ /// namespace ts { + // WARNING: The script `configureNightly.ts` uses a regexp to parse out these values. + // If changing the text in this section, be sure to test `configureNightly` too. export const versionMajorMinor = "2.5"; /** The version of the TypeScript compiler release */ export const version = `${versionMajorMinor}.0`; From e52ed1a23a470db3465704b8bc3c36dcead1eb4e Mon Sep 17 00:00:00 2001 From: gcnew Date: Thu, 20 Jul 2017 02:48:30 +0300 Subject: [PATCH 29/34] Check the return type of type guard functions --- src/compiler/checker.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 70827becffe..253a8708f3a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17996,6 +17996,8 @@ namespace ts { return; } + checkSourceElement(node.type); + const { parameterName } = node; if (isThisTypePredicate(typePredicate)) { getTypeFromThisTypeNode(parameterName as ThisTypeNode); From ca2a8e8518b84ce60502ba0c390acd67aab4121f Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 19 Jul 2017 16:59:27 -0700 Subject: [PATCH 30/34] Fix typeOperatingSpacingRule:use ReadonlyArray --- scripts/tslint/typeOperatorSpacingRule.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tslint/typeOperatorSpacingRule.ts b/scripts/tslint/typeOperatorSpacingRule.ts index 4bd70e6eefa..d7da2e6b5e8 100644 --- a/scripts/tslint/typeOperatorSpacingRule.ts +++ b/scripts/tslint/typeOperatorSpacingRule.ts @@ -19,7 +19,7 @@ function walk(ctx: Lint.WalkContext): void { ts.forEachChild(node, recur); } - function check(types: ts.TypeNode[]): void { + function check(types: ReadonlyArray): void { let expectedStart = types[0].end + 2; // space, | or & for (let i = 1; i < types.length; i++) { const currentType = types[i]; From 0654fa285cc1f349f0ef2f2a61b4ee4fed27a038 Mon Sep 17 00:00:00 2001 From: gcnew Date: Thu, 20 Jul 2017 02:50:55 +0300 Subject: [PATCH 31/34] Added tests --- .../typeGuardFunctionErrors.errors.txt | 63 +++++++++++++++++-- .../reference/typeGuardFunctionErrors.js | 32 ++++++++-- .../typeGuards/typeGuardFunctionErrors.ts | 28 ++++++++- 3 files changed, 112 insertions(+), 11 deletions(-) diff --git a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt index 63442a93b6b..46455687af2 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt +++ b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt @@ -62,9 +62,21 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(123,20 tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(128,34): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(132,34): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(136,39): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(152,68): error TS2344: Type 'T | "d"' does not satisfy the constraint 'Keys'. + Type '"d"' is not assignable to type 'Keys'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(159,31): error TS2344: Type 'Bar' does not satisfy the constraint 'Foo'. + Types of property ''a'' are incompatible. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(162,31): error TS2344: Type 'Bar' does not satisfy the constraint 'Foo'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(163,35): error TS2344: Type 'number' does not satisfy the constraint 'Foo'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(164,51): error TS2344: Type 'Bar' does not satisfy the constraint 'Foo'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(165,51): error TS2344: Type 'number' does not satisfy the constraint 'Foo'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(166,45): error TS2677: A type predicate's type must be assignable to its parameter's type. + Type 'NeedsFoo' is not assignable to type 'number'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(166,54): error TS2344: Type 'number' does not satisfy the constraint 'Foo'. -==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts (54 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts (62 errors) ==== class A { ~ !!! error TS2300: Duplicate identifier 'A'. @@ -175,7 +187,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(136,39 // No type guard in if statement if (hasNoTypeGuard(a)) { - a.propB; + a.propB; ~~~~~ !!! error TS2551: Property 'propB' does not exist on type 'A'. Did you mean 'propA'? } @@ -208,7 +220,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(136,39 return true; }; - // No matching signature + // No matching signature var assign3: (p1, p2) => p1 is A; assign3 = function(p1, p2, p3): p1 is A { ~~~~~~~ @@ -326,4 +338,47 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(136,39 var x: A; if (hasMissingParameter()) { x.propA; - } \ No newline at end of file + } + + // repro #17297 + + type Keys = 'a'|'b'|'c' + type KeySet = { [k in T]: true } + + // expected an error, since Keys doesn't have a 'd' + declare function hasKey(x: KeySet): x is KeySet; + ~~~~~ +!!! error TS2344: Type 'T | "d"' does not satisfy the constraint 'Keys'. +!!! error TS2344: Type '"d"' is not assignable to type 'Keys'. + + type Foo = { 'a': string; } + type Bar = { 'a': number; } + + interface NeedsFoo { + foo: T; + isFoo(): this is NeedsFoo; // should error + ~~~ +!!! error TS2344: Type 'Bar' does not satisfy the constraint 'Foo'. +!!! error TS2344: Types of property ''a'' are incompatible. +!!! error TS2344: Type 'number' is not assignable to type 'string'. + }; + + declare var anError: NeedsFoo; // error, as expected + ~~~ +!!! error TS2344: Type 'Bar' does not satisfy the constraint 'Foo'. + declare var alsoAnError: NeedsFoo; // also error, as expected + ~~~~~~ +!!! error TS2344: Type 'number' does not satisfy the constraint 'Foo'. + declare function newError1(x: any): x is NeedsFoo; // should error + ~~~ +!!! error TS2344: Type 'Bar' does not satisfy the constraint 'Foo'. + declare function newError2(x: any): x is NeedsFoo; // should error + ~~~~~~ +!!! error TS2344: Type 'number' does not satisfy the constraint 'Foo'. + declare function newError3(x: number): x is NeedsFoo; // should error + ~~~~~~~~~~~~~~~~ +!!! error TS2677: A type predicate's type must be assignable to its parameter's type. +!!! error TS2677: Type 'NeedsFoo' is not assignable to type 'number'. + ~~~~~~ +!!! error TS2344: Type 'number' does not satisfy the constraint 'Foo'. + \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardFunctionErrors.js b/tests/baselines/reference/typeGuardFunctionErrors.js index cc773ae11b1..12ffab852d8 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.js +++ b/tests/baselines/reference/typeGuardFunctionErrors.js @@ -67,7 +67,7 @@ if (funA(0, a)) { // No type guard in if statement if (hasNoTypeGuard(a)) { - a.propB; + a.propB; } // Type predicate type is not assignable @@ -86,7 +86,7 @@ assign2 = function(p1, p2): p2 is A { return true; }; -// No matching signature +// No matching signature var assign3: (p1, p2) => p1 is A; assign3 = function(p1, p2, p3): p1 is A { return true; @@ -142,7 +142,30 @@ function b7({a, b, c: {p1}}, p2, p3): p1 is A { var x: A; if (hasMissingParameter()) { x.propA; -} +} + +// repro #17297 + +type Keys = 'a'|'b'|'c' +type KeySet = { [k in T]: true } + +// expected an error, since Keys doesn't have a 'd' +declare function hasKey(x: KeySet): x is KeySet; + +type Foo = { 'a': string; } +type Bar = { 'a': number; } + +interface NeedsFoo { + foo: T; + isFoo(): this is NeedsFoo; // should error +}; + +declare var anError: NeedsFoo; // error, as expected +declare var alsoAnError: NeedsFoo; // also error, as expected +declare function newError1(x: any): x is NeedsFoo; // should error +declare function newError2(x: any): x is NeedsFoo; // should error +declare function newError3(x: number): x is NeedsFoo; // should error + //// [typeGuardFunctionErrors.js] var __extends = (this && this.__extends) || (function () { @@ -224,7 +247,7 @@ var assign2; assign2 = function (p1, p2) { return true; }; -// No matching signature +// No matching signature var assign3; assign3 = function (p1, p2, p3) { return true; @@ -290,3 +313,4 @@ var x; if (hasMissingParameter()) { x.propA; } +; diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts index 688099280c5..eb61473c5cc 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts @@ -67,7 +67,7 @@ if (funA(0, a)) { // No type guard in if statement if (hasNoTypeGuard(a)) { - a.propB; + a.propB; } // Type predicate type is not assignable @@ -86,7 +86,7 @@ assign2 = function(p1, p2): p2 is A { return true; }; -// No matching signature +// No matching signature var assign3: (p1, p2) => p1 is A; assign3 = function(p1, p2, p3): p1 is A { return true; @@ -142,4 +142,26 @@ function b7({a, b, c: {p1}}, p2, p3): p1 is A { var x: A; if (hasMissingParameter()) { x.propA; -} \ No newline at end of file +} + +// repro #17297 + +type Keys = 'a'|'b'|'c' +type KeySet = { [k in T]: true } + +// expected an error, since Keys doesn't have a 'd' +declare function hasKey(x: KeySet): x is KeySet; + +type Foo = { 'a': string; } +type Bar = { 'a': number; } + +interface NeedsFoo { + foo: T; + isFoo(): this is NeedsFoo; // should error +}; + +declare var anError: NeedsFoo; // error, as expected +declare var alsoAnError: NeedsFoo; // also error, as expected +declare function newError1(x: any): x is NeedsFoo; // should error +declare function newError2(x: any): x is NeedsFoo; // should error +declare function newError3(x: number): x is NeedsFoo; // should error From ed87b40902bb3d205556542249b67aec009fb2f6 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 19 Jul 2017 17:06:31 -0700 Subject: [PATCH 32/34] Fix linter (#17312) We just merged a change which makes the `.types` member of a union or intersection type a readonly array. Our lint rule's type annotation needs to reflect that. --- scripts/tslint/typeOperatorSpacingRule.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tslint/typeOperatorSpacingRule.ts b/scripts/tslint/typeOperatorSpacingRule.ts index 4bd70e6eefa..d7da2e6b5e8 100644 --- a/scripts/tslint/typeOperatorSpacingRule.ts +++ b/scripts/tslint/typeOperatorSpacingRule.ts @@ -19,7 +19,7 @@ function walk(ctx: Lint.WalkContext): void { ts.forEachChild(node, recur); } - function check(types: ts.TypeNode[]): void { + function check(types: ReadonlyArray): void { let expectedStart = types[0].end + 2; // space, | or & for (let i = 1; i < types.length; i++) { const currentType = types[i]; From 53e4040cebcfc7dca821bf04a244f578010b8ae8 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 20 Jul 2017 06:45:22 -0700 Subject: [PATCH 33/34] Remove duplicate `emptyArray`s (#17305) --- src/compiler/checker.ts | 2 +- src/compiler/program.ts | 15 +++++++-------- src/compiler/types.ts | 6 +++--- src/services/signatureHelp.ts | 2 -- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 70827becffe..a312f91c8f6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23552,7 +23552,7 @@ namespace ts { } // Initialize global symbol table - let augmentations: LiteralExpression[][]; + let augmentations: ReadonlyArray[]; for (const file of host.getSourceFiles()) { if (!isExternalOrCommonJsModule(file)) { mergeSymbolTable(globals, file.locals); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 3d63edddac1..f15baeefcdb 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -3,7 +3,6 @@ /// namespace ts { - const emptyArray: any[] = []; const ignoreDiagnosticCommentRegEx = /(^\s*$)|(^\s*\/\/\/?\s*(@ts-ignore)?)/; export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName = "tsconfig.json"): string { @@ -1390,8 +1389,8 @@ namespace ts { const isExternalModuleFile = isExternalModule(file); // file.imports may not be undefined if there exists dynamic import - let imports: LiteralExpression[]; - let moduleAugmentations: LiteralExpression[]; + let imports: StringLiteral[]; + let moduleAugmentations: StringLiteral[]; let ambientModules: string[]; // If we are importing helpers, we need to add a synthetic reference to resolve the @@ -1426,23 +1425,23 @@ namespace ts { case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.ExportDeclaration: const moduleNameExpr = getExternalModuleName(node); - if (!moduleNameExpr || moduleNameExpr.kind !== SyntaxKind.StringLiteral) { + if (!moduleNameExpr || !isStringLiteral(moduleNameExpr)) { break; } - if (!(moduleNameExpr).text) { + if (!moduleNameExpr.text) { break; } // TypeScript 1.0 spec (April 2014): 12.1.6 // An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference other external modules // only through top - level external module names. Relative external module names are not permitted. - if (!inAmbientModule || !isExternalModuleNameRelative((moduleNameExpr).text)) { - (imports || (imports = [])).push(moduleNameExpr); + if (!inAmbientModule || !isExternalModuleNameRelative(moduleNameExpr.text)) { + (imports || (imports = [])).push(moduleNameExpr); } break; case SyntaxKind.ModuleDeclaration: if (isAmbientModule(node) && (inAmbientModule || hasModifier(node, ModifierFlags.Ambient) || file.isDeclarationFile)) { - const moduleName = (node).name; + const moduleName = (node).name; // Ambient module declarations can be interpreted as augmentations for some existing external modules. // This will happen in two cases: // - if current file is external module then module augmentation is a ambient module declaration defined in the top level scope diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e16ee34b01a..0d8041220b0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2321,10 +2321,10 @@ namespace ts { // Content of this field should never be used directly - use getResolvedModuleFileName/setResolvedModuleFileName functions instead /* @internal */ resolvedModules: Map; /* @internal */ resolvedTypeReferenceDirectiveNames: Map; - /* @internal */ imports: StringLiteral[]; - /* @internal */ moduleAugmentations: StringLiteral[]; + /* @internal */ imports: ReadonlyArray; + /* @internal */ moduleAugmentations: ReadonlyArray; /* @internal */ patternAmbientModules?: PatternAmbientModule[]; - /* @internal */ ambientModuleNames: string[]; + /* @internal */ ambientModuleNames: ReadonlyArray; /* @internal */ checkJsDirective: CheckJsDirective | undefined; } diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 00ab0165805..71e6bc00b00 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -1,8 +1,6 @@ /// /* @internal */ namespace ts.SignatureHelp { - const emptyArray: any[] = []; - export const enum ArgumentListKind { TypeArguments, CallArguments, From c60774b4c629c4d38e3e24b8e08edfccdde8e576 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 20 Jul 2017 08:54:47 -0700 Subject: [PATCH 34/34] Make many 'static' variables readonly (#17306) --- src/harness/fourslash.ts | 2 +- src/harness/harnessLanguageService.ts | 2 +- src/harness/rwcRunner.ts | 4 +--- src/harness/test262Runner.ts | 12 ++++++------ src/server/editorServices.ts | 2 +- src/server/project.ts | 2 +- src/services/formatting/ruleOperationContext.ts | 4 ++-- 7 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 5e83c5339c6..d32bf92b007 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -130,7 +130,7 @@ namespace FourSlash { // 0 - cancelled // >0 - not cancelled // <0 - not cancelled and value denotes number of isCancellationRequested after which token become cancelled - private static NotCanceled: number = -1; + private static readonly NotCanceled: number = -1; private numberOfCallsBeforeCancellation: number = TestCancellationToken.NotCanceled; public isCancellationRequested(): boolean { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 156a9e08dce..c604b224656 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -108,7 +108,7 @@ namespace Harness.LanguageService { } class DefaultHostCancellationToken implements ts.HostCancellationToken { - public static Instance = new DefaultHostCancellationToken(); + public static readonly Instance = new DefaultHostCancellationToken(); public isCancellationRequested() { return false; diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index 1b7d54595e0..a25a0181511 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -238,10 +238,8 @@ namespace RWC { } class RWCRunner extends RunnerBase { - private static sourcePath = "internal/cases/rwc/"; - public enumerateTestFiles() { - return Harness.IO.listFiles(RWCRunner.sourcePath, /.+\.json$/); + return Harness.IO.listFiles("internal/cases/rwc/", /.+\.json$/); } public kind(): TestRunnerKind { diff --git a/src/harness/test262Runner.ts b/src/harness/test262Runner.ts index 939a02c7634..6c5b186f2b8 100644 --- a/src/harness/test262Runner.ts +++ b/src/harness/test262Runner.ts @@ -4,19 +4,19 @@ /* tslint:disable:no-null-keyword */ class Test262BaselineRunner extends RunnerBase { - private static basePath = "internal/cases/test262"; - private static helpersFilePath = "tests/cases/test262-harness/helpers.d.ts"; - private static helperFile: Harness.Compiler.TestFile = { + private static readonly basePath = "internal/cases/test262"; + private static readonly helpersFilePath = "tests/cases/test262-harness/helpers.d.ts"; + private static readonly helperFile: Harness.Compiler.TestFile = { unitName: Test262BaselineRunner.helpersFilePath, content: Harness.IO.readFile(Test262BaselineRunner.helpersFilePath), }; - private static testFileExtensionRegex = /\.js$/; - private static options: ts.CompilerOptions = { + private static readonly testFileExtensionRegex = /\.js$/; + private static readonly options: ts.CompilerOptions = { allowNonTsExtensions: true, target: ts.ScriptTarget.Latest, module: ts.ModuleKind.CommonJS }; - private static baselineOptions: Harness.Baseline.BaselineOptions = { + private static readonly baselineOptions: Harness.Baseline.BaselineOptions = { Subfolder: "test262", Baselinefolder: "internal/baselines" }; diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 4a7fe91d450..2633de1b266 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1614,7 +1614,7 @@ namespace ts.server { } /** Makes a filename safe to insert in a RegExp */ - private static filenameEscapeRegexp = /[-\/\\^$*+?.()|[\]{}]/g; + private static readonly filenameEscapeRegexp = /[-\/\\^$*+?.()|[\]{}]/g; private static escapeFilenameForRegex(filename: string) { return filename.replace(this.filenameEscapeRegexp, "\\$&"); } diff --git a/src/server/project.ts b/src/server/project.ts index 3b09fc48e85..106089fad97 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -837,7 +837,7 @@ namespace ts.server { */ export class InferredProject extends Project { - private static newName = (() => { + private static readonly newName = (() => { let nextId = 1; return () => { const id = nextId; diff --git a/src/services/formatting/ruleOperationContext.ts b/src/services/formatting/ruleOperationContext.ts index 6a19e23d57d..bf96363ad7d 100644 --- a/src/services/formatting/ruleOperationContext.ts +++ b/src/services/formatting/ruleOperationContext.ts @@ -4,13 +4,13 @@ namespace ts.formatting { export class RuleOperationContext { - private customContextChecks: { (context: FormattingContext): boolean; }[]; + private readonly customContextChecks: { (context: FormattingContext): boolean; }[]; constructor(...funcs: { (context: FormattingContext): boolean; }[]) { this.customContextChecks = funcs; } - static Any: RuleOperationContext = new RuleOperationContext(); + static readonly Any: RuleOperationContext = new RuleOperationContext(); public IsAny(): boolean { return this === RuleOperationContext.Any;