diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8d8b60c7920..30d4818592b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6148,14 +6148,25 @@ 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. + // 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)) { + (matchingTypes || (matchingTypes = [])).push(t); + inferFromTypes(t, t); + } + } + // 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) { + 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 @@ -6317,9 +6328,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; } } @@ -6327,29 +6338,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[] { @@ -15493,6 +15492,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 f81af831531..fb62143ca5e 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 @@ -2507,6 +2510,10 @@ "Checking if '{0}' is the longest matching prefix for '{1}' - '{2}'.": { "category": "Message", "code": 6112 + }, + "Expected type of 'typings' field in 'package.json' to be 'string', got '{0}'.": { + "category": "Message", + "code": 6113 }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index ee942a17390..e4262458d30 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/src/compiler/program.ts b/src/compiler/program.ts index c4e8adf844b..36a0db6325b 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -429,13 +429,18 @@ namespace ts { } if (jsonContent.typings) { - const typingsFile = normalizePath(combinePaths(candidate, jsonContent.typings)); - if (traceEnabled) { - trace(host, Diagnostics.package_json_has_typings_field_0_that_references_1, jsonContent.typings, typingsFile); + if (typeof jsonContent.typings === "string") { + const typingsFile = normalizePath(combinePaths(candidate, jsonContent.typings)); + if (traceEnabled) { + trace(host, Diagnostics.package_json_has_typings_field_0_that_references_1, jsonContent.typings, typingsFile); + } + const result = loadModuleFromFile(extensions, typingsFile, failedLookupLocation, host, traceEnabled); + if (result) { + return result; + } } - const result = loadModuleFromFile(extensions, typingsFile , failedLookupLocation, host, traceEnabled); - if (result) { - return result; + else if (traceEnabled) { + trace(host, Diagnostics.Expected_type_of_typings_field_in_package_json_to_be_string_got_0, typeof jsonContent.typings); } } else { @@ -446,7 +451,7 @@ namespace ts { } else { if (traceEnabled) { - trace(host, Diagnostics.package_json_does_not_have_typings_field); + trace(host, Diagnostics.File_0_does_not_exist, packageJsonPath); } // record package json as one of failed lookup locations - in the future if this file will appear it will invalidate resolution results failedLookupLocation.push(packageJsonPath); 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 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/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/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/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/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); +} 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 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 diff --git a/tests/cases/unittests/moduleResolution.ts b/tests/cases/unittests/moduleResolution.ts index b6ee19f6de2..b005d4939cc 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" }) };