mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Add fallback when both co- and contra-variant inference candidates exist (#54072)
This commit is contained in:
parent
cbb8dfe784
commit
ae6393e5eb
@ -25157,22 +25157,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
const inference = context.inferences[index];
|
||||
if (!inference.inferredType) {
|
||||
let inferredType: Type | undefined;
|
||||
const signature = context.signature;
|
||||
if (signature) {
|
||||
const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined;
|
||||
if (inference.contraCandidates) {
|
||||
// If we have both co- and contra-variant inferences, we use the co-variant inference if it is not 'never',
|
||||
// it is a subtype of some contra-variant inference, and no other type parameter is constrained to this type
|
||||
// parameter and has inferences that would conflict. Otherwise, we use the contra-variant inference.
|
||||
const useCovariantType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) &&
|
||||
let fallbackType: Type | undefined;
|
||||
if (context.signature) {
|
||||
const inferredCovariantType = inference.candidates ? getCovariantInference(inference, context.signature) : undefined;
|
||||
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
|
||||
// and has inferences that would conflict. Otherwise, we prefer the contra-variant inference.
|
||||
const preferCovariantType = inferredCovariantType && (!inferredContravariantType ||
|
||||
!(inferredCovariantType.flags & TypeFlags.Never) &&
|
||||
some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) &&
|
||||
every(context.inferences, other =>
|
||||
other !== inference && getConstraintOfTypeParameter(other.typeParameter) !== inference.typeParameter ||
|
||||
every(other.candidates, t => isTypeSubtypeOf(t, inferredCovariantType)));
|
||||
inferredType = useCovariantType ? inferredCovariantType : getContravariantInference(inference);
|
||||
}
|
||||
else if (inferredCovariantType) {
|
||||
inferredType = inferredCovariantType;
|
||||
every(other.candidates, t => isTypeSubtypeOf(t, inferredCovariantType))));
|
||||
inferredType = preferCovariantType ? inferredCovariantType : inferredContravariantType;
|
||||
fallbackType = preferCovariantType ? inferredContravariantType : inferredCovariantType;
|
||||
}
|
||||
else if (context.flags & InferenceFlags.NoDefault) {
|
||||
// We use silentNeverType as the wildcard that signals no inferences.
|
||||
@ -25202,7 +25203,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
if (constraint) {
|
||||
const instantiatedConstraint = instantiateType(constraint, context.nonFixingMapper);
|
||||
if (!inferredType || !context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
|
||||
inference.inferredType = inferredType = instantiatedConstraint;
|
||||
// If the fallback type satisfies the constraint, we pick it. Otherwise, we pick the constraint.
|
||||
inference.inferredType = fallbackType && context.compareTypes(fallbackType, getTypeWithThisArgument(instantiatedConstraint, fallbackType)) ? fallbackType : instantiatedConstraint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
=== tests/cases/compiler/coAndContraVariantInferences5.ts ===
|
||||
type Thing = 'a' | 'b';
|
||||
>Thing : Symbol(Thing, Decl(coAndContraVariantInferences5.ts, 0, 0))
|
||||
|
||||
function f(
|
||||
>f : Symbol(f, Decl(coAndContraVariantInferences5.ts, 0, 23))
|
||||
|
||||
options: SelectOptions<Thing>,
|
||||
>options : Symbol(options, Decl(coAndContraVariantInferences5.ts, 2, 11))
|
||||
>SelectOptions : Symbol(SelectOptions, Decl(coAndContraVariantInferences5.ts, 17, 2))
|
||||
>Thing : Symbol(Thing, Decl(coAndContraVariantInferences5.ts, 0, 0))
|
||||
|
||||
onChange: (status: Thing | null) => void,
|
||||
>onChange : Symbol(onChange, Decl(coAndContraVariantInferences5.ts, 3, 34))
|
||||
>status : Symbol(status, Decl(coAndContraVariantInferences5.ts, 4, 15))
|
||||
>Thing : Symbol(Thing, Decl(coAndContraVariantInferences5.ts, 0, 0))
|
||||
|
||||
): void {
|
||||
select({
|
||||
>select : Symbol(select, Decl(coAndContraVariantInferences5.ts, 10, 1))
|
||||
|
||||
options,
|
||||
>options : Symbol(options, Decl(coAndContraVariantInferences5.ts, 6, 12))
|
||||
|
||||
onChange,
|
||||
>onChange : Symbol(onChange, Decl(coAndContraVariantInferences5.ts, 7, 16))
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
declare function select<KeyT extends string>(props: SelectProps<KeyT>): void;
|
||||
>select : Symbol(select, Decl(coAndContraVariantInferences5.ts, 10, 1))
|
||||
>KeyT : Symbol(KeyT, Decl(coAndContraVariantInferences5.ts, 12, 24))
|
||||
>props : Symbol(props, Decl(coAndContraVariantInferences5.ts, 12, 45))
|
||||
>SelectProps : Symbol(SelectProps, Decl(coAndContraVariantInferences5.ts, 12, 77))
|
||||
>KeyT : Symbol(KeyT, Decl(coAndContraVariantInferences5.ts, 12, 24))
|
||||
|
||||
type SelectProps<KeyT extends string> = {
|
||||
>SelectProps : Symbol(SelectProps, Decl(coAndContraVariantInferences5.ts, 12, 77))
|
||||
>KeyT : Symbol(KeyT, Decl(coAndContraVariantInferences5.ts, 14, 17))
|
||||
|
||||
options?: SelectOptions<KeyT>;
|
||||
>options : Symbol(options, Decl(coAndContraVariantInferences5.ts, 14, 41))
|
||||
>SelectOptions : Symbol(SelectOptions, Decl(coAndContraVariantInferences5.ts, 17, 2))
|
||||
>KeyT : Symbol(KeyT, Decl(coAndContraVariantInferences5.ts, 14, 17))
|
||||
|
||||
onChange: (key: KeyT) => void;
|
||||
>onChange : Symbol(onChange, Decl(coAndContraVariantInferences5.ts, 15, 34))
|
||||
>key : Symbol(key, Decl(coAndContraVariantInferences5.ts, 16, 15))
|
||||
>KeyT : Symbol(KeyT, Decl(coAndContraVariantInferences5.ts, 14, 17))
|
||||
|
||||
};
|
||||
|
||||
type SelectOptions<KeyT extends string> =
|
||||
>SelectOptions : Symbol(SelectOptions, Decl(coAndContraVariantInferences5.ts, 17, 2))
|
||||
>KeyT : Symbol(KeyT, Decl(coAndContraVariantInferences5.ts, 19, 19))
|
||||
|
||||
| Array<{key: KeyT}>
|
||||
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>key : Symbol(key, Decl(coAndContraVariantInferences5.ts, 20, 13))
|
||||
>KeyT : Symbol(KeyT, Decl(coAndContraVariantInferences5.ts, 19, 19))
|
||||
|
||||
| Array<KeyT>;
|
||||
>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
|
||||
>KeyT : Symbol(KeyT, Decl(coAndContraVariantInferences5.ts, 19, 19))
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
=== tests/cases/compiler/coAndContraVariantInferences5.ts ===
|
||||
type Thing = 'a' | 'b';
|
||||
>Thing : "a" | "b"
|
||||
|
||||
function f(
|
||||
>f : (options: SelectOptions<Thing>, onChange: (status: Thing | null) => void) => void
|
||||
|
||||
options: SelectOptions<Thing>,
|
||||
>options : SelectOptions<Thing>
|
||||
|
||||
onChange: (status: Thing | null) => void,
|
||||
>onChange : (status: Thing | null) => void
|
||||
>status : Thing | null
|
||||
|
||||
): void {
|
||||
select({
|
||||
>select({ options, onChange, }) : void
|
||||
>select : <KeyT extends string>(props: SelectProps<KeyT>) => void
|
||||
>{ options, onChange, } : { options: SelectOptions<Thing>; onChange: (status: Thing | null) => void; }
|
||||
|
||||
options,
|
||||
>options : SelectOptions<Thing>
|
||||
|
||||
onChange,
|
||||
>onChange : (status: Thing | null) => void
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
declare function select<KeyT extends string>(props: SelectProps<KeyT>): void;
|
||||
>select : <KeyT extends string>(props: SelectProps<KeyT>) => void
|
||||
>props : SelectProps<KeyT>
|
||||
|
||||
type SelectProps<KeyT extends string> = {
|
||||
>SelectProps : SelectProps<KeyT>
|
||||
|
||||
options?: SelectOptions<KeyT>;
|
||||
>options : SelectOptions<KeyT> | undefined
|
||||
|
||||
onChange: (key: KeyT) => void;
|
||||
>onChange : (key: KeyT) => void
|
||||
>key : KeyT
|
||||
|
||||
};
|
||||
|
||||
type SelectOptions<KeyT extends string> =
|
||||
>SelectOptions : SelectOptions<KeyT>
|
||||
|
||||
| Array<{key: KeyT}>
|
||||
>key : KeyT
|
||||
|
||||
| Array<KeyT>;
|
||||
|
||||
25
tests/cases/compiler/coAndContraVariantInferences5.ts
Normal file
25
tests/cases/compiler/coAndContraVariantInferences5.ts
Normal file
@ -0,0 +1,25 @@
|
||||
// @strict: true
|
||||
// @noEmit: true
|
||||
|
||||
type Thing = 'a' | 'b';
|
||||
|
||||
function f(
|
||||
options: SelectOptions<Thing>,
|
||||
onChange: (status: Thing | null) => void,
|
||||
): void {
|
||||
select({
|
||||
options,
|
||||
onChange,
|
||||
});
|
||||
}
|
||||
|
||||
declare function select<KeyT extends string>(props: SelectProps<KeyT>): void;
|
||||
|
||||
type SelectProps<KeyT extends string> = {
|
||||
options?: SelectOptions<KeyT>;
|
||||
onChange: (key: KeyT) => void;
|
||||
};
|
||||
|
||||
type SelectOptions<KeyT extends string> =
|
||||
| Array<{key: KeyT}>
|
||||
| Array<KeyT>;
|
||||
Loading…
x
Reference in New Issue
Block a user