mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-16 07:13:45 -05:00
Merge pull request #22197 from Microsoft/fixConditionalTypes
Conditional type fixes
This commit is contained in:
@@ -318,7 +318,6 @@ namespace ts {
|
||||
const intersectionTypes = createMap<IntersectionType>();
|
||||
const literalTypes = createMap<LiteralType>();
|
||||
const indexedAccessTypes = createMap<IndexedAccessType>();
|
||||
const conditionalTypes = createMap<ConditionalType>();
|
||||
const evolvingArrayTypes: EvolvingArrayType[] = [];
|
||||
const undefinedProperties = createMap<Symbol>() as UnderscoreEscapedMap<Symbol>;
|
||||
|
||||
@@ -600,6 +599,7 @@ namespace ts {
|
||||
ObjectType = 1 << 9,
|
||||
EmptyObject = 1 << 10,
|
||||
Union = 1 << 11,
|
||||
Wildcard = 1 << 12,
|
||||
}
|
||||
|
||||
const enum MembersOrExportsResolutionKind {
|
||||
@@ -2975,8 +2975,8 @@ namespace ts {
|
||||
if (type.flags & TypeFlags.Conditional) {
|
||||
const checkTypeNode = typeToTypeNodeHelper((<ConditionalType>type).checkType, context);
|
||||
const extendsTypeNode = typeToTypeNodeHelper((<ConditionalType>type).extendsType, context);
|
||||
const trueTypeNode = typeToTypeNodeHelper((<ConditionalType>type).trueType, context);
|
||||
const falseTypeNode = typeToTypeNodeHelper((<ConditionalType>type).falseType, context);
|
||||
const trueTypeNode = typeToTypeNodeHelper(getTrueTypeFromConditionalType(<ConditionalType>type), context);
|
||||
const falseTypeNode = typeToTypeNodeHelper(getFalseTypeFromConditionalType(<ConditionalType>type), context);
|
||||
return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode);
|
||||
}
|
||||
if (type.flags & TypeFlags.Substitution) {
|
||||
@@ -5899,8 +5899,7 @@ namespace ts {
|
||||
// Create a mapper from T to the current iteration type constituent. Then, if the
|
||||
// mapped type is itself an instantiated type, combine the iteration mapper with the
|
||||
// instantiation mapper.
|
||||
const iterationMapper = createTypeMapper([typeParameter], [t]);
|
||||
const templateMapper = type.mapper ? combineTypeMappers(type.mapper, iterationMapper) : iterationMapper;
|
||||
const templateMapper = combineTypeMappers(type.mapper, createTypeMapper([typeParameter], [t]));
|
||||
const propType = instantiateType(templateType, templateMapper);
|
||||
// If the current iteration type constituent is a string literal type, create a property.
|
||||
// Otherwise, for type string create a string index signature.
|
||||
@@ -6122,22 +6121,20 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getDefaultConstraintOfConditionalType(type: ConditionalType) {
|
||||
return getUnionType([type.trueType, type.falseType]);
|
||||
return getUnionType([getTrueTypeFromConditionalType(type), getFalseTypeFromConditionalType(type)]);
|
||||
}
|
||||
|
||||
function getConstraintOfDistributiveConditionalType(type: ConditionalType) {
|
||||
function getConstraintOfDistributiveConditionalType(type: ConditionalType): Type {
|
||||
// Check if we have a conditional type of the form 'T extends U ? X : Y', where T is a constrained
|
||||
// type parameter. If so, create an instantiation of the conditional type where T is replaced
|
||||
// with its constraint. We do this because if the constraint is a union type it will be distributed
|
||||
// over the conditional type and possibly reduced. For example, 'T extends undefined ? never : T'
|
||||
// removes 'undefined' from T.
|
||||
if (isDistributiveConditionalType(type)) {
|
||||
if (type.root.isDistributive) {
|
||||
const constraint = getConstraintOfType(type.checkType);
|
||||
if (constraint) {
|
||||
const target = type.target || type;
|
||||
const mapper = createTypeMapper([<TypeParameter>target.checkType], [constraint]);
|
||||
const combinedMapper = type.mapper ? combineTypeMappers(mapper, type.mapper) : mapper;
|
||||
return instantiateType(target, combinedMapper);
|
||||
const mapper = createTypeMapper([<TypeParameter>type.root.checkType], [constraint]);
|
||||
return getConditionalTypeInstantiation(type, combineTypeMappers(mapper, type.mapper));
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
@@ -6236,7 +6233,8 @@ namespace ts {
|
||||
return baseIndexedAccess && baseIndexedAccess !== unknownType ? getBaseConstraint(baseIndexedAccess) : undefined;
|
||||
}
|
||||
if (t.flags & TypeFlags.Conditional) {
|
||||
return getBaseConstraint(getConstraintOfConditionalType(<ConditionalType>t));
|
||||
const constraint = getConstraintOfConditionalType(<ConditionalType>t);
|
||||
return constraint && getBaseConstraint(constraint);
|
||||
}
|
||||
if (t.flags & TypeFlags.Substitution) {
|
||||
return getBaseConstraint((<SubstitutionType>t).substitute);
|
||||
@@ -7639,6 +7637,7 @@ namespace ts {
|
||||
}
|
||||
else if (flags & TypeFlags.Any) {
|
||||
includes |= TypeIncludes.Any;
|
||||
if (type === wildcardType) includes |= TypeIncludes.Wildcard;
|
||||
}
|
||||
else if (!strictNullChecks && flags & TypeFlags.Nullable) {
|
||||
if (flags & TypeFlags.Undefined) includes |= TypeIncludes.Undefined;
|
||||
@@ -7758,7 +7757,7 @@ namespace ts {
|
||||
const typeSet: Type[] = [];
|
||||
const includes = addTypesToUnion(typeSet, 0, types);
|
||||
if (includes & TypeIncludes.Any) {
|
||||
return anyType;
|
||||
return includes & TypeIncludes.Wildcard ? wildcardType : anyType;
|
||||
}
|
||||
switch (unionReduction) {
|
||||
case UnionReduction.Literal:
|
||||
@@ -8140,7 +8139,7 @@ namespace ts {
|
||||
|
||||
function substituteIndexedMappedType(objectType: MappedType, type: IndexedAccessType) {
|
||||
const mapper = createTypeMapper([getTypeParameterFromMappedType(objectType)], [type.indexType]);
|
||||
const templateMapper = objectType.mapper ? combineTypeMappers(objectType.mapper, mapper) : mapper;
|
||||
const templateMapper = combineTypeMappers(objectType.mapper, mapper);
|
||||
return instantiateType(getTemplateTypeFromMappedType(objectType), templateMapper);
|
||||
}
|
||||
|
||||
@@ -8207,76 +8206,59 @@ namespace ts {
|
||||
return type.flags & TypeFlags.Substitution ? (<SubstitutionType>type).typeParameter : type;
|
||||
}
|
||||
|
||||
function createConditionalType(checkType: Type, extendsType: Type, trueType: Type, falseType: Type, inferTypeParameters: TypeParameter[], target: ConditionalType, mapper: TypeMapper, aliasSymbol: Symbol, aliasTypeArguments: Type[]) {
|
||||
const type = <ConditionalType>createType(TypeFlags.Conditional);
|
||||
type.checkType = checkType;
|
||||
type.extendsType = extendsType;
|
||||
type.trueType = trueType;
|
||||
type.falseType = falseType;
|
||||
type.inferTypeParameters = inferTypeParameters;
|
||||
type.target = target;
|
||||
type.mapper = mapper;
|
||||
type.aliasSymbol = aliasSymbol;
|
||||
type.aliasTypeArguments = aliasTypeArguments;
|
||||
return type;
|
||||
}
|
||||
|
||||
function getConditionalType(checkType: Type, baseExtendsType: Type, baseTrueType: Type, baseFalseType: Type, inferTypeParameters: TypeParameter[], target: ConditionalType, mapper: TypeMapper, aliasSymbol?: Symbol, baseAliasTypeArguments?: Type[]): Type {
|
||||
// Instantiate extends type without instantiating any 'infer T' type parameters
|
||||
const extendsType = instantiateType(baseExtendsType, mapper);
|
||||
function getConditionalType(root: ConditionalRoot, mapper: TypeMapper): Type {
|
||||
const checkType = instantiateType(root.checkType, mapper);
|
||||
const extendsType = instantiateType(root.extendsType, mapper);
|
||||
// Return falseType for a definitely false extends check. We check an instantations of the two
|
||||
// types with type parameters mapped to the wildcard type, the most permissive instantiations
|
||||
// possible (the wildcard type is assignable to and from all types). If those are not related,
|
||||
// then no instatiations will be and we can just return the false branch type.
|
||||
if (!typeMaybeAssignableTo(getWildcardInstantiation(checkType), getWildcardInstantiation(extendsType))) {
|
||||
return instantiateType(baseFalseType, mapper);
|
||||
return instantiateType(root.falseType, mapper);
|
||||
}
|
||||
// The check could be true for some instantiation
|
||||
let combinedMapper: TypeMapper;
|
||||
if (inferTypeParameters) {
|
||||
const inferences = map(inferTypeParameters, createInferenceInfo);
|
||||
if (root.inferTypeParameters) {
|
||||
const inferences = map(root.inferTypeParameters, createInferenceInfo);
|
||||
// We don't want inferences from constraints as they may cause us to eagerly resolve the
|
||||
// conditional type instead of deferring resolution. Also, we always want strict function
|
||||
// types rules (i.e. proper contravariance) for inferences.
|
||||
inferTypes(inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
|
||||
// We infer 'never' when there are no candidates for a type parameter
|
||||
const inferredTypes = map(inferences, inference => getTypeFromInference(inference) || neverType);
|
||||
const inferenceMapper = createTypeMapper(inferTypeParameters, inferredTypes);
|
||||
combinedMapper = mapper ? combineTypeMappers(mapper, inferenceMapper) : inferenceMapper;
|
||||
// We infer {} when there are no candidates for a type parameter
|
||||
const inferredTypes = map(inferences, inference => getTypeFromInference(inference) || emptyObjectType);
|
||||
combinedMapper = combineTypeMappers(mapper, createTypeMapper(root.inferTypeParameters, inferredTypes));
|
||||
}
|
||||
// Return union of trueType and falseType for any and never since they match anything
|
||||
if (checkType.flags & TypeFlags.Any || (checkType.flags & TypeFlags.Never && !(extendsType.flags & TypeFlags.Never))) {
|
||||
return getUnionType([instantiateType(baseTrueType, combinedMapper || mapper), instantiateType(baseFalseType, mapper)]);
|
||||
// Return union of trueType and falseType for 'any' since it matches anything
|
||||
if (checkType.flags & TypeFlags.Any) {
|
||||
return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), instantiateType(root.falseType, mapper)]);
|
||||
}
|
||||
// Instantiate the extends type including inferences for 'infer T' type parameters
|
||||
const inferredExtendsType = combinedMapper ? instantiateType(baseExtendsType, combinedMapper) : extendsType;
|
||||
const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType;
|
||||
// Return trueType for a definitely true extends check. The definitely assignable relation excludes
|
||||
// type variable constraints from consideration. Without the definitely assignable relation, the type
|
||||
// type Foo<T extends { x: any }> = T extends { x: string } ? string : number
|
||||
// would immediately resolve to 'string' instead of being deferred.
|
||||
if (checkTypeRelatedTo(checkType, inferredExtendsType, definitelyAssignableRelation, /*errorNode*/ undefined)) {
|
||||
return instantiateType(baseTrueType, combinedMapper || mapper);
|
||||
return instantiateType(root.trueType, combinedMapper || mapper);
|
||||
}
|
||||
// Return a deferred type for a check that is neither definitely true nor definitely false
|
||||
const erasedCheckType = getActualTypeParameter(checkType);
|
||||
const trueType = instantiateType(baseTrueType, mapper);
|
||||
const falseType = instantiateType(baseFalseType, mapper);
|
||||
// We compute the cache key from the ids of the four constituent types, plus an indicator of whether the
|
||||
// type is distributive (i.e. whether the original declaration has a type parameter as the check type).
|
||||
const isDistributive = (target ? target.checkType : erasedCheckType).flags & TypeFlags.TypeParameter ? 1 : 0;
|
||||
const id = erasedCheckType.id + "," + extendsType.id + "," + trueType.id + "," + falseType.id + "," + isDistributive;
|
||||
const cached = conditionalTypes.get(id);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
const result = createConditionalType(erasedCheckType, extendsType, trueType, falseType,
|
||||
inferTypeParameters, target, mapper, aliasSymbol, instantiateTypes(baseAliasTypeArguments, mapper));
|
||||
conditionalTypes.set(id, result);
|
||||
const result = <ConditionalType>createType(TypeFlags.Conditional);
|
||||
result.root = root;
|
||||
result.checkType = erasedCheckType;
|
||||
result.extendsType = extendsType;
|
||||
result.mapper = mapper;
|
||||
result.aliasSymbol = root.aliasSymbol;
|
||||
result.aliasTypeArguments = instantiateTypes(root.aliasTypeArguments, mapper);
|
||||
return result;
|
||||
}
|
||||
|
||||
function isDistributiveConditionalType(type: ConditionalType) {
|
||||
return !!((type.target || type).checkType.flags & TypeFlags.TypeParameter);
|
||||
function getTrueTypeFromConditionalType(type: ConditionalType) {
|
||||
return type.resolvedTrueType || (type.resolvedTrueType = instantiateType(type.root.trueType, type.mapper));
|
||||
}
|
||||
|
||||
function getFalseTypeFromConditionalType(type: ConditionalType) {
|
||||
return type.resolvedFalseType || (type.resolvedFalseType = instantiateType(type.root.falseType, type.mapper));
|
||||
}
|
||||
|
||||
function getInferTypeParameters(node: ConditionalTypeNode): TypeParameter[] {
|
||||
@@ -8294,11 +8276,28 @@ namespace ts {
|
||||
function getTypeFromConditionalTypeNode(node: ConditionalTypeNode): Type {
|
||||
const links = getNodeLinks(node);
|
||||
if (!links.resolvedType) {
|
||||
links.resolvedType = getConditionalType(
|
||||
getTypeFromTypeNode(node.checkType), getTypeFromTypeNode(node.extendsType),
|
||||
getTypeFromTypeNode(node.trueType), getTypeFromTypeNode(node.falseType),
|
||||
getInferTypeParameters(node), /*target*/ undefined, /*mapper*/ undefined,
|
||||
getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node));
|
||||
const checkType = getTypeFromTypeNode(node.checkType);
|
||||
const aliasTypeArguments = getAliasTypeArgumentsForTypeNode(node);
|
||||
const allOuterTypeParameters = getOuterTypeParameters(node, /*includeThisTypes*/ true);
|
||||
const outerTypeParameters = aliasTypeArguments ? allOuterTypeParameters : filter(allOuterTypeParameters, tp => isTypeParameterPossiblyReferenced(tp, node));
|
||||
const root: ConditionalRoot = {
|
||||
node,
|
||||
checkType,
|
||||
extendsType: getTypeFromTypeNode(node.extendsType),
|
||||
trueType: getTypeFromTypeNode(node.trueType),
|
||||
falseType: getTypeFromTypeNode(node.falseType),
|
||||
isDistributive: !!(checkType.flags & TypeFlags.TypeParameter),
|
||||
inferTypeParameters: getInferTypeParameters(node),
|
||||
outerTypeParameters,
|
||||
instantiations: undefined,
|
||||
aliasSymbol: getAliasSymbolForTypeNode(node),
|
||||
aliasTypeArguments
|
||||
};
|
||||
links.resolvedType = getConditionalType(root, /*mapper*/ undefined);
|
||||
if (outerTypeParameters) {
|
||||
root.instantiations = createMap<Type>();
|
||||
root.instantiations.set(getTypeListId(outerTypeParameters), links.resolvedType);
|
||||
}
|
||||
}
|
||||
return links.resolvedType;
|
||||
}
|
||||
@@ -8689,6 +8688,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function combineTypeMappers(mapper1: TypeMapper, mapper2: TypeMapper): TypeMapper {
|
||||
if (!mapper1) return mapper2;
|
||||
if (!mapper2) return mapper1;
|
||||
return t => instantiateType(mapper1(t), mapper2);
|
||||
}
|
||||
|
||||
@@ -8886,24 +8887,36 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getConditionalTypeInstantiation(type: ConditionalType, mapper: TypeMapper): Type {
|
||||
const target = type.target || type;
|
||||
const combinedMapper = type.mapper ? combineTypeMappers(type.mapper, mapper) : mapper;
|
||||
const root = type.root;
|
||||
if (root.outerTypeParameters) {
|
||||
// We are instantiating a conditional type that has one or more type parameters in scope. Apply the
|
||||
// mapper to the type parameters to produce the effective list of type arguments, and compute the
|
||||
// instantiation cache key from the type IDs of the type arguments.
|
||||
const typeArguments = map(root.outerTypeParameters, mapper);
|
||||
const id = getTypeListId(typeArguments);
|
||||
let result = root.instantiations.get(id);
|
||||
if (!result) {
|
||||
const newMapper = createTypeMapper(root.outerTypeParameters, typeArguments);
|
||||
result = instantiateConditionalType(root, newMapper);
|
||||
root.instantiations.set(id, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
function instantiateConditionalType(root: ConditionalRoot, mapper: TypeMapper): Type {
|
||||
// Check if we have a conditional type where the check type is a naked type parameter. If so,
|
||||
// the conditional type is distributive over union types and when T is instantiated to a union
|
||||
// type A | B, we produce (A extends U ? X : Y) | (B extends U ? X : Y).
|
||||
if (isDistributiveConditionalType(target)) {
|
||||
const checkType = <TypeParameter>target.checkType;
|
||||
const instantiatedType = combinedMapper(checkType);
|
||||
if (checkType !== instantiatedType && instantiatedType.flags & TypeFlags.Union) {
|
||||
return mapType(instantiatedType, t => instantiateConditionalType(target, createReplacementMapper(checkType, t, combinedMapper)));
|
||||
if (root.isDistributive) {
|
||||
const checkType = <TypeParameter>root.checkType;
|
||||
const instantiatedType = mapper(checkType);
|
||||
if (checkType !== instantiatedType && instantiatedType.flags & (TypeFlags.Union | TypeFlags.Never)) {
|
||||
return mapType(instantiatedType, t => getConditionalType(root, createReplacementMapper(checkType, t, mapper)));
|
||||
}
|
||||
}
|
||||
return instantiateConditionalType(target, combinedMapper);
|
||||
}
|
||||
|
||||
function instantiateConditionalType(type: ConditionalType, mapper: TypeMapper): Type {
|
||||
return getConditionalType(instantiateType(type.checkType, mapper), type.extendsType, type.trueType, type.falseType,
|
||||
type.inferTypeParameters, type, mapper, type.aliasSymbol, type.aliasTypeArguments);
|
||||
return getConditionalType(root, mapper);
|
||||
}
|
||||
|
||||
function instantiateType(type: Type, mapper: TypeMapper): Type {
|
||||
@@ -8945,7 +8958,7 @@ namespace ts {
|
||||
return getIndexedAccessType(instantiateType((<IndexedAccessType>type).objectType, mapper), instantiateType((<IndexedAccessType>type).indexType, mapper));
|
||||
}
|
||||
if (type.flags & TypeFlags.Conditional) {
|
||||
return getConditionalTypeInstantiation(<ConditionalType>type, mapper);
|
||||
return getConditionalTypeInstantiation(<ConditionalType>type, combineTypeMappers((<ConditionalType>type).mapper, mapper));
|
||||
}
|
||||
if (type.flags & TypeFlags.Substitution) {
|
||||
return mapper((<SubstitutionType>type).typeParameter);
|
||||
@@ -9691,11 +9704,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
if (flags & TypeFlags.Conditional) {
|
||||
if (result = isRelatedTo((<ConditionalType>source).checkType, (<ConditionalType>target).checkType, /*reportErrors*/ false)) {
|
||||
if (result &= isRelatedTo((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType, /*reportErrors*/ false)) {
|
||||
if (result &= isRelatedTo((<ConditionalType>source).trueType, (<ConditionalType>target).trueType, /*reportErrors*/ false)) {
|
||||
if (result &= isRelatedTo((<ConditionalType>source).falseType, (<ConditionalType>target).falseType, /*reportErrors*/ false)) {
|
||||
if (isDistributiveConditionalType(<ConditionalType>source) === isDistributiveConditionalType(<ConditionalType>target)) {
|
||||
if ((<ConditionalType>source).root.isDistributive === (<ConditionalType>target).root.isDistributive) {
|
||||
if (result = isRelatedTo((<ConditionalType>source).checkType, (<ConditionalType>target).checkType, /*reportErrors*/ false)) {
|
||||
if (result &= isRelatedTo((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType, /*reportErrors*/ false)) {
|
||||
if (result &= isRelatedTo(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target), /*reportErrors*/ false)) {
|
||||
if (result &= isRelatedTo(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target), /*reportErrors*/ false)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -10085,20 +10098,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
else if (source.flags & TypeFlags.Conditional) {
|
||||
if (relation !== definitelyAssignableRelation) {
|
||||
const constraint = getConstraintOfDistributiveConditionalType(<ConditionalType>source);
|
||||
if (constraint) {
|
||||
if (result = isRelatedTo(constraint, target, reportErrors)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target.flags & TypeFlags.Conditional) {
|
||||
if (isTypeIdenticalTo((<ConditionalType>source).checkType, (<ConditionalType>target).checkType) &&
|
||||
isTypeIdenticalTo((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType)) {
|
||||
if (result = isRelatedTo((<ConditionalType>source).trueType, (<ConditionalType>target).trueType, reportErrors)) {
|
||||
result &= isRelatedTo((<ConditionalType>source).falseType, (<ConditionalType>target).falseType, reportErrors);
|
||||
if (result = isRelatedTo(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target), reportErrors)) {
|
||||
result &= isRelatedTo(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target), reportErrors);
|
||||
}
|
||||
if (result) {
|
||||
errorInfo = saveErrorInfo;
|
||||
@@ -10106,9 +10110,21 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (result = isRelatedTo(getDefaultConstraintOfConditionalType(<ConditionalType>source), target, reportErrors)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
return result;
|
||||
else if (relation !== definitelyAssignableRelation) {
|
||||
const distributiveConstraint = getConstraintOfDistributiveConditionalType(<ConditionalType>source);
|
||||
if (distributiveConstraint) {
|
||||
if (result = isRelatedTo(distributiveConstraint, target, reportErrors)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
const defaultConstraint = getDefaultConstraintOfConditionalType(<ConditionalType>source);
|
||||
if (defaultConstraint) {
|
||||
if (result = isRelatedTo(defaultConstraint, target, reportErrors)) {
|
||||
errorInfo = saveErrorInfo;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -11479,12 +11495,23 @@ namespace ts {
|
||||
let symbolStack: Symbol[];
|
||||
let visited: Map<boolean>;
|
||||
let contravariant = false;
|
||||
let propagationType: Type;
|
||||
inferFromTypes(originalSource, originalTarget);
|
||||
|
||||
function inferFromTypes(source: Type, target: Type) {
|
||||
if (!couldContainTypeVariables(target)) {
|
||||
return;
|
||||
}
|
||||
if (source.flags & (TypeFlags.Any | TypeFlags.Never) && source !== silentNeverType) {
|
||||
// We are inferring from 'any' or 'never'. We want to infer this type for every type parameter
|
||||
// referenced in the target type, so we record the propagation type and infer from the target
|
||||
// to itself. Then, as we find candidates we substitute the propagation type.
|
||||
const savePropagationType = propagationType;
|
||||
propagationType = source;
|
||||
inferFromTypes(target, target);
|
||||
propagationType = savePropagationType;
|
||||
return;
|
||||
}
|
||||
if (source.aliasSymbol && source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) {
|
||||
// Source and target are types originating in the same generic type alias declaration.
|
||||
// Simply infer from source type arguments to target type arguments.
|
||||
@@ -11552,11 +11579,12 @@ namespace ts {
|
||||
inference.priority = priority;
|
||||
}
|
||||
if (priority === inference.priority) {
|
||||
const candidate = propagationType || source;
|
||||
if (contravariant) {
|
||||
inference.contraCandidates = append(inference.contraCandidates, source);
|
||||
inference.contraCandidates = append(inference.contraCandidates, candidate);
|
||||
}
|
||||
else {
|
||||
inference.candidates = append(inference.candidates, source);
|
||||
inference.candidates = append(inference.candidates, candidate);
|
||||
}
|
||||
}
|
||||
if (!(priority & InferencePriority.ReturnType) && target.flags & TypeFlags.TypeParameter && !isTypeParameterAtTopLevel(originalTarget, <TypeParameter>target)) {
|
||||
@@ -11599,8 +11627,8 @@ namespace ts {
|
||||
else if (source.flags & TypeFlags.Conditional && target.flags & TypeFlags.Conditional) {
|
||||
inferFromTypes((<ConditionalType>source).checkType, (<ConditionalType>target).checkType);
|
||||
inferFromTypes((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType);
|
||||
inferFromTypes((<ConditionalType>source).trueType, (<ConditionalType>target).trueType);
|
||||
inferFromTypes((<ConditionalType>source).falseType, (<ConditionalType>target).falseType);
|
||||
inferFromTypes(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target));
|
||||
inferFromTypes(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target));
|
||||
}
|
||||
else if (target.flags & TypeFlags.UnionOrIntersection) {
|
||||
const targetTypes = (<UnionOrIntersectionType>target).types;
|
||||
@@ -12424,6 +12452,9 @@ namespace ts {
|
||||
// is a union type, the mapping function is applied to each constituent type and a union
|
||||
// of the resulting types is returned.
|
||||
function mapType(type: Type, mapper: (t: Type) => Type, noReductions?: boolean): Type {
|
||||
if (type.flags & TypeFlags.Never) {
|
||||
return type;
|
||||
}
|
||||
if (!(type.flags & TypeFlags.Union)) {
|
||||
return mapper(type);
|
||||
}
|
||||
|
||||
@@ -3821,16 +3821,27 @@ namespace ts {
|
||||
type: InstantiableType | UnionOrIntersectionType;
|
||||
}
|
||||
|
||||
// T extends U ? X : Y (TypeFlags.Conditional)
|
||||
export interface ConditionalType extends InstantiableType {
|
||||
export interface ConditionalRoot {
|
||||
node: ConditionalTypeNode;
|
||||
checkType: Type;
|
||||
extendsType: Type;
|
||||
trueType: Type;
|
||||
falseType: Type;
|
||||
/* @internal */
|
||||
isDistributive: boolean;
|
||||
inferTypeParameters: TypeParameter[];
|
||||
/* @internal */
|
||||
target?: ConditionalType;
|
||||
outerTypeParameters?: TypeParameter[];
|
||||
instantiations?: Map<Type>;
|
||||
aliasSymbol: Symbol;
|
||||
aliasTypeArguments: Type[];
|
||||
}
|
||||
|
||||
// T extends U ? X : Y (TypeFlags.Conditional)
|
||||
export interface ConditionalType extends InstantiableType {
|
||||
root: ConditionalRoot;
|
||||
checkType: Type;
|
||||
extendsType: Type;
|
||||
resolvedTrueType?: Type;
|
||||
resolvedFalseType?: Type;
|
||||
/* @internal */
|
||||
mapper?: TypeMapper;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user