diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 2b57aca48acc9..07265cf946206 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -4785,9 +4785,14 @@ namespace ts {
let result = Ternary.True;
let properties = getPropertiesOfObjectType(target);
let requireOptionalProperties = relation === subtypeRelation && !(source.flags & TypeFlags.ObjectLiteral);
+ let foundMatchingProperty = !isWeak(target);
for (let targetProp of properties) {
let sourceProp = getPropertyOfType(source, targetProp.name);
+ if (sourceProp) {
+ foundMatchingProperty = true;
+ }
+
if (sourceProp !== targetProp) {
if (!sourceProp) {
if (!(targetProp.flags & SymbolFlags.Optional) || requireOptionalProperties) {
@@ -4859,6 +4864,14 @@ namespace ts {
}
}
}
+
+ if (!foundMatchingProperty && getPropertiesOfType(source).length > 0) {
+ if (reportErrors) {
+ reportError(Diagnostics.Weak_type_0_has_no_properties_in_common_with_1, typeToString(target), typeToString(source));
+ }
+ return Ternary.False;
+ }
+
return result;
}
@@ -5121,6 +5134,17 @@ namespace ts {
return false;
}
+ // A type is 'weak' if it is an object type with at least one optional property
+ // and no required properties or index signatures
+ function isWeak(type: Type) {
+ let props = getPropertiesOfType(type);
+ return type.flags & TypeFlags.ObjectType &&
+ props.length > 0 &&
+ !forEach(props, p => !(p.flags & SymbolFlags.Optional)) &&
+ !getIndexTypeOfType(type, IndexKind.String) &&
+ !getIndexTypeOfType(type, IndexKind.Number);
+ }
+
function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
return compareProperties(sourceProp, targetProp, compareTypes) !== Ternary.False;
}
diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts
index a17f885759081..12b257eac3447 100644
--- a/src/compiler/diagnosticInformationMap.generated.ts
+++ b/src/compiler/diagnosticInformationMap.generated.ts
@@ -414,6 +414,7 @@ namespace ts {
The_arguments_object_cannot_be_referenced_in_an_async_arrow_function_Consider_using_a_standard_async_function_expression: { code: 2522, category: DiagnosticCategory.Error, key: "The 'arguments' object cannot be referenced in an async arrow function. Consider using a standard async function expression." },
yield_expressions_cannot_be_used_in_a_parameter_initializer: { code: 2523, category: DiagnosticCategory.Error, key: "'yield' expressions cannot be used in a parameter initializer." },
await_expressions_cannot_be_used_in_a_parameter_initializer: { code: 2524, category: DiagnosticCategory.Error, key: "'await' expressions cannot be used in a parameter initializer." },
+ Weak_type_0_has_no_properties_in_common_with_1: { code: 2525, category: DiagnosticCategory.Error, key: "Weak type '{0}' has no properties in common with '{1}'." },
JSX_element_attributes_type_0_must_be_an_object_type: { code: 2600, category: DiagnosticCategory.Error, key: "JSX element attributes type '{0}' must be an object type." },
The_return_type_of_a_JSX_element_constructor_must_return_an_object_type: { code: 2601, category: DiagnosticCategory.Error, key: "The return type of a JSX element constructor must return an object type." },
JSX_element_implicitly_has_type_any_because_the_global_type_JSX_Element_does_not_exist: { code: 2602, category: DiagnosticCategory.Error, key: "JSX element implicitly has type 'any' because the global type 'JSX.Element' does not exist." },
diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json
index 94be4f53c9717..59c856e87d20d 100644
--- a/src/compiler/diagnosticMessages.json
+++ b/src/compiler/diagnosticMessages.json
@@ -1645,6 +1645,10 @@
"category": "Error",
"code": 2524
},
+ "Weak type '{0}' has no properties in common with '{1}'.": {
+ "category": "Error",
+ "code": 2525
+ },
"JSX element attributes type '{0}' must be an object type.": {
"category": "Error",
"code": 2600
diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts
index 91d400ff26361..04cad8775618d 100644
--- a/src/server/editorServices.ts
+++ b/src/server/editorServices.ts
@@ -912,7 +912,7 @@ namespace ts.server {
var dirPath = ts.getDirectoryPath(configFilename);
var rawConfig: { config?: ProjectOptions; error?: Diagnostic; } = ts.readConfigFile(configFilename);
if (rawConfig.error) {
- return rawConfig.error;
+ return { errorMsg: ts.flattenDiagnosticMessageText(rawConfig.error.messageText, '\n') };
}
else {
var parsedCommandLine = ts.parseConfigFile(rawConfig.config, this.host, dirPath);
diff --git a/tests/baselines/reference/assignmentCompatWithObjectMembersOptionality2.errors.txt b/tests/baselines/reference/assignmentCompatWithObjectMembersOptionality2.errors.txt
index de2751beacaba..e8d6b32d21c32 100644
--- a/tests/baselines/reference/assignmentCompatWithObjectMembersOptionality2.errors.txt
+++ b/tests/baselines/reference/assignmentCompatWithObjectMembersOptionality2.errors.txt
@@ -1,3 +1,18 @@
+tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersOptionality2.ts(33,5): error TS2322: Type 'D' is not assignable to type 'C'.
+ Weak type 'C' has no properties in common with 'D'.
+tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersOptionality2.ts(34,5): error TS2322: Type 'E' is not assignable to type 'C'.
+ Weak type 'C' has no properties in common with 'E'.
+tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersOptionality2.ts(35,5): error TS2322: Type 'F' is not assignable to type 'C'.
+ Weak type 'C' has no properties in common with 'F'.
+tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersOptionality2.ts(38,5): error TS2322: Type 'D' is not assignable to type '{ opt?: Base; }'.
+ Weak type '{ opt?: Base; }' has no properties in common with 'D'.
+tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersOptionality2.ts(39,5): error TS2322: Type 'E' is not assignable to type '{ opt?: Base; }'.
+ Weak type '{ opt?: Base; }' has no properties in common with 'E'.
+tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersOptionality2.ts(40,5): error TS2322: Type 'F' is not assignable to type '{ opt?: Base; }'.
+ Weak type '{ opt?: Base; }' has no properties in common with 'F'.
+tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersOptionality2.ts(43,5): error TS2322: Type 'D' is not assignable to type '{ opt?: Base; }'.
+tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersOptionality2.ts(44,5): error TS2322: Type 'E' is not assignable to type '{ opt?: Base; }'.
+tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersOptionality2.ts(45,5): error TS2322: Type 'F' is not assignable to type '{ opt?: Base; }'.
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersOptionality2.ts(74,5): error TS2322: Type 'D' is not assignable to type 'C'.
Property 'opt' is missing in type 'D'.
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersOptionality2.ts(75,5): error TS2322: Type 'E' is not assignable to type 'C'.
@@ -18,7 +33,7 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
Property 'opt' is missing in type 'F'.
-==== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersOptionality2.ts (9 errors) ====
+==== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithObjectMembersOptionality2.ts (18 errors) ====
// M is optional and S contains no property with the same name as M
// N is optional and T contains no property with the same name as N
@@ -52,18 +67,42 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
// all ok
c = d;
+ ~
+!!! error TS2322: Type 'D' is not assignable to type 'C'.
+!!! error TS2322: Weak type 'C' has no properties in common with 'D'.
c = e;
+ ~
+!!! error TS2322: Type 'E' is not assignable to type 'C'.
+!!! error TS2322: Weak type 'C' has no properties in common with 'E'.
c = f;
+ ~
+!!! error TS2322: Type 'F' is not assignable to type 'C'.
+!!! error TS2322: Weak type 'C' has no properties in common with 'F'.
c = a;
a = d;
+ ~
+!!! error TS2322: Type 'D' is not assignable to type '{ opt?: Base; }'.
+!!! error TS2322: Weak type '{ opt?: Base; }' has no properties in common with 'D'.
a = e;
+ ~
+!!! error TS2322: Type 'E' is not assignable to type '{ opt?: Base; }'.
+!!! error TS2322: Weak type '{ opt?: Base; }' has no properties in common with 'E'.
a = f;
+ ~
+!!! error TS2322: Type 'F' is not assignable to type '{ opt?: Base; }'.
+!!! error TS2322: Weak type '{ opt?: Base; }' has no properties in common with 'F'.
a = c;
b = d;
+ ~
+!!! error TS2322: Type 'D' is not assignable to type '{ opt?: Base; }'.
b = e;
+ ~
+!!! error TS2322: Type 'E' is not assignable to type '{ opt?: Base; }'.
b = f;
+ ~
+!!! error TS2322: Type 'F' is not assignable to type '{ opt?: Base; }'.
b = a;
b = c;
}
diff --git a/tests/baselines/reference/subtypingWithObjectMembers5.errors.txt b/tests/baselines/reference/subtypingWithObjectMembers5.errors.txt
index 7fd025721ec27..f42947c6eff6f 100644
--- a/tests/baselines/reference/subtypingWithObjectMembers5.errors.txt
+++ b/tests/baselines/reference/subtypingWithObjectMembers5.errors.txt
@@ -4,9 +4,15 @@ tests/cases/conformance/types/typeRelationships/subtypesAndSuperTypes/subtypingW
Property '1' is missing in type 'B2'.
tests/cases/conformance/types/typeRelationships/subtypesAndSuperTypes/subtypingWithObjectMembers5.ts(32,11): error TS2420: Class 'B3' incorrectly implements interface 'A3'.
Property ''1'' is missing in type 'B3'.
+tests/cases/conformance/types/typeRelationships/subtypesAndSuperTypes/subtypingWithObjectMembers5.ts(43,11): error TS2420: Class 'B' incorrectly implements interface 'A'.
+ Weak type 'A' has no properties in common with 'B'.
+tests/cases/conformance/types/typeRelationships/subtypesAndSuperTypes/subtypingWithObjectMembers5.ts(51,11): error TS2420: Class 'B2' incorrectly implements interface 'A2'.
+ Weak type 'A2' has no properties in common with 'B2'.
+tests/cases/conformance/types/typeRelationships/subtypesAndSuperTypes/subtypingWithObjectMembers5.ts(59,11): error TS2420: Class 'B3' incorrectly implements interface 'A3'.
+ Weak type 'A3' has no properties in common with 'B3'.
-==== tests/cases/conformance/types/typeRelationships/subtypesAndSuperTypes/subtypingWithObjectMembers5.ts (3 errors) ====
+==== tests/cases/conformance/types/typeRelationships/subtypesAndSuperTypes/subtypingWithObjectMembers5.ts (6 errors) ====
interface Base {
foo: string;
}
@@ -59,6 +65,9 @@ tests/cases/conformance/types/typeRelationships/subtypesAndSuperTypes/subtypingW
}
class B implements A {
+ ~
+!!! error TS2420: Class 'B' incorrectly implements interface 'A'.
+!!! error TS2420: Weak type 'A' has no properties in common with 'B'.
fooo: Derived; // ok
}
@@ -67,6 +76,9 @@ tests/cases/conformance/types/typeRelationships/subtypesAndSuperTypes/subtypingW
}
class B2 implements A2 {
+ ~~
+!!! error TS2420: Class 'B2' incorrectly implements interface 'A2'.
+!!! error TS2420: Weak type 'A2' has no properties in common with 'B2'.
2: Derived; // ok
}
@@ -75,6 +87,9 @@ tests/cases/conformance/types/typeRelationships/subtypesAndSuperTypes/subtypingW
}
class B3 implements A3 {
+ ~~
+!!! error TS2420: Class 'B3' incorrectly implements interface 'A3'.
+!!! error TS2420: Weak type 'A3' has no properties in common with 'B3'.
'1.0': Derived; // ok
}
}
\ No newline at end of file
diff --git a/tests/baselines/reference/underscoreTest1.errors.txt b/tests/baselines/reference/underscoreTest1.errors.txt
new file mode 100644
index 0000000000000..cdb7991a04e62
--- /dev/null
+++ b/tests/baselines/reference/underscoreTest1.errors.txt
@@ -0,0 +1,907 @@
+tests/cases/compiler/underscoreTest1_underscoreTests.ts(252,66): error TS2345: Argument of type '{ variable: string; }' is not assignable to parameter of type 'TemplateSettings'.
+ Weak type 'TemplateSettings' has no properties in common with '{ variable: string; }'.
+
+
+==== tests/cases/compiler/underscoreTest1_underscoreTests.ts (1 errors) ====
+ ///
+
+ declare var $;
+ declare function alert(x: string): void;
+
+ _.each([1, 2, 3], (num) => alert(num.toString()));
+ _.each({ one: 1, two: 2, three: 3 }, (value: number, key?: string) => alert(value.toString()));
+
+ _.map([1, 2, 3], (num) => num * 3);
+ _.map({ one: 1, two: 2, three: 3 }, (value: number, key?: string) => value * 3);
+
+ var sum = _.reduce([1, 2, 3], (memo, num) => memo + num, 0);
+
+ var list = [[0, 1], [2, 3], [4, 5]];
+ var flat = _.reduceRight(list, (a, b) => a.concat(b), []);
+
+ var even = _.find([1, 2, 3, 4, 5, 6], (num) => num % 2 == 0);
+
+ var evens = _.filter([1, 2, 3, 4, 5, 6], (num) => num % 2 == 0);
+
+ var listOfPlays = [{ title: "Cymbeline", author: "Shakespeare", year: 1611 }, { title: "The Tempest", author: "Shakespeare", year: 1611 }, { title: "Other", author: "Not Shakespeare", year: 2012 }];
+ _.where(listOfPlays, { author: "Shakespeare", year: 1611 });
+
+ var odds = _.reject([1, 2, 3, 4, 5, 6], (num) => num % 2 == 0);
+
+ _.all([true, 1, null, 'yes'], _.identity);
+
+ _.any([null, 0, 'yes', false]);
+
+ _.contains([1, 2, 3], 3);
+
+ _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
+
+ var stooges = [{ name: 'moe', age: 40 }, { name: 'larry', age: 50 }, { name: 'curly', age: 60 }];
+ _.pluck(stooges, 'name');
+
+ _.max(stooges, (stooge) => stooge.age);
+
+ var numbers = [10, 5, 100, 2, 1000];
+ _.min(numbers);
+
+ _.sortBy([1, 2, 3, 4, 5, 6], (num) => Math.sin(num));
+
+
+ // not sure how this is typechecking at all.. Math.floor(e) is number not string..?
+ _([1.3, 2.1, 2.4]).groupBy((e: number, i?: number, list?: number[]) => Math.floor(e));
+ _.groupBy([1.3, 2.1, 2.4], (num: number) => Math.floor(num));
+ _.groupBy(['one', 'two', 'three'], 'length');
+
+ _.countBy([1, 2, 3, 4, 5], (num) => num % 2 == 0 ? 'even' : 'odd');
+
+ _.shuffle([1, 2, 3, 4, 5, 6]);
+
+ // (function(){ return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
+
+ _.size({ one: 1, two: 2, three: 3 });
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ _.first([5, 4, 3, 2, 1]);
+ _.initial([5, 4, 3, 2, 1]);
+ _.last([5, 4, 3, 2, 1]);
+ _.rest([5, 4, 3, 2, 1]);
+ _.compact([0, 1, false, 2, '', 3]);
+
+ _.flatten([1, 2, 3, 4]);
+ _.flatten([1, [2]]);
+
+ // typescript doesn't like the elements being different
+ _.flatten([1, [2], [3, [[4]]]]);
+ _.flatten([1, [2], [3, [[4]]]], true);
+ _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
+ _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
+ _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
+ _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
+ _.uniq([1, 2, 1, 3, 1, 4]);
+ _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]);
+ _.object(['moe', 'larry', 'curly'], [30, 40, 50]);
+ _.object([['moe', 30], ['larry', 40], ['curly', 50]]);
+ _.indexOf([1, 2, 3], 2);
+ _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
+ _.sortedIndex([10, 20, 30, 40, 50], 35);
+ _.range(10);
+ _.range(1, 11);
+ _.range(0, 30, 5);
+ _.range(0, 30, 5);
+ _.range(0);
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ var func = function (greeting) { return greeting + ': ' + this.name };
+ // need a second var otherwise typescript thinks func signature is the above func type,
+ // instead of the newly returned _bind => func type.
+ var func2 = _.bind(func, { name: 'moe' }, 'hi');
+ func2();
+
+ var buttonView = {
+ label: 'underscore',
+ onClick: function () { alert('clicked: ' + this.label); },
+ onHover: function () { alert('hovering: ' + this.label); }
+ };
+ _.bindAll(buttonView);
+ $('#underscore_button').bind('click', buttonView.onClick);
+
+ var fibonacci = _.memoize(function (n) {
+ return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
+ });
+
+ var log = _.bind((message?: string, ...rest: string[]) => { }, Date);
+ _.delay(log, 1000, 'logged later');
+
+ _.defer(function () { alert('deferred'); });
+
+ var updatePosition = () => alert('updating position...');
+ var throttled = _.throttle(updatePosition, 100);
+ $(null).scroll(throttled);
+
+ var calculateLayout = () => alert('calculating layout...');
+ var lazyLayout = _.debounce(calculateLayout, 300);
+ $(null).resize(lazyLayout);
+
+ var createApplication = () => alert('creating application...');
+ var initialize = _.once(createApplication);
+ initialize();
+ initialize();
+
+ var notes: any[];
+ var render = () => alert("rendering...");
+ var renderNotes = _.after(notes.length, render);
+ _.each(notes, (note) => note.asyncSave({ success: renderNotes }));
+
+ var hello = function (name) { return "hello: " + name; };
+ hello = _.wrap(hello, (func, arg) => { return "before, " + func(arg) + ", after"; });
+ hello("moe");
+
+ var greet = function (name) { return "hi: " + name; };
+ var exclaim = function (statement) { return statement + "!"; };
+ var welcome = _.compose(exclaim, greet);
+ welcome('moe');
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ _.keys({ one: 1, two: 2, three: 3 });
+ _.values({ one: 1, two: 2, three: 3 });
+ _.pairs({ one: 1, two: 2, three: 3 });
+ _.invert({ Moe: "Moses", Larry: "Louis", Curly: "Jerome" });
+ _.functions(_);
+ _.extend({ name: 'moe' }, { age: 50 });
+ _.pick({ name: 'moe', age: 50, userid: 'moe1' }, 'name', 'age');
+ _.omit({ name: 'moe', age: 50, userid: 'moe1' }, 'userid');
+
+ var iceCream = { flavor: "chocolate" };
+ _.defaults(iceCream, { flavor: "vanilla", sprinkles: "lots" });
+
+ _.clone({ name: 'moe' });
+
+ _.chain([1, 2, 3, 200])
+ .filter(function (num) { return num % 2 == 0; })
+ .tap(alert)
+ .map(function (num) { return num * num })
+ .value();
+
+ _.has({ a: 1, b: 2, c: 3 }, "b");
+
+ var moe = { name: 'moe', luckyNumbers: [13, 27, 34] };
+ var clone = { name: 'moe', luckyNumbers: [13, 27, 34] };
+ moe == clone;
+ _.isEqual(moe, clone);
+
+ _.isEmpty([1, 2, 3]);
+ _.isEmpty({});
+
+ _.isElement($('body')[0]);
+
+ (function () { return _.isArray(arguments); })();
+ _.isArray([1, 2, 3]);
+
+ _.isObject({});
+ _.isObject(1);
+
+
+ // (() => { return _.isArguments(arguments); })(1, 2, 3);
+ _.isArguments([1, 2, 3]);
+
+ _.isFunction(alert);
+
+ _.isString("moe");
+
+ _.isNumber(8.4 * 5);
+
+ _.isFinite(-101);
+
+ _.isFinite(-Infinity);
+
+ _.isBoolean(null);
+
+ _.isDate(new Date());
+
+ _.isRegExp(/moe/);
+
+ _.isNaN(NaN);
+ isNaN(undefined);
+ _.isNaN(undefined);
+
+ _.isNull(null);
+ _.isNull(undefined);
+
+ _.isUndefined((null).missingVariable);
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ var underscore = _.noConflict();
+
+ var moe2 = { name: 'moe' };
+ moe2 === _.identity(moe);
+
+ var genie;
+
+ _.times(3, function (n) { genie.grantWishNumber(n); });
+
+ _.random(0, 100);
+
+ _.mixin({
+ capitalize: function (string) {
+ return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
+ }
+ });
+ (_("fabio")).capitalize();
+
+ _.uniqueId('contact_');
+
+ _.escape('Curly, Larry & Moe');
+
+ var object = { cheese: 'crumpets', stuff: function () { return 'nonsense'; } };
+ _.result(object, 'cheese');
+
+ _.result(object, 'stuff');
+
+ var compiled = _.template("hello: <%= name %>");
+ compiled({ name: 'moe' });
+ var list2 = "<% _.each(people, function(name) { %> <%= name %> <% }); %>";
+ _.template(list2, { people: ['moe', 'curly', 'larry'] });
+ var template = _.template("<%- value %>");
+ template({ value: '