diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 10400a3853a12..2b910e6abb84d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8938,11 +8938,21 @@ namespace ts { !(target.flags & TypeFlags.Union) && !isIntersectionConstituent && source !== globalObjectType && - getPropertiesOfType(source).length > 0 && + (getPropertiesOfType(source).length > 0 || + getSignaturesOfType(source, SignatureKind.Call).length > 0 || + getSignaturesOfType(source, SignatureKind.Construct).length > 0) && isWeakType(target) && !hasCommonProperties(source, target)) { if (reportErrors) { - reportError(Diagnostics.Type_0_has_no_properties_in_common_with_type_1, typeToString(source), typeToString(target)); + const calls = getSignaturesOfType(source, SignatureKind.Call); + const constructs = getSignaturesOfType(source, SignatureKind.Construct); + if (calls.length > 0 && isRelatedTo(getReturnTypeOfSignature(calls[0]), target, /*reportErrors*/ false) || + constructs.length > 0 && isRelatedTo(getReturnTypeOfSignature(constructs[0]), target, /*reportErrors*/ false)) { + reportError(Diagnostics.Value_of_type_0_has_no_properties_in_common_with_type_1_Did_you_mean_to_call_it, typeToString(source), typeToString(target)); + } + else { + reportError(Diagnostics.Type_0_has_no_properties_in_common_with_type_1, typeToString(source), typeToString(target)); + } } return Ternary.False; } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 3b4b0ddf66711..03164c54ffd48 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1908,6 +1908,10 @@ "category": "Error", "code": 2559 }, + "Value of type '{0}' has no properties in common with type '{1}'. Did you mean to call it?": { + "category": "Error", + "code": 2560 + }, "JSX element attributes type '{0}' may not be a union type.": { "category": "Error", "code": 2600 diff --git a/tests/baselines/reference/weakType.errors.txt b/tests/baselines/reference/weakType.errors.txt index 441ef70ac1674..ffc1d23759358 100644 --- a/tests/baselines/reference/weakType.errors.txt +++ b/tests/baselines/reference/weakType.errors.txt @@ -1,14 +1,17 @@ -tests/cases/compiler/weakType.ts(16,13): error TS2559: Type '12' has no properties in common with type 'Settings'. -tests/cases/compiler/weakType.ts(17,13): error TS2559: Type '"completely wrong"' has no properties in common with type 'Settings'. -tests/cases/compiler/weakType.ts(18,13): error TS2559: Type 'false' has no properties in common with type 'Settings'. -tests/cases/compiler/weakType.ts(35,18): error TS2559: Type '{ error?: number; }' has no properties in common with type 'ChangeOptions'. -tests/cases/compiler/weakType.ts(60,5): error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak & Spoiler'. +tests/cases/compiler/weakType.ts(15,13): error TS2560: Value of type '() => { timeout: number; }' has no properties in common with type 'Settings'. Did you mean to call it? +tests/cases/compiler/weakType.ts(16,13): error TS2560: Value of type '() => { timeout: number; }' has no properties in common with type 'Settings'. Did you mean to call it? +tests/cases/compiler/weakType.ts(17,13): error TS2560: Value of type 'CtorOnly' has no properties in common with type 'Settings'. Did you mean to call it? +tests/cases/compiler/weakType.ts(18,13): error TS2559: Type '12' has no properties in common with type 'Settings'. +tests/cases/compiler/weakType.ts(19,13): error TS2559: Type '"completely wrong"' has no properties in common with type 'Settings'. +tests/cases/compiler/weakType.ts(20,13): error TS2559: Type 'false' has no properties in common with type 'Settings'. +tests/cases/compiler/weakType.ts(37,18): error TS2559: Type '{ error?: number; }' has no properties in common with type 'ChangeOptions'. +tests/cases/compiler/weakType.ts(62,5): error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak & Spoiler'. Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak'. Types of property 'properties' are incompatible. Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'. -==== tests/cases/compiler/weakType.ts (5 errors) ==== +==== tests/cases/compiler/weakType.ts (8 errors) ==== interface Settings { timeout?: number; onError?(): void; @@ -17,13 +20,21 @@ tests/cases/compiler/weakType.ts(60,5): error TS2322: Type '{ properties: { wron function getDefaultSettings() { return { timeout: 1000 }; } + interface CtorOnly { + new(s: string): { timeout: 1000 } + } function doSomething(settings: Settings) { /* ... */ } // forgot to call `getDefaultSettings` - // but it is not caught because we don't check for call signatures doSomething(getDefaultSettings); - // same for arrow expressions: - doSomething(() => { }); + ~~~~~~~~~~~~~~~~~~ +!!! error TS2560: Value of type '() => { timeout: number; }' has no properties in common with type 'Settings'. Did you mean to call it? + doSomething(() => ({ timeout: 1000 })); + ~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2560: Value of type '() => { timeout: number; }' has no properties in common with type 'Settings'. Did you mean to call it? + doSomething(null as CtorOnly); + ~~~~~~~~~~~~~~~~ +!!! error TS2560: Value of type 'CtorOnly' has no properties in common with type 'Settings'. Did you mean to call it? doSomething(12); ~~ !!! error TS2559: Type '12' has no properties in common with type 'Settings'. @@ -82,4 +93,5 @@ tests/cases/compiler/weakType.ts(60,5): error TS2322: Type '{ properties: { wron !!! error TS2322: Type '{ properties: { wrong: string; }; }' is not assignable to type 'Weak'. !!! error TS2322: Types of property 'properties' are incompatible. !!! error TS2322: Type '{ wrong: string; }' has no properties in common with type '{ b?: number; }'. + \ No newline at end of file diff --git a/tests/baselines/reference/weakType.js b/tests/baselines/reference/weakType.js index 5637271ccec3a..2a1dc4ca0e40d 100644 --- a/tests/baselines/reference/weakType.js +++ b/tests/baselines/reference/weakType.js @@ -7,13 +7,15 @@ interface Settings { function getDefaultSettings() { return { timeout: 1000 }; } +interface CtorOnly { + new(s: string): { timeout: 1000 } +} function doSomething(settings: Settings) { /* ... */ } // forgot to call `getDefaultSettings` -// but it is not caught because we don't check for call signatures doSomething(getDefaultSettings); -// same for arrow expressions: -doSomething(() => { }); +doSomething(() => ({ timeout: 1000 })); +doSomething(null as CtorOnly); doSomething(12); doSomething('completely wrong'); doSomething(false); @@ -59,6 +61,7 @@ declare let unknown: { } } let weak: Weak & Spoiler = unknown + //// [weakType.js] @@ -67,10 +70,9 @@ function getDefaultSettings() { } function doSomething(settings) { } // forgot to call `getDefaultSettings` -// but it is not caught because we don't check for call signatures doSomething(getDefaultSettings); -// same for arrow expressions: -doSomething(function () { }); +doSomething(function () { return ({ timeout: 1000 }); }); +doSomething(null); doSomething(12); doSomething('completely wrong'); doSomething(false); diff --git a/tests/cases/compiler/weakType.ts b/tests/cases/compiler/weakType.ts index ffe51205e53c5..08c9d95e672e6 100644 --- a/tests/cases/compiler/weakType.ts +++ b/tests/cases/compiler/weakType.ts @@ -6,13 +6,15 @@ interface Settings { function getDefaultSettings() { return { timeout: 1000 }; } +interface CtorOnly { + new(s: string): { timeout: 1000 } +} function doSomething(settings: Settings) { /* ... */ } // forgot to call `getDefaultSettings` -// but it is not caught because we don't check for call signatures doSomething(getDefaultSettings); -// same for arrow expressions: -doSomething(() => { }); +doSomething(() => ({ timeout: 1000 })); +doSomething(null as CtorOnly); doSomething(12); doSomething('completely wrong'); doSomething(false); @@ -58,3 +60,4 @@ declare let unknown: { } } let weak: Weak & Spoiler = unknown +