mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Improve logic for choosing between co- and contra-variant inferences (#46392)
* Improve logic for choosing between co- and contra-variant inferences * Add tests
This commit is contained in:
@@ -22329,12 +22329,11 @@ namespace ts {
|
||||
if (signature) {
|
||||
const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined;
|
||||
if (inference.contraCandidates) {
|
||||
const inferredContravariantType = getContravariantInference(inference);
|
||||
// If we have both co- and contra-variant inferences, we prefer the contra-variant inference
|
||||
// unless the co-variant inference is a subtype and not 'never'.
|
||||
// unless the co-variant inference is a subtype of some contra-variant inference and not 'never'.
|
||||
inferredType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) &&
|
||||
isTypeSubtypeOf(inferredCovariantType, inferredContravariantType) ?
|
||||
inferredCovariantType : inferredContravariantType;
|
||||
some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) ?
|
||||
inferredCovariantType : getContravariantInference(inference);
|
||||
}
|
||||
else if (inferredCovariantType) {
|
||||
inferredType = inferredCovariantType;
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
//// [coAndContraVariantInferences.ts]
|
||||
type A = { kind: 'a' };
|
||||
type B = { kind: 'b' };
|
||||
|
||||
declare const a: A;
|
||||
declare const b: B;
|
||||
|
||||
declare function fab(arg: A | B): void;
|
||||
|
||||
declare function foo<T>(x: { kind: T }, f: (arg: { kind: T }) => void): void;
|
||||
|
||||
foo(a, fab);
|
||||
foo(b, fab);
|
||||
|
||||
// Repro from #45603
|
||||
|
||||
interface Action<TName extends string,TPayload> {
|
||||
name: TName,
|
||||
payload: TPayload
|
||||
}
|
||||
|
||||
const actionA = { payload: 'any-string' } as Action<'ACTION_A', string>;
|
||||
const actionB = { payload: true } as Action<'ACTION_B', boolean>;
|
||||
|
||||
function call<TName extends string,TPayload>(
|
||||
action: Action<TName,TPayload>,
|
||||
fn: (action: Action<TName,TPayload>)=> any,
|
||||
) {
|
||||
fn(action);
|
||||
}
|
||||
|
||||
const printFn = (action: typeof actionA | typeof actionB)=> console.log(action);
|
||||
|
||||
call(actionA, printFn);
|
||||
call(actionB, printFn);
|
||||
|
||||
|
||||
//// [coAndContraVariantInferences.js]
|
||||
"use strict";
|
||||
foo(a, fab);
|
||||
foo(b, fab);
|
||||
var actionA = { payload: 'any-string' };
|
||||
var actionB = { payload: true };
|
||||
function call(action, fn) {
|
||||
fn(action);
|
||||
}
|
||||
var printFn = function (action) { return console.log(action); };
|
||||
call(actionA, printFn);
|
||||
call(actionB, printFn);
|
||||
|
||||
|
||||
//// [coAndContraVariantInferences.d.ts]
|
||||
declare type A = {
|
||||
kind: 'a';
|
||||
};
|
||||
declare type B = {
|
||||
kind: 'b';
|
||||
};
|
||||
declare const a: A;
|
||||
declare const b: B;
|
||||
declare function fab(arg: A | B): void;
|
||||
declare function foo<T>(x: {
|
||||
kind: T;
|
||||
}, f: (arg: {
|
||||
kind: T;
|
||||
}) => void): void;
|
||||
interface Action<TName extends string, TPayload> {
|
||||
name: TName;
|
||||
payload: TPayload;
|
||||
}
|
||||
declare const actionA: Action<"ACTION_A", string>;
|
||||
declare const actionB: Action<"ACTION_B", boolean>;
|
||||
declare function call<TName extends string, TPayload>(action: Action<TName, TPayload>, fn: (action: Action<TName, TPayload>) => any): void;
|
||||
declare const printFn: (action: typeof actionA | typeof actionB) => void;
|
||||
@@ -0,0 +1,114 @@
|
||||
=== tests/cases/compiler/coAndContraVariantInferences.ts ===
|
||||
type A = { kind: 'a' };
|
||||
>A : Symbol(A, Decl(coAndContraVariantInferences.ts, 0, 0))
|
||||
>kind : Symbol(kind, Decl(coAndContraVariantInferences.ts, 0, 10))
|
||||
|
||||
type B = { kind: 'b' };
|
||||
>B : Symbol(B, Decl(coAndContraVariantInferences.ts, 0, 23))
|
||||
>kind : Symbol(kind, Decl(coAndContraVariantInferences.ts, 1, 10))
|
||||
|
||||
declare const a: A;
|
||||
>a : Symbol(a, Decl(coAndContraVariantInferences.ts, 3, 13))
|
||||
>A : Symbol(A, Decl(coAndContraVariantInferences.ts, 0, 0))
|
||||
|
||||
declare const b: B;
|
||||
>b : Symbol(b, Decl(coAndContraVariantInferences.ts, 4, 13))
|
||||
>B : Symbol(B, Decl(coAndContraVariantInferences.ts, 0, 23))
|
||||
|
||||
declare function fab(arg: A | B): void;
|
||||
>fab : Symbol(fab, Decl(coAndContraVariantInferences.ts, 4, 19))
|
||||
>arg : Symbol(arg, Decl(coAndContraVariantInferences.ts, 6, 21))
|
||||
>A : Symbol(A, Decl(coAndContraVariantInferences.ts, 0, 0))
|
||||
>B : Symbol(B, Decl(coAndContraVariantInferences.ts, 0, 23))
|
||||
|
||||
declare function foo<T>(x: { kind: T }, f: (arg: { kind: T }) => void): void;
|
||||
>foo : Symbol(foo, Decl(coAndContraVariantInferences.ts, 6, 39))
|
||||
>T : Symbol(T, Decl(coAndContraVariantInferences.ts, 8, 21))
|
||||
>x : Symbol(x, Decl(coAndContraVariantInferences.ts, 8, 24))
|
||||
>kind : Symbol(kind, Decl(coAndContraVariantInferences.ts, 8, 28))
|
||||
>T : Symbol(T, Decl(coAndContraVariantInferences.ts, 8, 21))
|
||||
>f : Symbol(f, Decl(coAndContraVariantInferences.ts, 8, 39))
|
||||
>arg : Symbol(arg, Decl(coAndContraVariantInferences.ts, 8, 44))
|
||||
>kind : Symbol(kind, Decl(coAndContraVariantInferences.ts, 8, 50))
|
||||
>T : Symbol(T, Decl(coAndContraVariantInferences.ts, 8, 21))
|
||||
|
||||
foo(a, fab);
|
||||
>foo : Symbol(foo, Decl(coAndContraVariantInferences.ts, 6, 39))
|
||||
>a : Symbol(a, Decl(coAndContraVariantInferences.ts, 3, 13))
|
||||
>fab : Symbol(fab, Decl(coAndContraVariantInferences.ts, 4, 19))
|
||||
|
||||
foo(b, fab);
|
||||
>foo : Symbol(foo, Decl(coAndContraVariantInferences.ts, 6, 39))
|
||||
>b : Symbol(b, Decl(coAndContraVariantInferences.ts, 4, 13))
|
||||
>fab : Symbol(fab, Decl(coAndContraVariantInferences.ts, 4, 19))
|
||||
|
||||
// Repro from #45603
|
||||
|
||||
interface Action<TName extends string,TPayload> {
|
||||
>Action : Symbol(Action, Decl(coAndContraVariantInferences.ts, 11, 12))
|
||||
>TName : Symbol(TName, Decl(coAndContraVariantInferences.ts, 15, 17))
|
||||
>TPayload : Symbol(TPayload, Decl(coAndContraVariantInferences.ts, 15, 38))
|
||||
|
||||
name: TName,
|
||||
>name : Symbol(Action.name, Decl(coAndContraVariantInferences.ts, 15, 49))
|
||||
>TName : Symbol(TName, Decl(coAndContraVariantInferences.ts, 15, 17))
|
||||
|
||||
payload: TPayload
|
||||
>payload : Symbol(Action.payload, Decl(coAndContraVariantInferences.ts, 16, 16))
|
||||
>TPayload : Symbol(TPayload, Decl(coAndContraVariantInferences.ts, 15, 38))
|
||||
}
|
||||
|
||||
const actionA = { payload: 'any-string' } as Action<'ACTION_A', string>;
|
||||
>actionA : Symbol(actionA, Decl(coAndContraVariantInferences.ts, 20, 5))
|
||||
>payload : Symbol(payload, Decl(coAndContraVariantInferences.ts, 20, 17))
|
||||
>Action : Symbol(Action, Decl(coAndContraVariantInferences.ts, 11, 12))
|
||||
|
||||
const actionB = { payload: true } as Action<'ACTION_B', boolean>;
|
||||
>actionB : Symbol(actionB, Decl(coAndContraVariantInferences.ts, 21, 5))
|
||||
>payload : Symbol(payload, Decl(coAndContraVariantInferences.ts, 21, 17))
|
||||
>Action : Symbol(Action, Decl(coAndContraVariantInferences.ts, 11, 12))
|
||||
|
||||
function call<TName extends string,TPayload>(
|
||||
>call : Symbol(call, Decl(coAndContraVariantInferences.ts, 21, 65))
|
||||
>TName : Symbol(TName, Decl(coAndContraVariantInferences.ts, 23, 14))
|
||||
>TPayload : Symbol(TPayload, Decl(coAndContraVariantInferences.ts, 23, 35))
|
||||
|
||||
action: Action<TName,TPayload>,
|
||||
>action : Symbol(action, Decl(coAndContraVariantInferences.ts, 23, 45))
|
||||
>Action : Symbol(Action, Decl(coAndContraVariantInferences.ts, 11, 12))
|
||||
>TName : Symbol(TName, Decl(coAndContraVariantInferences.ts, 23, 14))
|
||||
>TPayload : Symbol(TPayload, Decl(coAndContraVariantInferences.ts, 23, 35))
|
||||
|
||||
fn: (action: Action<TName,TPayload>)=> any,
|
||||
>fn : Symbol(fn, Decl(coAndContraVariantInferences.ts, 24, 33))
|
||||
>action : Symbol(action, Decl(coAndContraVariantInferences.ts, 25, 7))
|
||||
>Action : Symbol(Action, Decl(coAndContraVariantInferences.ts, 11, 12))
|
||||
>TName : Symbol(TName, Decl(coAndContraVariantInferences.ts, 23, 14))
|
||||
>TPayload : Symbol(TPayload, Decl(coAndContraVariantInferences.ts, 23, 35))
|
||||
|
||||
) {
|
||||
fn(action);
|
||||
>fn : Symbol(fn, Decl(coAndContraVariantInferences.ts, 24, 33))
|
||||
>action : Symbol(action, Decl(coAndContraVariantInferences.ts, 23, 45))
|
||||
}
|
||||
|
||||
const printFn = (action: typeof actionA | typeof actionB)=> console.log(action);
|
||||
>printFn : Symbol(printFn, Decl(coAndContraVariantInferences.ts, 30, 5))
|
||||
>action : Symbol(action, Decl(coAndContraVariantInferences.ts, 30, 17))
|
||||
>actionA : Symbol(actionA, Decl(coAndContraVariantInferences.ts, 20, 5))
|
||||
>actionB : Symbol(actionB, Decl(coAndContraVariantInferences.ts, 21, 5))
|
||||
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
|
||||
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
|
||||
>action : Symbol(action, Decl(coAndContraVariantInferences.ts, 30, 17))
|
||||
|
||||
call(actionA, printFn);
|
||||
>call : Symbol(call, Decl(coAndContraVariantInferences.ts, 21, 65))
|
||||
>actionA : Symbol(actionA, Decl(coAndContraVariantInferences.ts, 20, 5))
|
||||
>printFn : Symbol(printFn, Decl(coAndContraVariantInferences.ts, 30, 5))
|
||||
|
||||
call(actionB, printFn);
|
||||
>call : Symbol(call, Decl(coAndContraVariantInferences.ts, 21, 65))
|
||||
>actionB : Symbol(actionB, Decl(coAndContraVariantInferences.ts, 21, 5))
|
||||
>printFn : Symbol(printFn, Decl(coAndContraVariantInferences.ts, 30, 5))
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
=== tests/cases/compiler/coAndContraVariantInferences.ts ===
|
||||
type A = { kind: 'a' };
|
||||
>A : A
|
||||
>kind : "a"
|
||||
|
||||
type B = { kind: 'b' };
|
||||
>B : B
|
||||
>kind : "b"
|
||||
|
||||
declare const a: A;
|
||||
>a : A
|
||||
|
||||
declare const b: B;
|
||||
>b : B
|
||||
|
||||
declare function fab(arg: A | B): void;
|
||||
>fab : (arg: A | B) => void
|
||||
>arg : A | B
|
||||
|
||||
declare function foo<T>(x: { kind: T }, f: (arg: { kind: T }) => void): void;
|
||||
>foo : <T>(x: { kind: T;}, f: (arg: { kind: T;}) => void) => void
|
||||
>x : { kind: T; }
|
||||
>kind : T
|
||||
>f : (arg: { kind: T;}) => void
|
||||
>arg : { kind: T; }
|
||||
>kind : T
|
||||
|
||||
foo(a, fab);
|
||||
>foo(a, fab) : void
|
||||
>foo : <T>(x: { kind: T; }, f: (arg: { kind: T; }) => void) => void
|
||||
>a : A
|
||||
>fab : (arg: A | B) => void
|
||||
|
||||
foo(b, fab);
|
||||
>foo(b, fab) : void
|
||||
>foo : <T>(x: { kind: T; }, f: (arg: { kind: T; }) => void) => void
|
||||
>b : B
|
||||
>fab : (arg: A | B) => void
|
||||
|
||||
// Repro from #45603
|
||||
|
||||
interface Action<TName extends string,TPayload> {
|
||||
name: TName,
|
||||
>name : TName
|
||||
|
||||
payload: TPayload
|
||||
>payload : TPayload
|
||||
}
|
||||
|
||||
const actionA = { payload: 'any-string' } as Action<'ACTION_A', string>;
|
||||
>actionA : Action<"ACTION_A", string>
|
||||
>{ payload: 'any-string' } as Action<'ACTION_A', string> : Action<"ACTION_A", string>
|
||||
>{ payload: 'any-string' } : { payload: string; }
|
||||
>payload : string
|
||||
>'any-string' : "any-string"
|
||||
|
||||
const actionB = { payload: true } as Action<'ACTION_B', boolean>;
|
||||
>actionB : Action<"ACTION_B", boolean>
|
||||
>{ payload: true } as Action<'ACTION_B', boolean> : Action<"ACTION_B", boolean>
|
||||
>{ payload: true } : { payload: true; }
|
||||
>payload : true
|
||||
>true : true
|
||||
|
||||
function call<TName extends string,TPayload>(
|
||||
>call : <TName extends string, TPayload>(action: Action<TName, TPayload>, fn: (action: Action<TName, TPayload>) => any) => void
|
||||
|
||||
action: Action<TName,TPayload>,
|
||||
>action : Action<TName, TPayload>
|
||||
|
||||
fn: (action: Action<TName,TPayload>)=> any,
|
||||
>fn : (action: Action<TName, TPayload>) => any
|
||||
>action : Action<TName, TPayload>
|
||||
|
||||
) {
|
||||
fn(action);
|
||||
>fn(action) : any
|
||||
>fn : (action: Action<TName, TPayload>) => any
|
||||
>action : Action<TName, TPayload>
|
||||
}
|
||||
|
||||
const printFn = (action: typeof actionA | typeof actionB)=> console.log(action);
|
||||
>printFn : (action: typeof actionA | typeof actionB) => void
|
||||
>(action: typeof actionA | typeof actionB)=> console.log(action) : (action: typeof actionA | typeof actionB) => void
|
||||
>action : Action<"ACTION_A", string> | Action<"ACTION_B", boolean>
|
||||
>actionA : Action<"ACTION_A", string>
|
||||
>actionB : Action<"ACTION_B", boolean>
|
||||
>console.log(action) : void
|
||||
>console.log : (...data: any[]) => void
|
||||
>console : Console
|
||||
>log : (...data: any[]) => void
|
||||
>action : Action<"ACTION_A", string> | Action<"ACTION_B", boolean>
|
||||
|
||||
call(actionA, printFn);
|
||||
>call(actionA, printFn) : void
|
||||
>call : <TName extends string, TPayload>(action: Action<TName, TPayload>, fn: (action: Action<TName, TPayload>) => any) => void
|
||||
>actionA : Action<"ACTION_A", string>
|
||||
>printFn : (action: Action<"ACTION_A", string> | Action<"ACTION_B", boolean>) => void
|
||||
|
||||
call(actionB, printFn);
|
||||
>call(actionB, printFn) : void
|
||||
>call : <TName extends string, TPayload>(action: Action<TName, TPayload>, fn: (action: Action<TName, TPayload>) => any) => void
|
||||
>actionB : Action<"ACTION_B", boolean>
|
||||
>printFn : (action: Action<"ACTION_A", string> | Action<"ACTION_B", boolean>) => void
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// @strict: true
|
||||
// @declaration: true
|
||||
|
||||
type A = { kind: 'a' };
|
||||
type B = { kind: 'b' };
|
||||
|
||||
declare const a: A;
|
||||
declare const b: B;
|
||||
|
||||
declare function fab(arg: A | B): void;
|
||||
|
||||
declare function foo<T>(x: { kind: T }, f: (arg: { kind: T }) => void): void;
|
||||
|
||||
foo(a, fab);
|
||||
foo(b, fab);
|
||||
|
||||
// Repro from #45603
|
||||
|
||||
interface Action<TName extends string,TPayload> {
|
||||
name: TName,
|
||||
payload: TPayload
|
||||
}
|
||||
|
||||
const actionA = { payload: 'any-string' } as Action<'ACTION_A', string>;
|
||||
const actionB = { payload: true } as Action<'ACTION_B', boolean>;
|
||||
|
||||
function call<TName extends string,TPayload>(
|
||||
action: Action<TName,TPayload>,
|
||||
fn: (action: Action<TName,TPayload>)=> any,
|
||||
) {
|
||||
fn(action);
|
||||
}
|
||||
|
||||
const printFn = (action: typeof actionA | typeof actionB)=> console.log(action);
|
||||
|
||||
call(actionA, printFn);
|
||||
call(actionB, printFn);
|
||||
Reference in New Issue
Block a user