mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
Improve logic that chooses co- vs. contra-variant inferences (#57909)
This commit is contained in:
parent
ed81ca6c0a
commit
a71841c77b
@ -26811,15 +26811,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;
|
||||
}
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
//// [tests/cases/compiler/coAndContraVariantInferences7.ts] ////
|
||||
|
||||
=== coAndContraVariantInferences7.ts ===
|
||||
type Request<TSchema extends Schema> = {
|
||||
>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<TSchema extends Schema>(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<TSchema>) => 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 {};
|
||||
|
||||
107
tests/baselines/reference/coAndContraVariantInferences7.types
Normal file
107
tests/baselines/reference/coAndContraVariantInferences7.types
Normal file
@ -0,0 +1,107 @@
|
||||
//// [tests/cases/compiler/coAndContraVariantInferences7.ts] ////
|
||||
|
||||
=== coAndContraVariantInferences7.ts ===
|
||||
type Request<TSchema extends Schema> = {
|
||||
>Request : Request<TSchema>
|
||||
> : ^^^^^^^^^^^^^^^^
|
||||
|
||||
query: TSchema["query"];
|
||||
>query : TSchema["query"]
|
||||
> : ^^^^^^^^^^^^^^^^
|
||||
|
||||
};
|
||||
|
||||
type Schema = { query?: unknown; body?: unknown };
|
||||
>Schema : Schema
|
||||
> : ^^^^^^
|
||||
>query : unknown
|
||||
> : ^^^^^^^
|
||||
>body : unknown
|
||||
> : ^^^^^^^
|
||||
|
||||
declare function route<TSchema extends Schema>(obj: {
|
||||
>route : <TSchema extends Schema>(obj: { pre: (a: TSchema) => void; schema: TSchema; handle: (req: Request<TSchema>) => void; }) => void
|
||||
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
|
||||
>obj : { pre: (a: TSchema) => void; schema: TSchema; handle: (req: Request<TSchema>) => void; }
|
||||
> : ^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^
|
||||
|
||||
pre: (a: TSchema) => void;
|
||||
>pre : (a: TSchema) => void
|
||||
> : ^ ^^ ^^^^^
|
||||
>a : TSchema
|
||||
> : ^^^^^^^
|
||||
|
||||
schema: TSchema;
|
||||
>schema : TSchema
|
||||
> : ^^^^^^^
|
||||
|
||||
handle: (req: Request<TSchema>) => void;
|
||||
>handle : (req: Request<TSchema>) => void
|
||||
> : ^ ^^ ^^^^^
|
||||
>req : Request<TSchema>
|
||||
> : ^^^^^^^^^^^^^^^^
|
||||
|
||||
}): 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 : <TSchema extends Schema>(obj: { pre: (a: TSchema) => void; schema: TSchema; handle: (req: Request<TSchema>) => 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 {};
|
||||
|
||||
@ -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 {};
|
||||
|
||||
@ -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 : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
|
||||
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^
|
||||
>fn : (() => void) | ((a: number) => void)
|
||||
> : ^^^^^^^ ^^^^^^ ^^ ^^^^^ ^
|
||||
>call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
|
||||
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^
|
||||
>x : number
|
||||
> : ^^^^^^
|
||||
|
||||
fn.call(null, y);
|
||||
>fn.call(null, y) : void
|
||||
> : ^^^^
|
||||
>fn.call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
|
||||
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^
|
||||
>fn : (() => void) | ((a: number) => void)
|
||||
> : ^^^^^^^ ^^^^^^ ^^ ^^^^^ ^
|
||||
>call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
|
||||
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^
|
||||
>y : any
|
||||
|
||||
export {};
|
||||
|
||||
28
tests/cases/compiler/coAndContraVariantInferences7.ts
Normal file
28
tests/cases/compiler/coAndContraVariantInferences7.ts
Normal file
@ -0,0 +1,28 @@
|
||||
// @strict: true
|
||||
// @noEmit: true
|
||||
|
||||
type Request<TSchema extends Schema> = {
|
||||
query: TSchema["query"];
|
||||
};
|
||||
|
||||
type Schema = { query?: unknown; body?: unknown };
|
||||
|
||||
declare function route<TSchema extends Schema>(obj: {
|
||||
pre: (a: TSchema) => void;
|
||||
schema: TSchema;
|
||||
handle: (req: Request<TSchema>) => void;
|
||||
}): void;
|
||||
|
||||
const validate = (_: { query?: unknown; body?: unknown }) => {};
|
||||
|
||||
route({
|
||||
pre: validate,
|
||||
schema: {
|
||||
query: "",
|
||||
},
|
||||
handle: (req) => {
|
||||
const test: string = req.query;
|
||||
},
|
||||
});
|
||||
|
||||
export {};
|
||||
14
tests/cases/compiler/coAndContraVariantInferences8.ts
Normal file
14
tests/cases/compiler/coAndContraVariantInferences8.ts
Normal file
@ -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 {};
|
||||
Loading…
x
Reference in New Issue
Block a user