diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3e64493503c42..775951f832462 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26809,15 +26809,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const inferredContravariantType = inference.contraCandidates ? getContravariantInference(inference) : undefined; if (inferredCovariantType || inferredContravariantType) { // If we have both co- and contra-variant inferences, we prefer the co-variant inference if it is not 'never', - // all co-variant inferences are subtypes of it (i.e. it isn't one of a conflicting set of candidates), it is - // a subtype of some contra-variant inference, and no other type parameter is constrained to this type parameter + // all co-variant inferences are assignable to it (i.e. it isn't one of a conflicting set of candidates), it is + // assignable to some contra-variant inference, and no other type parameter is constrained to this type parameter // and has inferences that would conflict. Otherwise, we prefer the contra-variant inference. + // Similarly ignore co-variant `any` inference when both are available as almost everything is assignable to it + // and it would spoil the overall inference. const preferCovariantType = inferredCovariantType && (!inferredContravariantType || - !(inferredCovariantType.flags & TypeFlags.Never) && - some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) && + !(inferredCovariantType.flags & (TypeFlags.Never | TypeFlags.Any)) && + some(inference.contraCandidates, t => isTypeAssignableTo(inferredCovariantType, t)) && every(context.inferences, other => other !== inference && getConstraintOfTypeParameter(other.typeParameter) !== inference.typeParameter || - every(other.candidates, t => isTypeSubtypeOf(t, inferredCovariantType)))); + every(other.candidates, t => isTypeAssignableTo(t, inferredCovariantType)))); inferredType = preferCovariantType ? inferredCovariantType : inferredContravariantType; fallbackType = preferCovariantType ? inferredContravariantType : inferredCovariantType; } diff --git a/tests/baselines/reference/coAndContraVariantInferences7.symbols b/tests/baselines/reference/coAndContraVariantInferences7.symbols new file mode 100644 index 0000000000000..0d25446e6ae16 --- /dev/null +++ b/tests/baselines/reference/coAndContraVariantInferences7.symbols @@ -0,0 +1,77 @@ +//// [tests/cases/compiler/coAndContraVariantInferences7.ts] //// + +=== coAndContraVariantInferences7.ts === +type Request = { +>Request : Symbol(Request, Decl(coAndContraVariantInferences7.ts, 0, 0)) +>TSchema : Symbol(TSchema, Decl(coAndContraVariantInferences7.ts, 0, 13)) +>Schema : Symbol(Schema, Decl(coAndContraVariantInferences7.ts, 2, 2)) + + query: TSchema["query"]; +>query : Symbol(query, Decl(coAndContraVariantInferences7.ts, 0, 40)) +>TSchema : Symbol(TSchema, Decl(coAndContraVariantInferences7.ts, 0, 13)) + +}; + +type Schema = { query?: unknown; body?: unknown }; +>Schema : Symbol(Schema, Decl(coAndContraVariantInferences7.ts, 2, 2)) +>query : Symbol(query, Decl(coAndContraVariantInferences7.ts, 4, 15)) +>body : Symbol(body, Decl(coAndContraVariantInferences7.ts, 4, 32)) + +declare function route(obj: { +>route : Symbol(route, Decl(coAndContraVariantInferences7.ts, 4, 50)) +>TSchema : Symbol(TSchema, Decl(coAndContraVariantInferences7.ts, 6, 23)) +>Schema : Symbol(Schema, Decl(coAndContraVariantInferences7.ts, 2, 2)) +>obj : Symbol(obj, Decl(coAndContraVariantInferences7.ts, 6, 47)) + + pre: (a: TSchema) => void; +>pre : Symbol(pre, Decl(coAndContraVariantInferences7.ts, 6, 53)) +>a : Symbol(a, Decl(coAndContraVariantInferences7.ts, 7, 8)) +>TSchema : Symbol(TSchema, Decl(coAndContraVariantInferences7.ts, 6, 23)) + + schema: TSchema; +>schema : Symbol(schema, Decl(coAndContraVariantInferences7.ts, 7, 28)) +>TSchema : Symbol(TSchema, Decl(coAndContraVariantInferences7.ts, 6, 23)) + + handle: (req: Request) => void; +>handle : Symbol(handle, Decl(coAndContraVariantInferences7.ts, 8, 18)) +>req : Symbol(req, Decl(coAndContraVariantInferences7.ts, 9, 11)) +>Request : Symbol(Request, Decl(coAndContraVariantInferences7.ts, 0, 0)) +>TSchema : Symbol(TSchema, Decl(coAndContraVariantInferences7.ts, 6, 23)) + +}): void; + +const validate = (_: { query?: unknown; body?: unknown }) => {}; +>validate : Symbol(validate, Decl(coAndContraVariantInferences7.ts, 12, 5)) +>_ : Symbol(_, Decl(coAndContraVariantInferences7.ts, 12, 18)) +>query : Symbol(query, Decl(coAndContraVariantInferences7.ts, 12, 22)) +>body : Symbol(body, Decl(coAndContraVariantInferences7.ts, 12, 39)) + +route({ +>route : Symbol(route, Decl(coAndContraVariantInferences7.ts, 4, 50)) + + pre: validate, +>pre : Symbol(pre, Decl(coAndContraVariantInferences7.ts, 14, 7)) +>validate : Symbol(validate, Decl(coAndContraVariantInferences7.ts, 12, 5)) + + schema: { +>schema : Symbol(schema, Decl(coAndContraVariantInferences7.ts, 15, 16)) + + query: "", +>query : Symbol(query, Decl(coAndContraVariantInferences7.ts, 16, 11)) + + }, + handle: (req) => { +>handle : Symbol(handle, Decl(coAndContraVariantInferences7.ts, 18, 4)) +>req : Symbol(req, Decl(coAndContraVariantInferences7.ts, 19, 11)) + + const test: string = req.query; +>test : Symbol(test, Decl(coAndContraVariantInferences7.ts, 20, 9)) +>req.query : Symbol(query, Decl(coAndContraVariantInferences7.ts, 0, 40)) +>req : Symbol(req, Decl(coAndContraVariantInferences7.ts, 19, 11)) +>query : Symbol(query, Decl(coAndContraVariantInferences7.ts, 0, 40)) + + }, +}); + +export {}; + diff --git a/tests/baselines/reference/coAndContraVariantInferences7.types b/tests/baselines/reference/coAndContraVariantInferences7.types new file mode 100644 index 0000000000000..789394b5b41d5 --- /dev/null +++ b/tests/baselines/reference/coAndContraVariantInferences7.types @@ -0,0 +1,107 @@ +//// [tests/cases/compiler/coAndContraVariantInferences7.ts] //// + +=== coAndContraVariantInferences7.ts === +type Request = { +>Request : Request +> : ^^^^^^^^^^^^^^^^ + + query: TSchema["query"]; +>query : TSchema["query"] +> : ^^^^^^^^^^^^^^^^ + +}; + +type Schema = { query?: unknown; body?: unknown }; +>Schema : Schema +> : ^^^^^^ +>query : unknown +> : ^^^^^^^ +>body : unknown +> : ^^^^^^^ + +declare function route(obj: { +>route : (obj: { pre: (a: TSchema) => void; schema: TSchema; handle: (req: Request) => void; }) => void +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>obj : { pre: (a: TSchema) => void; schema: TSchema; handle: (req: Request) => void; } +> : ^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^ + + pre: (a: TSchema) => void; +>pre : (a: TSchema) => void +> : ^ ^^ ^^^^^ +>a : TSchema +> : ^^^^^^^ + + schema: TSchema; +>schema : TSchema +> : ^^^^^^^ + + handle: (req: Request) => void; +>handle : (req: Request) => void +> : ^ ^^ ^^^^^ +>req : Request +> : ^^^^^^^^^^^^^^^^ + +}): void; + +const validate = (_: { query?: unknown; body?: unknown }) => {}; +>validate : (_: { query?: unknown; body?: unknown; }) => void +> : ^ ^^ ^^^^^^^^^ +>(_: { query?: unknown; body?: unknown }) => {} : (_: { query?: unknown; body?: unknown; }) => void +> : ^ ^^ ^^^^^^^^^ +>_ : { query?: unknown; body?: unknown; } +> : ^^^^^^^^^^ ^^^^^^^^^ ^^^ +>query : unknown +> : ^^^^^^^ +>body : unknown +> : ^^^^^^^ + +route({ +>route({ pre: validate, schema: { query: "", }, handle: (req) => { const test: string = req.query; },}) : void +> : ^^^^ +>route : (obj: { pre: (a: TSchema) => void; schema: TSchema; handle: (req: Request) => void; }) => void +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^ +>{ pre: validate, schema: { query: "", }, handle: (req) => { const test: string = req.query; },} : { pre: (_: { query?: unknown; body?: unknown; }) => void; schema: { query: string; }; handle: (req: Request<{ query: string; }>) => void; } +> : ^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + pre: validate, +>pre : (_: { query?: unknown; body?: unknown; }) => void +> : ^ ^^ ^^^^^^^^^ +>validate : (_: { query?: unknown; body?: unknown; }) => void +> : ^ ^^ ^^^^^^^^^ + + schema: { +>schema : { query: string; } +> : ^^^^^^^^^^^^^^^^^^ +>{ query: "", } : { query: string; } +> : ^^^^^^^^^^^^^^^^^^ + + query: "", +>query : string +> : ^^^^^^ +>"" : "" +> : ^^ + + }, + handle: (req) => { +>handle : (req: Request<{ query: string; }>) => void +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>(req) => { const test: string = req.query; } : (req: Request<{ query: string; }>) => void +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>req : Request<{ query: string; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + const test: string = req.query; +>test : string +> : ^^^^^^ +>req.query : string +> : ^^^^^^ +>req : Request<{ query: string; }> +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>query : string +> : ^^^^^^ + + }, +}); + +export {}; + diff --git a/tests/baselines/reference/coAndContraVariantInferences8.symbols b/tests/baselines/reference/coAndContraVariantInferences8.symbols new file mode 100644 index 0000000000000..7f5fb8b66e393 --- /dev/null +++ b/tests/baselines/reference/coAndContraVariantInferences8.symbols @@ -0,0 +1,29 @@ +//// [tests/cases/compiler/coAndContraVariantInferences8.ts] //// + +=== coAndContraVariantInferences8.ts === +// https://github.com/microsoft/TypeScript/issues/58468 + +declare const fn: (() => void) | ((a: number) => void); +>fn : Symbol(fn, Decl(coAndContraVariantInferences8.ts, 2, 13)) +>a : Symbol(a, Decl(coAndContraVariantInferences8.ts, 2, 35)) + +declare const x: number; +>x : Symbol(x, Decl(coAndContraVariantInferences8.ts, 4, 13)) + +declare const y: any; +>y : Symbol(y, Decl(coAndContraVariantInferences8.ts, 5, 13)) + +fn.call(null, x); +>fn.call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --)) +>fn : Symbol(fn, Decl(coAndContraVariantInferences8.ts, 2, 13)) +>call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(coAndContraVariantInferences8.ts, 4, 13)) + +fn.call(null, y); +>fn.call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --)) +>fn : Symbol(fn, Decl(coAndContraVariantInferences8.ts, 2, 13)) +>call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --)) +>y : Symbol(y, Decl(coAndContraVariantInferences8.ts, 5, 13)) + +export {}; + diff --git a/tests/baselines/reference/coAndContraVariantInferences8.types b/tests/baselines/reference/coAndContraVariantInferences8.types new file mode 100644 index 0000000000000..95eae412d8b2d --- /dev/null +++ b/tests/baselines/reference/coAndContraVariantInferences8.types @@ -0,0 +1,43 @@ +//// [tests/cases/compiler/coAndContraVariantInferences8.ts] //// + +=== coAndContraVariantInferences8.ts === +// https://github.com/microsoft/TypeScript/issues/58468 + +declare const fn: (() => void) | ((a: number) => void); +>fn : (() => void) | ((a: number) => void) +> : ^^^^^^^ ^^^^^^ ^^ ^^^^^ ^ +>a : number +> : ^^^^^^ + +declare const x: number; +>x : number +> : ^^^^^^ + +declare const y: any; +>y : any + +fn.call(null, x); +>fn.call(null, x) : void +> : ^^^^ +>fn.call : (this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^ +>fn : (() => void) | ((a: number) => void) +> : ^^^^^^^ ^^^^^^ ^^ ^^^^^ ^ +>call : (this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^ +>x : number +> : ^^^^^^ + +fn.call(null, y); +>fn.call(null, y) : void +> : ^^^^ +>fn.call : (this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^ +>fn : (() => void) | ((a: number) => void) +> : ^^^^^^^ ^^^^^^ ^^ ^^^^^ ^ +>call : (this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^ +>y : any + +export {}; + diff --git a/tests/cases/compiler/coAndContraVariantInferences7.ts b/tests/cases/compiler/coAndContraVariantInferences7.ts new file mode 100644 index 0000000000000..a54eb8c0eade4 --- /dev/null +++ b/tests/cases/compiler/coAndContraVariantInferences7.ts @@ -0,0 +1,28 @@ +// @strict: true +// @noEmit: true + +type Request = { + query: TSchema["query"]; +}; + +type Schema = { query?: unknown; body?: unknown }; + +declare function route(obj: { + pre: (a: TSchema) => void; + schema: TSchema; + handle: (req: Request) => void; +}): void; + +const validate = (_: { query?: unknown; body?: unknown }) => {}; + +route({ + pre: validate, + schema: { + query: "", + }, + handle: (req) => { + const test: string = req.query; + }, +}); + +export {}; diff --git a/tests/cases/compiler/coAndContraVariantInferences8.ts b/tests/cases/compiler/coAndContraVariantInferences8.ts new file mode 100644 index 0000000000000..ab13ce7c1d909 --- /dev/null +++ b/tests/cases/compiler/coAndContraVariantInferences8.ts @@ -0,0 +1,14 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/58468 + +declare const fn: (() => void) | ((a: number) => void); + +declare const x: number; +declare const y: any; + +fn.call(null, x); +fn.call(null, y); + +export {};