Skip to content

Delete the {} and unconstrained type parameter assignability rule #33570

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

24 changes: 11 additions & 13 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16918,6 +16918,9 @@ namespace ts {
return result;
}
reportRelationError(headMessage, source, target);
if (source.flags & TypeFlags.TypeVariable && source.symbol?.declarations?.[0] && !getConstraintOfType(<TypeVariable>source) && isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) {
associateRelatedInfo(createDiagnosticForNode(source.symbol.declarations[0], Diagnostics.This_type_parameter_likely_needs_an_extends_object_constraint));
}
}
}
}
Expand Down Expand Up @@ -17468,22 +17471,17 @@ namespace ts {
}
else {
const constraint = getConstraintOfType(<TypeVariable>source);
if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) {
// A type variable with no constraint is not related to the non-primitive object type.
if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) {
if (constraint) {
// hi-speed no-this-instantiation check (less accurate, but avoids costly `this`-instantiation when the constraint will suffice), see #28231 for report on why this is needed
if (result = isRelatedTo(constraint, target, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) {
resetErrorInfo(saveErrorInfo);
return result;
}
// slower, fuller, this-instantiated check (necessary when comparing raw `this` types from base classes), see `subclassWithPolymorphicThisIsAssignable.ts` test for example
else if (result = isRelatedTo(getTypeWithThisArgument(constraint, source), target, reportErrors, /*headMessage*/ undefined, intersectionState)) {
resetErrorInfo(saveErrorInfo);
return result;
}
}
// hi-speed no-this-instantiation check (less accurate, but avoids costly `this`-instantiation when the constraint will suffice), see #28231 for report on why this is needed
else if (result = isRelatedTo(constraint, target, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) {
resetErrorInfo(saveErrorInfo);
return result;
}
// slower, fuller, this-instantiated check (necessary when comparing raw `this` types from base classes), see `subclassWithPolymorphicThisIsAssignable.ts` test for example
else if (result = isRelatedTo(getTypeWithThisArgument(constraint, source), target, reportErrors, /*headMessage*/ undefined, intersectionState)) {
resetErrorInfo(saveErrorInfo);
return result;
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1532,7 +1532,7 @@ namespace ts {
/**
* Tests whether a value is an array.
*/
export function isArray(value: any): value is readonly {}[] {
export function isArray(value: any): value is readonly unknown[] {
return Array.isArray ? Array.isArray(value) : value instanceof Array;
}

Expand Down Expand Up @@ -1565,7 +1565,7 @@ namespace ts {
}

/** Does nothing. */
export function noop(_?: {} | null | undefined): void { }
export function noop(_?: unknown): void { }

/** Do nothing and return false */
export function returnFalse(): false { return false; }
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3059,6 +3059,10 @@
"category": "Error",
"code": 2796
},
"This type parameter likely needs an `extends object` constraint.": {
"category": "Error",
"code": 2797
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
4 changes: 2 additions & 2 deletions src/harness/harnessGlobals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ globalThis.assert = _chai.assert;
}
assertDeepImpl(a, b, msg);

function arrayExtraKeysObject(a: readonly ({} | null | undefined)[]): object {
const obj: { [key: string]: {} | null | undefined } = {};
function arrayExtraKeysObject(a: readonly unknown[]): object {
const obj: { [key: string]: unknown } = {};
for (const key in a) {
if (Number.isNaN(Number(key))) {
obj[key] = a[key];
Expand Down
2 changes: 1 addition & 1 deletion src/services/shims.ts
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ namespace ts {
}
}

function simpleForwardCall(logger: Logger, actionDescription: string, action: () => {}, logPerformance: boolean): {} {
function simpleForwardCall(logger: Logger, actionDescription: string, action: () => unknown, logPerformance: boolean): unknown {
let start: number | undefined;
if (logPerformance) {
logger.log(actionDescription);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfConditionalExpressions.ts(25,5): error TS2322: Type 'T | U' is not assignable to type 'Object'.
Type 'T' is not assignable to type 'Object'.


==== tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfConditionalExpressions.ts (1 errors) ====
// conditional expressions return the best common type of the branches plus contextual type (using the first candidate if multiple BCTs exist)
// no errors expected here

var a: { x: number; y?: number };
var b: { x: number; z?: number };

class Base { foo: string; }
class Derived extends Base { bar: string; }
class Derived2 extends Base { baz: string; }
var base: Base;
var derived: Derived;
var derived2: Derived2;

var r = true ? 1 : 2;
var r3 = true ? 1 : {};
var r4 = true ? a : b; // typeof a
var r5 = true ? b : a; // typeof b
var r6 = true ? (x: number) => { } : (x: Object) => { }; // returns number => void
var r7: (x: Object) => void = true ? (x: number) => { } : (x: Object) => { };
var r8 = true ? (x: Object) => { } : (x: number) => { }; // returns Object => void
var r10: Base = true ? derived : derived2; // no error since we use the contextual type in BCT
var r11 = true ? base : derived2;

function foo5<T, U>(t: T, u: U): Object {
return true ? t : u; // BCT is Object
~~~~~~~~~~~~~~~~~~~~
!!! error TS2322: Type 'T | U' is not assignable to type 'Object'.
!!! error TS2322: Type 'T' is not assignable to type 'Object'.
!!! related TS2797 tests/cases/conformance/types/typeRelationships/bestCommonType/bestCommonTypeOfConditionalExpressions.ts:24:15: This type parameter likely needs an `extends object` constraint.
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,41 @@ tests/cases/conformance/expressions/binaryOperators/comparisonOperator/compariso
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(38,15): error TS2367: This condition will always return 'true' since the types 'V' and 'T' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(39,15): error TS2367: This condition will always return 'false' since the types 'V' and 'T' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(40,15): error TS2367: This condition will always return 'true' since the types 'V' and 'T' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(43,15): error TS2365: Operator '<' cannot be applied to types 'T' and '{}'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(44,15): error TS2365: Operator '>' cannot be applied to types 'T' and '{}'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(45,15): error TS2365: Operator '<=' cannot be applied to types 'T' and '{}'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(46,15): error TS2365: Operator '>=' cannot be applied to types 'T' and '{}'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(47,15): error TS2367: This condition will always return 'false' since the types 'T' and '{}' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(48,15): error TS2367: This condition will always return 'true' since the types 'T' and '{}' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(49,15): error TS2367: This condition will always return 'false' since the types 'T' and '{}' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(50,15): error TS2367: This condition will always return 'true' since the types 'T' and '{}' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(52,15): error TS2365: Operator '<' cannot be applied to types '{}' and 'T'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(53,15): error TS2365: Operator '>' cannot be applied to types '{}' and 'T'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(54,15): error TS2365: Operator '<=' cannot be applied to types '{}' and 'T'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(55,15): error TS2365: Operator '>=' cannot be applied to types '{}' and 'T'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(56,15): error TS2367: This condition will always return 'false' since the types '{}' and 'T' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(57,15): error TS2367: This condition will always return 'true' since the types '{}' and 'T' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(58,15): error TS2367: This condition will always return 'false' since the types '{}' and 'T' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(59,15): error TS2367: This condition will always return 'true' since the types '{}' and 'T' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(61,15): error TS2365: Operator '<' cannot be applied to types 'T' and 'Object'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(62,15): error TS2365: Operator '>' cannot be applied to types 'T' and 'Object'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(63,15): error TS2365: Operator '<=' cannot be applied to types 'T' and 'Object'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(64,15): error TS2365: Operator '>=' cannot be applied to types 'T' and 'Object'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(65,15): error TS2367: This condition will always return 'false' since the types 'T' and 'Object' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(66,15): error TS2367: This condition will always return 'true' since the types 'T' and 'Object' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(67,15): error TS2367: This condition will always return 'false' since the types 'T' and 'Object' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(68,15): error TS2367: This condition will always return 'true' since the types 'T' and 'Object' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(70,15): error TS2365: Operator '<' cannot be applied to types 'Object' and 'T'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(71,15): error TS2365: Operator '>' cannot be applied to types 'Object' and 'T'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(72,15): error TS2365: Operator '<=' cannot be applied to types 'Object' and 'T'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(73,15): error TS2365: Operator '>=' cannot be applied to types 'Object' and 'T'.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(74,15): error TS2367: This condition will always return 'false' since the types 'Object' and 'T' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(75,15): error TS2367: This condition will always return 'true' since the types 'Object' and 'T' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(76,15): error TS2367: This condition will always return 'false' since the types 'Object' and 'T' have no overlap.
tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts(77,15): error TS2367: This condition will always return 'true' since the types 'Object' and 'T' have no overlap.


==== tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts (32 errors) ====
==== tests/cases/conformance/expressions/binaryOperators/comparisonOperator/comparisonOperatorWithTypeParameter.ts (64 errors) ====
var a: {};
var b: Object;

Expand Down Expand Up @@ -140,38 +172,102 @@ tests/cases/conformance/expressions/binaryOperators/comparisonOperator/compariso

// ok
var re1 = t < a;
~~~~~
!!! error TS2365: Operator '<' cannot be applied to types 'T' and '{}'.
var re2 = t > a;
~~~~~
!!! error TS2365: Operator '>' cannot be applied to types 'T' and '{}'.
var re3 = t <= a;
~~~~~~
!!! error TS2365: Operator '<=' cannot be applied to types 'T' and '{}'.
var re4 = t >= a;
~~~~~~
!!! error TS2365: Operator '>=' cannot be applied to types 'T' and '{}'.
var re5 = t == a;
~~~~~~
!!! error TS2367: This condition will always return 'false' since the types 'T' and '{}' have no overlap.
var re6 = t != a;
~~~~~~
!!! error TS2367: This condition will always return 'true' since the types 'T' and '{}' have no overlap.
var re7 = t === a;
~~~~~~~
!!! error TS2367: This condition will always return 'false' since the types 'T' and '{}' have no overlap.
var re8 = t !== a;
~~~~~~~
!!! error TS2367: This condition will always return 'true' since the types 'T' and '{}' have no overlap.

var rf1 = a < t;
~~~~~
!!! error TS2365: Operator '<' cannot be applied to types '{}' and 'T'.
var rf2 = a > t;
~~~~~
!!! error TS2365: Operator '>' cannot be applied to types '{}' and 'T'.
var rf3 = a <= t;
~~~~~~
!!! error TS2365: Operator '<=' cannot be applied to types '{}' and 'T'.
var rf4 = a >= t;
~~~~~~
!!! error TS2365: Operator '>=' cannot be applied to types '{}' and 'T'.
var rf5 = a == t;
~~~~~~
!!! error TS2367: This condition will always return 'false' since the types '{}' and 'T' have no overlap.
var rf6 = a != t;
~~~~~~
!!! error TS2367: This condition will always return 'true' since the types '{}' and 'T' have no overlap.
var rf7 = a === t;
~~~~~~~
!!! error TS2367: This condition will always return 'false' since the types '{}' and 'T' have no overlap.
var rf8 = a !== t;
~~~~~~~
!!! error TS2367: This condition will always return 'true' since the types '{}' and 'T' have no overlap.

var rg1 = t < b;
~~~~~
!!! error TS2365: Operator '<' cannot be applied to types 'T' and 'Object'.
var rg2 = t > b;
~~~~~
!!! error TS2365: Operator '>' cannot be applied to types 'T' and 'Object'.
var rg3 = t <= b;
~~~~~~
!!! error TS2365: Operator '<=' cannot be applied to types 'T' and 'Object'.
var rg4 = t >= b;
~~~~~~
!!! error TS2365: Operator '>=' cannot be applied to types 'T' and 'Object'.
var rg5 = t == b;
~~~~~~
!!! error TS2367: This condition will always return 'false' since the types 'T' and 'Object' have no overlap.
var rg6 = t != b;
~~~~~~
!!! error TS2367: This condition will always return 'true' since the types 'T' and 'Object' have no overlap.
var rg7 = t === b;
~~~~~~~
!!! error TS2367: This condition will always return 'false' since the types 'T' and 'Object' have no overlap.
var rg8 = t !== b;
~~~~~~~
!!! error TS2367: This condition will always return 'true' since the types 'T' and 'Object' have no overlap.

var rh1 = b < t;
~~~~~
!!! error TS2365: Operator '<' cannot be applied to types 'Object' and 'T'.
var rh2 = b > t;
~~~~~
!!! error TS2365: Operator '>' cannot be applied to types 'Object' and 'T'.
var rh3 = b <= t;
~~~~~~
!!! error TS2365: Operator '<=' cannot be applied to types 'Object' and 'T'.
var rh4 = b >= t;
~~~~~~
!!! error TS2365: Operator '>=' cannot be applied to types 'Object' and 'T'.
var rh5 = b == t;
~~~~~~
!!! error TS2367: This condition will always return 'false' since the types 'Object' and 'T' have no overlap.
var rh6 = b != t;
~~~~~~
!!! error TS2367: This condition will always return 'true' since the types 'Object' and 'T' have no overlap.
var rh7 = b === t;
~~~~~~~
!!! error TS2367: This condition will always return 'false' since the types 'Object' and 'T' have no overlap.
var rh8 = b !== t;
~~~~~~~
!!! error TS2367: This condition will always return 'true' since the types 'Object' and 'T' have no overlap.
}
Loading