From f15fe5b335aacc1364fc6bf111dfba0f52b3d14b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20V=C5=A1eti=C4=8Dka?= Date: Fri, 6 Nov 2015 16:45:26 +0100 Subject: [PATCH 1/7] Improved error messages for property declarations --- src/compiler/checker.ts | 5 +++ src/compiler/diagnosticMessages.json | 5 ++- src/compiler/parser.ts | 38 ++++++++++++++----- .../reference/ClassDeclaration26.errors.txt | 23 +++++++++++ .../baselines/reference/ClassDeclaration26.js | 15 ++++++++ ...validConstOnPropertyDeclaration.errors.txt | 9 +++++ ...onWithInvalidConstOnPropertyDeclaration.js | 12 ++++++ ...nWithInvalidConstOnPropertyDeclaration2.js | 13 +++++++ ...InvalidConstOnPropertyDeclaration2.symbols | 10 +++++ ...thInvalidConstOnPropertyDeclaration2.types | 11 ++++++ tests/cases/compiler/ClassDeclaration26.ts | 5 +++ ...onWithInvalidConstOnPropertyDeclaration.ts | 3 ++ ...nWithInvalidConstOnPropertyDeclaration2.ts | 4 ++ .../cases/fourslash/getOccurrencesConst04.ts | 8 ++-- 14 files changed, 148 insertions(+), 13 deletions(-) create mode 100644 tests/baselines/reference/ClassDeclaration26.errors.txt create mode 100644 tests/baselines/reference/ClassDeclaration26.js create mode 100644 tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration.errors.txt create mode 100644 tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration.js create mode 100644 tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.js create mode 100644 tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.symbols create mode 100644 tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.types create mode 100644 tests/cases/compiler/ClassDeclaration26.ts create mode 100644 tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts create mode 100644 tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8d053fb001f..039ab731823 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15409,6 +15409,11 @@ namespace ts { let flags = 0; for (const modifier of node.modifiers) { switch (modifier.kind) { + case SyntaxKind.ConstKeyword: + if (node.kind !== SyntaxKind.EnumDeclaration && node.parent.kind === SyntaxKind.ClassDeclaration) { + return grammarErrorOnNode(node, Diagnostics.A_class_member_cannot_have_the_0_keyword, tokenToString(SyntaxKind.ConstKeyword)); + } + break; case SyntaxKind.PublicKeyword: case SyntaxKind.ProtectedKeyword: case SyntaxKind.PrivateKeyword: diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 24f17a79ef3..f517efa3741 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -791,7 +791,10 @@ "category": "Error", "code": 1247 }, - + "A class member cannot have the '{0}' keyword.": { + "category": "Error", + "code": 1248 + }, "'with' statements are not allowed in an async function block.": { "category": "Error", "code": 1300 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 3a3350f2166..4f88d680e44 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1134,6 +1134,14 @@ namespace ts { return token === t && tryParse(nextTokenCanFollowModifier); } + function nextTokenIsOnSameLineAndCanFollowModifier() { + nextToken(); + if (scanner.hasPrecedingLineBreak()) { + return false; + } + return canFollowModifier(); + } + function nextTokenCanFollowModifier() { if (token === SyntaxKind.ConstKeyword) { // 'const' is only a modifier if followed by 'enum'. @@ -1154,11 +1162,7 @@ namespace ts { return canFollowModifier(); } - nextToken(); - if (scanner.hasPrecedingLineBreak()) { - return false; - } - return canFollowModifier(); + return nextTokenIsOnSameLineAndCanFollowModifier(); } function parseAnyContextualModifier(): boolean { @@ -4923,15 +4927,31 @@ namespace ts { return decorators; } - function parseModifiers(): ModifiersArray { + /* + * There are situations in which a modifier like 'const' will appear unexpectedly, such as on a class member. + * In those situations, if we are entirely sure that 'const' is not valid on its own (such as when ASI takes effect + * and turns it into a standalone declaration), then it is better to parse it and report an error later. + * + * In such situations, 'permitInvalidConstAsModifier' should be set to true. + */ + function parseModifiers(permitInvalidConstAsModifier?: boolean): ModifiersArray { let flags = 0; let modifiers: ModifiersArray; while (true) { const modifierStart = scanner.getStartPos(); const modifierKind = token; - if (!parseAnyContextualModifier()) { - break; + if (token === SyntaxKind.ConstKeyword && permitInvalidConstAsModifier) { + // We need to ensure that any subsequent modifiers appear on the same line + // so that when 'const' is a standalone declaration, we don't issue an error. + if (!tryParse(nextTokenIsOnSameLineAndCanFollowModifier)) { + break; + } + } + else { + if (!parseAnyContextualModifier()) { + break; + } } if (!modifiers) { @@ -4976,7 +4996,7 @@ namespace ts { const fullStart = getNodePos(); const decorators = parseDecorators(); - const modifiers = parseModifiers(); + const modifiers = parseModifiers(/*permitInvalidConstAsModifier*/ true); const accessor = tryParseAccessorDeclaration(fullStart, decorators, modifiers); if (accessor) { diff --git a/tests/baselines/reference/ClassDeclaration26.errors.txt b/tests/baselines/reference/ClassDeclaration26.errors.txt new file mode 100644 index 00000000000..5e2d570b801 --- /dev/null +++ b/tests/baselines/reference/ClassDeclaration26.errors.txt @@ -0,0 +1,23 @@ +tests/cases/compiler/ClassDeclaration26.ts(2,22): error TS1005: ';' expected. +tests/cases/compiler/ClassDeclaration26.ts(4,5): error TS1068: Unexpected token. A constructor, method, accessor, or property was expected. +tests/cases/compiler/ClassDeclaration26.ts(4,20): error TS1005: '=' expected. +tests/cases/compiler/ClassDeclaration26.ts(4,23): error TS1005: '=>' expected. +tests/cases/compiler/ClassDeclaration26.ts(5,1): error TS1128: Declaration or statement expected. + + +==== tests/cases/compiler/ClassDeclaration26.ts (5 errors) ==== + class C { + public const var export foo = 10; + ~~~~~~ +!!! error TS1005: ';' expected. + + var constructor() { } + ~~~ +!!! error TS1068: Unexpected token. A constructor, method, accessor, or property was expected. + ~ +!!! error TS1005: '=' expected. + ~ +!!! error TS1005: '=>' expected. + } + ~ +!!! error TS1128: Declaration or statement expected. \ No newline at end of file diff --git a/tests/baselines/reference/ClassDeclaration26.js b/tests/baselines/reference/ClassDeclaration26.js new file mode 100644 index 00000000000..b0879502119 --- /dev/null +++ b/tests/baselines/reference/ClassDeclaration26.js @@ -0,0 +1,15 @@ +//// [ClassDeclaration26.ts] +class C { + public const var export foo = 10; + + var constructor() { } +} + +//// [ClassDeclaration26.js] +var C = (function () { + function C() { + this.foo = 10; + } + return C; +})(); +var constructor = function () { }; diff --git a/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration.errors.txt b/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration.errors.txt new file mode 100644 index 00000000000..d07f07e2096 --- /dev/null +++ b/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration.errors.txt @@ -0,0 +1,9 @@ +tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts(2,3): error TS1248: A class member cannot have the 'const' keyword. + + +==== tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts (1 errors) ==== + class AtomicNumbers { + static const H = 1; + ~~~~~~~~~~~~~~~~~~~ +!!! error TS1248: A class member cannot have the 'const' keyword. + } \ No newline at end of file diff --git a/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration.js b/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration.js new file mode 100644 index 00000000000..b8c4dbdf433 --- /dev/null +++ b/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration.js @@ -0,0 +1,12 @@ +//// [ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts] +class AtomicNumbers { + static const H = 1; +} + +//// [ClassDeclarationWithInvalidConstOnPropertyDeclaration.js] +var AtomicNumbers = (function () { + function AtomicNumbers() { + } + AtomicNumbers.H = 1; + return AtomicNumbers; +})(); diff --git a/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.js b/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.js new file mode 100644 index 00000000000..bd1f1e0c674 --- /dev/null +++ b/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.js @@ -0,0 +1,13 @@ +//// [ClassDeclarationWithInvalidConstOnPropertyDeclaration2.ts] +class C { + const + x = 10; +} + +//// [ClassDeclarationWithInvalidConstOnPropertyDeclaration2.js] +var C = (function () { + function C() { + this.x = 10; + } + return C; +})(); diff --git a/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.symbols b/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.symbols new file mode 100644 index 00000000000..ce0ab000017 --- /dev/null +++ b/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.symbols @@ -0,0 +1,10 @@ +=== tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.ts === +class C { +>C : Symbol(C, Decl(ClassDeclarationWithInvalidConstOnPropertyDeclaration2.ts, 0, 0)) + + const +>const : Symbol(const, Decl(ClassDeclarationWithInvalidConstOnPropertyDeclaration2.ts, 0, 9)) + + x = 10; +>x : Symbol(x, Decl(ClassDeclarationWithInvalidConstOnPropertyDeclaration2.ts, 1, 9)) +} diff --git a/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.types b/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.types new file mode 100644 index 00000000000..47139729995 --- /dev/null +++ b/tests/baselines/reference/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.types @@ -0,0 +1,11 @@ +=== tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.ts === +class C { +>C : C + + const +>const : any + + x = 10; +>x : number +>10 : number +} diff --git a/tests/cases/compiler/ClassDeclaration26.ts b/tests/cases/compiler/ClassDeclaration26.ts new file mode 100644 index 00000000000..0adcbc470e0 --- /dev/null +++ b/tests/cases/compiler/ClassDeclaration26.ts @@ -0,0 +1,5 @@ +class C { + public const var export foo = 10; + + var constructor() { } +} \ No newline at end of file diff --git a/tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts b/tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts new file mode 100644 index 00000000000..06af5a0fdb5 --- /dev/null +++ b/tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts @@ -0,0 +1,3 @@ +class AtomicNumbers { + static const H = 1; +} \ No newline at end of file diff --git a/tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.ts b/tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.ts new file mode 100644 index 00000000000..fe90dded9e3 --- /dev/null +++ b/tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.ts @@ -0,0 +1,4 @@ +class C { + const + x = 10; +} \ No newline at end of file diff --git a/tests/cases/fourslash/getOccurrencesConst04.ts b/tests/cases/fourslash/getOccurrencesConst04.ts index c7f293450d3..2a8ee4c9e2c 100644 --- a/tests/cases/fourslash/getOccurrencesConst04.ts +++ b/tests/cases/fourslash/getOccurrencesConst04.ts @@ -1,12 +1,14 @@ /// ////export const class C { -//// private static c/*1*/onst foo; -//// constructor(public con/*2*/st foo) { +//// private static c/*1*/onst f/*2*/oo; +//// constructor(public con/*3*/st foo) { //// } ////} goTo.marker("1"); -verify.occurrencesAtPositionCount(1); +verify.occurrencesAtPositionCount(0); goTo.marker("2"); +verify.occurrencesAtPositionCount(1); +goTo.marker("3"); verify.occurrencesAtPositionCount(0); \ No newline at end of file From e6259a557075beb808b264ff842e5b6c30d3fc9a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 4 Dec 2015 16:04:47 -0800 Subject: [PATCH 2/7] Fix union/union and intersection/intersection type inference --- src/compiler/checker.ts | 59 +++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a4816013753..68ae6b20456 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6087,14 +6087,23 @@ namespace ts { function inferFromTypes(source: Type, target: Type) { if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union || source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) { - // Source and target are both unions or both intersections. To improve the quality of - // inferences we first reduce the types by removing constituents that are identically - // matched by a constituent in the other type. For example, when inferring from - // 'string | string[]' to 'string | T', we reduce the types to 'string[]' and 'T'. - const reducedSource = reduceUnionOrIntersectionType(source, target); - const reducedTarget = reduceUnionOrIntersectionType(target, source); - source = reducedSource; - target = reducedTarget; + // Source and target are both unions or both intersections. First, find each + // target constituent type that has an identically matching source constituent + // type, and for each such target constituent type, infer from the type to itself. + let matchingTypes: Type[]; + for (const t of (target).types) { + if (typeIdenticalToSomeType(t, (source).types)) { + (matchingTypes || (matchingTypes = [])).push(t); + inferFromTypes(t, t); + } + } + // To improve the quality of inferences, reduce the source and target types by + // removing the identically matched constituents. For example, when inferring from + // 'string | string[]' to 'string | T' we reduce the types to 'string[]' and 'T'. + if (matchingTypes) { + source = removeTypesFromUnionOrIntersection(source, matchingTypes); + target = removeTypesFromUnionOrIntersection(target, matchingTypes); + } } if (target.flags & TypeFlags.TypeParameter) { // If target is a type parameter, make an inference, unless the source type contains @@ -6256,9 +6265,9 @@ namespace ts { } } - function typeIdenticalToSomeType(source: Type, target: UnionOrIntersectionType): boolean { - for (const t of target.types) { - if (isTypeIdenticalTo(source, t)) { + function typeIdenticalToSomeType(type: Type, types: Type[]): boolean { + for (const t of types) { + if (isTypeIdenticalTo(t, type)) { return true; } } @@ -6266,29 +6275,17 @@ namespace ts { } /** - * Return the reduced form of the source type. This type is computed by by removing all source - * constituents that have an identical match in the target type. + * Return a new union or intersection type computed by removing a given set of types + * from a given union or intersection type. */ - function reduceUnionOrIntersectionType(source: UnionOrIntersectionType, target: UnionOrIntersectionType) { - let sourceTypes = source.types; - let sourceIndex = 0; - let modified = false; - while (sourceIndex < sourceTypes.length) { - if (typeIdenticalToSomeType(sourceTypes[sourceIndex], target)) { - if (!modified) { - sourceTypes = sourceTypes.slice(0); - modified = true; - } - sourceTypes.splice(sourceIndex, 1); - } - else { - sourceIndex++; + function removeTypesFromUnionOrIntersection(type: UnionOrIntersectionType, typesToRemove: Type[]) { + const reducedTypes: Type[] = []; + for (const t of type.types) { + if (!typeIdenticalToSomeType(t, typesToRemove)) { + reducedTypes.push(t); } } - if (modified) { - return source.flags & TypeFlags.Union ? getUnionType(sourceTypes, /*noSubtypeReduction*/ true) : getIntersectionType(sourceTypes); - } - return source; + return type.flags & TypeFlags.Union ? getUnionType(reducedTypes, /*noSubtypeReduction*/ true) : getIntersectionType(reducedTypes); } function getInferenceCandidates(context: InferenceContext, index: number): Type[] { From ae9d93b41a9c9b3a4ccb874b7c1766d3ff18c074 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 4 Dec 2015 16:05:01 -0800 Subject: [PATCH 3/7] Adding test --- .../reference/recursiveUnionTypeInference.js | 14 +++++++++++ .../recursiveUnionTypeInference.symbols | 23 ++++++++++++++++++ .../recursiveUnionTypeInference.types | 24 +++++++++++++++++++ .../compiler/recursiveUnionTypeInference.ts | 7 ++++++ 4 files changed, 68 insertions(+) create mode 100644 tests/baselines/reference/recursiveUnionTypeInference.js create mode 100644 tests/baselines/reference/recursiveUnionTypeInference.symbols create mode 100644 tests/baselines/reference/recursiveUnionTypeInference.types create mode 100644 tests/cases/compiler/recursiveUnionTypeInference.ts diff --git a/tests/baselines/reference/recursiveUnionTypeInference.js b/tests/baselines/reference/recursiveUnionTypeInference.js new file mode 100644 index 00000000000..580a6bf83e5 --- /dev/null +++ b/tests/baselines/reference/recursiveUnionTypeInference.js @@ -0,0 +1,14 @@ +//// [recursiveUnionTypeInference.ts] +interface Foo { + x: T; +} + +function bar(x: Foo | string): T { + return bar(x); +} + + +//// [recursiveUnionTypeInference.js] +function bar(x) { + return bar(x); +} diff --git a/tests/baselines/reference/recursiveUnionTypeInference.symbols b/tests/baselines/reference/recursiveUnionTypeInference.symbols new file mode 100644 index 00000000000..88233573941 --- /dev/null +++ b/tests/baselines/reference/recursiveUnionTypeInference.symbols @@ -0,0 +1,23 @@ +=== tests/cases/compiler/recursiveUnionTypeInference.ts === +interface Foo { +>Foo : Symbol(Foo, Decl(recursiveUnionTypeInference.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveUnionTypeInference.ts, 0, 14)) + + x: T; +>x : Symbol(x, Decl(recursiveUnionTypeInference.ts, 0, 18)) +>T : Symbol(T, Decl(recursiveUnionTypeInference.ts, 0, 14)) +} + +function bar(x: Foo | string): T { +>bar : Symbol(bar, Decl(recursiveUnionTypeInference.ts, 2, 1)) +>T : Symbol(T, Decl(recursiveUnionTypeInference.ts, 4, 13)) +>x : Symbol(x, Decl(recursiveUnionTypeInference.ts, 4, 16)) +>Foo : Symbol(Foo, Decl(recursiveUnionTypeInference.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveUnionTypeInference.ts, 4, 13)) +>T : Symbol(T, Decl(recursiveUnionTypeInference.ts, 4, 13)) + + return bar(x); +>bar : Symbol(bar, Decl(recursiveUnionTypeInference.ts, 2, 1)) +>x : Symbol(x, Decl(recursiveUnionTypeInference.ts, 4, 16)) +} + diff --git a/tests/baselines/reference/recursiveUnionTypeInference.types b/tests/baselines/reference/recursiveUnionTypeInference.types new file mode 100644 index 00000000000..e5d62a051a5 --- /dev/null +++ b/tests/baselines/reference/recursiveUnionTypeInference.types @@ -0,0 +1,24 @@ +=== tests/cases/compiler/recursiveUnionTypeInference.ts === +interface Foo { +>Foo : Foo +>T : T + + x: T; +>x : T +>T : T +} + +function bar(x: Foo | string): T { +>bar : (x: Foo | string) => T +>T : T +>x : Foo | string +>Foo : Foo +>T : T +>T : T + + return bar(x); +>bar(x) : T +>bar : (x: Foo | string) => T +>x : Foo | string +} + diff --git a/tests/cases/compiler/recursiveUnionTypeInference.ts b/tests/cases/compiler/recursiveUnionTypeInference.ts new file mode 100644 index 00000000000..4ffc6fcf4a7 --- /dev/null +++ b/tests/cases/compiler/recursiveUnionTypeInference.ts @@ -0,0 +1,7 @@ +interface Foo { + x: T; +} + +function bar(x: Foo | string): T { + return bar(x); +} From 6901a98c85e309803c0faaa0055b460f99f89d10 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 5 Dec 2015 08:53:21 -0800 Subject: [PATCH 4/7] Adding a bit more text to comments --- src/compiler/checker.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 68ae6b20456..d2eeadea527 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6089,7 +6089,9 @@ namespace ts { source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) { // Source and target are both unions or both intersections. First, find each // target constituent type that has an identically matching source constituent - // type, and for each such target constituent type, infer from the type to itself. + // type, and for each such target constituent type infer from the type to itself. + // When inferring from a type to itself we effectively find all type parameter + // occurrences within that type and infer themselves as their type arguments. let matchingTypes: Type[]; for (const t of (target).types) { if (typeIdenticalToSomeType(t, (source).types)) { @@ -6097,7 +6099,7 @@ namespace ts { inferFromTypes(t, t); } } - // To improve the quality of inferences, reduce the source and target types by + // Next, to improve the quality of inferences, reduce the source and target types by // removing the identically matched constituents. For example, when inferring from // 'string | string[]' to 'string | T' we reduce the types to 'string[]' and 'T'. if (matchingTypes) { From 57c595a49d5e3208ec6ee0e521e45ad197608c3f Mon Sep 17 00:00:00 2001 From: Dan Corder Date: Mon, 7 Dec 2015 15:21:15 +0000 Subject: [PATCH 5/7] Fix for issue #5947 --- src/services/formatting/smartIndenter.ts | 1 + tests/cases/fourslash/smartIndentNamedImport.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 tests/cases/fourslash/smartIndentNamedImport.ts diff --git a/src/services/formatting/smartIndenter.ts b/src/services/formatting/smartIndenter.ts index de1d1b19cf2..bc02f6d6f32 100644 --- a/src/services/formatting/smartIndenter.ts +++ b/src/services/formatting/smartIndenter.ts @@ -461,6 +461,7 @@ namespace ts.formatting { case SyntaxKind.ParenthesizedType: case SyntaxKind.TaggedTemplateExpression: case SyntaxKind.AwaitExpression: + case SyntaxKind.NamedImports: return true; } return false; diff --git a/tests/cases/fourslash/smartIndentNamedImport.ts b/tests/cases/fourslash/smartIndentNamedImport.ts new file mode 100644 index 00000000000..37ef80f6316 --- /dev/null +++ b/tests/cases/fourslash/smartIndentNamedImport.ts @@ -0,0 +1,12 @@ +/// + +////import {/*0*/ +//// numbers as bn,/*1*/ +//// list/*2*/ +////} from '@bykov/basics';/*3*/ + +format.document(); +goTo.marker("0"); verify.currentLineContentIs("import {"); +goTo.marker("1"); verify.currentLineContentIs(" numbers as bn,"); +goTo.marker("2"); verify.currentLineContentIs(" list"); +goTo.marker("3"); verify.currentLineContentIs("} from '@bykov/basics';"); \ No newline at end of file From 384ee18c7b333840028839cea797e514bad74922 Mon Sep 17 00:00:00 2001 From: Tim Perry Date: Mon, 7 Dec 2015 18:25:25 +0100 Subject: [PATCH 6/7] Gracefully handle errors where 'typings' is not a string (fixes #4828) --- src/compiler/program.ts | 2 +- tests/cases/unittests/moduleResolution.ts | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index ed9f010c56a..96afd69ea4d 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -99,7 +99,7 @@ namespace ts { jsonContent = { typings: undefined }; } - if (jsonContent.typings) { + if (typeof jsonContent.typings === "string") { const result = loadNodeModuleFromFile(extensions, normalizePath(combinePaths(candidate, jsonContent.typings)), failedLookupLocation, host); if (result) { return result; diff --git a/tests/cases/unittests/moduleResolution.ts b/tests/cases/unittests/moduleResolution.ts index c4c17ebb223..5b924650401 100644 --- a/tests/cases/unittests/moduleResolution.ts +++ b/tests/cases/unittests/moduleResolution.ts @@ -99,13 +99,34 @@ module ts { assert.equal(resolution.failedLookupLocations.length, supportedTypeScriptExtensions.length); } - it("module name as directory - load from typings", () => { + it("module name as directory - load from 'typings'", () => { testLoadingFromPackageJson("/a/b/c/d.ts", "/a/b/c/bar/package.json", "c/d/e.d.ts", "/a/b/c/bar/c/d/e.d.ts", "./bar"); testLoadingFromPackageJson("/a/b/c/d.ts", "/a/bar/package.json", "e.d.ts", "/a/bar/e.d.ts", "../../bar"); testLoadingFromPackageJson("/a/b/c/d.ts", "/bar/package.json", "e.d.ts", "/bar/e.d.ts", "/bar"); testLoadingFromPackageJson("c:/a/b/c/d.ts", "c:/bar/package.json", "e.d.ts", "c:/bar/e.d.ts", "c:/bar"); }); + function testTypingsIgnored(typings: any): void { + let containingFile = { name: "/a/b.ts" }; + let packageJson = { name: "/node_modules/b/package.json", content: JSON.stringify({ "typings": typings }) }; + let moduleFile = { name: "/a/b.d.ts" }; + + let indexPath = "/node_modules/b/index.d.ts"; + let indexFile = { name: indexPath } + + let resolution = nodeModuleNameResolver("b", containingFile.name, {}, createModuleResolutionHost(containingFile, packageJson, moduleFile, indexFile)); + + assert.equal(resolution.resolvedModule.resolvedFileName, indexPath); + } + + it("module name as directory - handle invalid 'typings'", () => { + testTypingsIgnored(["a", "b"]); + testTypingsIgnored({ "a": "b" }); + testTypingsIgnored(true); + testTypingsIgnored(null); + testTypingsIgnored(undefined); + }); + it("module name as directory - load index.d.ts", () => { let containingFile = { name: "/a/b/c.ts" }; let packageJson = { name: "/a/b/foo/package.json", content: JSON.stringify({ main: "/c/d" }) }; From 3bf39d6df172a5c30ac9725403ce3f251f715454 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Mon, 7 Dec 2015 09:51:00 -0800 Subject: [PATCH 7/7] Explicitly list tuple overloads for `Promise.all`. Previously, the array type would infer a union type for multiple arguments of differing types. The original overload remains, but I added 9 tuple-based overloads to support up to 10 heterogeneously-typed arguments. --- src/lib/es6.d.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/lib/es6.d.ts b/src/lib/es6.d.ts index e2b26747516..531dbf9e5ea 100644 --- a/src/lib/es6.d.ts +++ b/src/lib/es6.d.ts @@ -1224,7 +1224,7 @@ declare namespace Reflect { function isExtensible(target: any): boolean; function ownKeys(target: any): Array; function preventExtensions(target: any): boolean; - function set(target: any, propertyKey: PropertyKey, value: any, receiver? :any): boolean; + function set(target: any, propertyKey: PropertyKey, value: any, receiver?: any): boolean; function setPrototypeOf(target: any, proto: any): boolean; } @@ -1272,7 +1272,16 @@ interface PromiseConstructor { * @param values An array of Promises. * @returns A new Promise. */ - all(values: Iterable>): Promise; + all(values: [T1 | PromiseLike, T2 | PromiseLike]): Promise<[T1, T2]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike]): Promise<[T1, T2, T3]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike ]): Promise<[T1, T2, T3, T4]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike , T5 | PromiseLike]): Promise<[T1, T2, T3, T4, T5]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike , T5 | PromiseLike, T6 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike , T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike , T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike , T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike, T9 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9]>; + all(values: [T1 | PromiseLike, T2 | PromiseLike, T3 | PromiseLike, T4 | PromiseLike , T5 | PromiseLike, T6 | PromiseLike, T7 | PromiseLike, T8 | PromiseLike, T9 | PromiseLike, T10 | PromiseLike]): Promise<[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]>; + all(values: Iterable>): Promise; /** * Creates a Promise that is resolved or rejected when any of the provided Promises are resolved