diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f76f1b318e935..8564e9e360e28 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12168,10 +12168,6 @@ namespace ts { return result; } - function getConstraintForRelation(type: Type) { - return relation === definitelyAssignableRelation ? undefined : getConstraintOfType(type); - } - function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary { const flags = source.flags & target.flags; if (relation === identityRelation && !(flags & TypeFlags.Object)) { @@ -12304,23 +12300,25 @@ namespace ts { return result; } } - const constraint = getConstraintForRelation(source); - if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.AnyOrUnknown)) { - // A type variable with no constraint is not related to the non-primitive object type. - if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) { - errorInfo = saveErrorInfo; - return result; + if (relation !== definitelyAssignableRelation) { + const constraint = getConstraintOfType(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))) { + errorInfo = 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, isIntersectionConstituent)) { + // 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, isIntersectionConstituent)) { + errorInfo = 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, isIntersectionConstituent)) { errorInfo = 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, isIntersectionConstituent)) { - errorInfo = saveErrorInfo; - return result; + } } } else if (source.flags & TypeFlags.Index) { diff --git a/tests/baselines/reference/unknownType1.errors.txt b/tests/baselines/reference/unknownType1.errors.txt index 83849184dcea5..57fb611406943 100644 --- a/tests/baselines/reference/unknownType1.errors.txt +++ b/tests/baselines/reference/unknownType1.errors.txt @@ -17,13 +17,18 @@ tests/cases/conformance/types/unknown/unknownType1.ts(113,9): error TS2322: Type tests/cases/conformance/types/unknown/unknownType1.ts(114,9): error TS2322: Type 'unknown' is not assignable to type '{} | null | undefined'. Type 'unknown' is not assignable to type '{}'. tests/cases/conformance/types/unknown/unknownType1.ts(120,9): error TS2322: Type 'T' is not assignable to type 'object'. + Type 'unknown' is not assignable to type 'object'. tests/cases/conformance/types/unknown/unknownType1.ts(129,5): error TS2322: Type '123' is not assignable to type '{ [x: string]: unknown; }'. tests/cases/conformance/types/unknown/unknownType1.ts(149,17): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value. tests/cases/conformance/types/unknown/unknownType1.ts(155,14): error TS2700: Rest types may only be created from object types. tests/cases/conformance/types/unknown/unknownType1.ts(161,5): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor. +tests/cases/conformance/types/unknown/unknownType1.ts(170,9): error TS2322: Type 'U' is not assignable to type '{}'. + Type 'unknown' is not assignable to type '{}'. +tests/cases/conformance/types/unknown/unknownType1.ts(180,5): error TS2322: Type 'T' is not assignable to type '{}'. + Type 'unknown' is not assignable to type '{}'. -==== tests/cases/conformance/types/unknown/unknownType1.ts (22 errors) ==== +==== tests/cases/conformance/types/unknown/unknownType1.ts (24 errors) ==== // In an intersection everything absorbs unknown type T00 = unknown & null; // null @@ -181,6 +186,7 @@ tests/cases/conformance/types/unknown/unknownType1.ts(161,5): error TS2564: Prop let y: object = x; // Error ~ !!! error TS2322: Type 'T' is not assignable to type 'object'. +!!! error TS2322: Type 'unknown' is not assignable to type 'object'. } // Anything but primitive assignable to { [x: string]: unknown } @@ -233,4 +239,27 @@ tests/cases/conformance/types/unknown/unknownType1.ts(161,5): error TS2564: Prop b: unknown; c: any; } + + // Type parameter with explicit 'unknown' constraint not assignable to '{}' + + function f30(t: T, u: U) { + let x: {} = t; + let y: {} = u; + ~ +!!! error TS2322: Type 'U' is not assignable to type '{}'. +!!! error TS2322: Type 'unknown' is not assignable to type '{}'. + } + + // Repro from #26796 + + type Test1 = [unknown] extends [{}] ? true : false; // false + type IsDefinitelyDefined = [T] extends [{}] ? true : false; + type Test2 = IsDefinitelyDefined; // false + + function oops(arg: T): {} { + return arg; // Error + ~~~~~~~~~~~ +!!! error TS2322: Type 'T' is not assignable to type '{}'. +!!! error TS2322: Type 'unknown' is not assignable to type '{}'. + } \ No newline at end of file diff --git a/tests/baselines/reference/unknownType1.js b/tests/baselines/reference/unknownType1.js index 81a787b582728..9dd1568861f08 100644 --- a/tests/baselines/reference/unknownType1.js +++ b/tests/baselines/reference/unknownType1.js @@ -163,6 +163,23 @@ class C1 { b: unknown; c: any; } + +// Type parameter with explicit 'unknown' constraint not assignable to '{}' + +function f30(t: T, u: U) { + let x: {} = t; + let y: {} = u; +} + +// Repro from #26796 + +type Test1 = [unknown] extends [{}] ? true : false; // false +type IsDefinitelyDefined = [T] extends [{}] ? true : false; +type Test2 = IsDefinitelyDefined; // false + +function oops(arg: T): {} { + return arg; // Error +} //// [unknownType1.js] @@ -276,3 +293,11 @@ var C1 = /** @class */ (function () { } return C1; }()); +// Type parameter with explicit 'unknown' constraint not assignable to '{}' +function f30(t, u) { + var x = t; + var y = u; +} +function oops(arg) { + return arg; // Error +} diff --git a/tests/baselines/reference/unknownType1.symbols b/tests/baselines/reference/unknownType1.symbols index bf4088c6e3841..a91664fe0293c 100644 --- a/tests/baselines/reference/unknownType1.symbols +++ b/tests/baselines/reference/unknownType1.symbols @@ -407,3 +407,47 @@ class C1 { >c : Symbol(C1.c, Decl(unknownType1.ts, 161, 15)) } +// Type parameter with explicit 'unknown' constraint not assignable to '{}' + +function f30(t: T, u: U) { +>f30 : Symbol(f30, Decl(unknownType1.ts, 163, 1)) +>T : Symbol(T, Decl(unknownType1.ts, 167, 13)) +>U : Symbol(U, Decl(unknownType1.ts, 167, 15)) +>t : Symbol(t, Decl(unknownType1.ts, 167, 35)) +>T : Symbol(T, Decl(unknownType1.ts, 167, 13)) +>u : Symbol(u, Decl(unknownType1.ts, 167, 40)) +>U : Symbol(U, Decl(unknownType1.ts, 167, 15)) + + let x: {} = t; +>x : Symbol(x, Decl(unknownType1.ts, 168, 7)) +>t : Symbol(t, Decl(unknownType1.ts, 167, 35)) + + let y: {} = u; +>y : Symbol(y, Decl(unknownType1.ts, 169, 7)) +>u : Symbol(u, Decl(unknownType1.ts, 167, 40)) +} + +// Repro from #26796 + +type Test1 = [unknown] extends [{}] ? true : false; // false +>Test1 : Symbol(Test1, Decl(unknownType1.ts, 170, 1)) + +type IsDefinitelyDefined = [T] extends [{}] ? true : false; +>IsDefinitelyDefined : Symbol(IsDefinitelyDefined, Decl(unknownType1.ts, 174, 51)) +>T : Symbol(T, Decl(unknownType1.ts, 175, 25)) +>T : Symbol(T, Decl(unknownType1.ts, 175, 25)) + +type Test2 = IsDefinitelyDefined; // false +>Test2 : Symbol(Test2, Decl(unknownType1.ts, 175, 78)) +>IsDefinitelyDefined : Symbol(IsDefinitelyDefined, Decl(unknownType1.ts, 174, 51)) + +function oops(arg: T): {} { +>oops : Symbol(oops, Decl(unknownType1.ts, 176, 42)) +>T : Symbol(T, Decl(unknownType1.ts, 178, 14)) +>arg : Symbol(arg, Decl(unknownType1.ts, 178, 33)) +>T : Symbol(T, Decl(unknownType1.ts, 178, 14)) + + return arg; // Error +>arg : Symbol(arg, Decl(unknownType1.ts, 178, 33)) +} + diff --git a/tests/baselines/reference/unknownType1.types b/tests/baselines/reference/unknownType1.types index bf177d4fde863..bd3408a914df8 100644 --- a/tests/baselines/reference/unknownType1.types +++ b/tests/baselines/reference/unknownType1.types @@ -453,3 +453,42 @@ class C1 { >c : any } +// Type parameter with explicit 'unknown' constraint not assignable to '{}' + +function f30(t: T, u: U) { +>f30 : (t: T, u: U) => void +>t : T +>u : U + + let x: {} = t; +>x : {} +>t : T + + let y: {} = u; +>y : {} +>u : U +} + +// Repro from #26796 + +type Test1 = [unknown] extends [{}] ? true : false; // false +>Test1 : false +>true : true +>false : false + +type IsDefinitelyDefined = [T] extends [{}] ? true : false; +>IsDefinitelyDefined : IsDefinitelyDefined +>true : true +>false : false + +type Test2 = IsDefinitelyDefined; // false +>Test2 : false + +function oops(arg: T): {} { +>oops : (arg: T) => {} +>arg : T + + return arg; // Error +>arg : T +} + diff --git a/tests/cases/conformance/types/unknown/unknownType1.ts b/tests/cases/conformance/types/unknown/unknownType1.ts index 3307f5c44810f..d1f5ce1126160 100644 --- a/tests/cases/conformance/types/unknown/unknownType1.ts +++ b/tests/cases/conformance/types/unknown/unknownType1.ts @@ -164,3 +164,20 @@ class C1 { b: unknown; c: any; } + +// Type parameter with explicit 'unknown' constraint not assignable to '{}' + +function f30(t: T, u: U) { + let x: {} = t; + let y: {} = u; +} + +// Repro from #26796 + +type Test1 = [unknown] extends [{}] ? true : false; // false +type IsDefinitelyDefined = [T] extends [{}] ? true : false; +type Test2 = IsDefinitelyDefined; // false + +function oops(arg: T): {} { + return arg; // Error +}