Skip to content

Commit c6f0dfb

Browse files
committed
Add definitelyAssignableRelation
1 parent edffb12 commit c6f0dfb

1 file changed

Lines changed: 24 additions & 14 deletions

File tree

src/compiler/checker.ts

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ namespace ts {
538538

539539
const subtypeRelation = createMap<RelationComparisonResult>();
540540
const assignableRelation = createMap<RelationComparisonResult>();
541+
const definitelyAssignableRelation = createMap<RelationComparisonResult>();
541542
const comparableRelation = createMap<RelationComparisonResult>();
542543
const identityRelation = createMap<RelationComparisonResult>();
543544
const enumRelation = createMap<boolean>();
@@ -8128,8 +8129,11 @@ namespace ts {
81288129
}
81298130
// Instantiate the extends type including inferences for 'infer T' type parameters
81308131
const inferredExtendsType = combinedMapper ? instantiateType(baseExtendsType, combinedMapper) : extendsType;
8131-
// Return trueType for a definitely true extends check
8132-
if (isTypeAssignableTo(checkType, inferredExtendsType)) {
8132+
// Return trueType for a definitely true extends check. The definitely assignable relation excludes
8133+
// type variable constraints from consideration. Without the definitely assignable relation, the type
8134+
// type Foo<T extends { x: any }> = T extends { x: string } ? string : number
8135+
// would immediately resolve to 'string' instead of being deferred.
8136+
if (checkTypeRelatedTo(checkType, inferredExtendsType, definitelyAssignableRelation, /*errorNode*/ undefined)) {
81338137
return instantiateType(baseTrueType, combinedMapper || mapper);
81348138
}
81358139
// Return a deferred type for a check that is neither definitely true nor definitely false
@@ -9238,7 +9242,7 @@ namespace ts {
92389242
if (s & TypeFlags.Null && (!strictNullChecks || t & TypeFlags.Null)) return true;
92399243
if (s & TypeFlags.Object && t & TypeFlags.NonPrimitive) return true;
92409244
if (s & TypeFlags.UniqueESSymbol || t & TypeFlags.UniqueESSymbol) return false;
9241-
if (relation === assignableRelation || relation === comparableRelation) {
9245+
if (relation === assignableRelation || relation === definitelyAssignableRelation || relation === comparableRelation) {
92429246
if (s & TypeFlags.Any) return true;
92439247
// Type number or any numeric literal type is assignable to any numeric enum type or any
92449248
// numeric enum literal type. This rule exists for backwards compatibility reasons because
@@ -9407,7 +9411,7 @@ namespace ts {
94079411
target = (<LiteralType>target).regularType;
94089412
}
94099413
if (source.flags & TypeFlags.Substitution) {
9410-
source = (<SubstitutionType>source).substitute;
9414+
source = relation === definitelyAssignableRelation ? (<SubstitutionType>source).typeParameter : (<SubstitutionType>source).substitute;
94119415
}
94129416
if (target.flags & TypeFlags.Substitution) {
94139417
target = (<SubstitutionType>target).typeParameter;
@@ -9538,7 +9542,7 @@ namespace ts {
95389542
function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean {
95399543
if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) {
95409544
const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes);
9541-
if ((relation === assignableRelation || relation === comparableRelation) &&
9545+
if ((relation === assignableRelation || relation === definitelyAssignableRelation || relation === comparableRelation) &&
95429546
(isTypeSubsetOf(globalObjectType, target) || (!isComparingJsxAttributes && isEmptyObjectType(target)))) {
95439547
return false;
95449548
}
@@ -9810,6 +9814,10 @@ namespace ts {
98109814
return result;
98119815
}
98129816

9817+
function getConstraintForRelation(type: Type) {
9818+
return relation === definitelyAssignableRelation ? undefined : getConstraintOfType(type);
9819+
}
9820+
98139821
function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean): Ternary {
98149822
let result: Ternary;
98159823
let originalErrorInfo: DiagnosticMessageChain;
@@ -9835,7 +9843,7 @@ namespace ts {
98359843
}
98369844
// A type S is assignable to keyof T if S is assignable to keyof C, where C is the
98379845
// constraint of T.
9838-
const constraint = getConstraintOfType((<IndexType>target).type);
9846+
const constraint = getConstraintForRelation((<IndexType>target).type);
98399847
if (constraint) {
98409848
if (result = isRelatedTo(source, getIndexType(constraint), reportErrors)) {
98419849
return result;
@@ -9845,7 +9853,7 @@ namespace ts {
98459853
else if (target.flags & TypeFlags.IndexedAccess) {
98469854
// A type S is related to a type T[K] if S is related to A[K], where K is string-like and
98479855
// A is the apparent type of T.
9848-
const constraint = getConstraintOfIndexedAccess(<IndexedAccessType>target);
9856+
const constraint = getConstraintForRelation(<IndexedAccessType>target);
98499857
if (constraint) {
98509858
if (result = isRelatedTo(source, constraint, reportErrors)) {
98519859
errorInfo = saveErrorInfo;
@@ -9872,7 +9880,7 @@ namespace ts {
98729880
}
98739881

98749882
if (source.flags & TypeFlags.TypeParameter) {
9875-
let constraint = getConstraintOfTypeParameter(<TypeParameter>source);
9883+
let constraint = getConstraintForRelation(<TypeParameter>source);
98769884
// A type parameter with no constraint is not related to the non-primitive object type.
98779885
if (constraint || !(target.flags & TypeFlags.NonPrimitive)) {
98789886
if (!constraint || constraint.flags & TypeFlags.Any) {
@@ -9889,7 +9897,7 @@ namespace ts {
98899897
else if (source.flags & TypeFlags.IndexedAccess) {
98909898
// A type S[K] is related to a type T if A[K] is related to T, where K is string-like and
98919899
// A is the apparent type of S.
9892-
const constraint = getConstraintOfIndexedAccess(<IndexedAccessType>source);
9900+
const constraint = getConstraintForRelation(<IndexedAccessType>source);
98939901
if (constraint) {
98949902
if (result = isRelatedTo(constraint, target, reportErrors)) {
98959903
errorInfo = saveErrorInfo;
@@ -9906,11 +9914,13 @@ namespace ts {
99069914
}
99079915
}
99089916
else if (source.flags & TypeFlags.Conditional) {
9909-
const constraint = getConstraintOfDistributiveConditionalType(<ConditionalType>source);
9910-
if (constraint) {
9911-
if (result = isRelatedTo(constraint, target, reportErrors)) {
9912-
errorInfo = saveErrorInfo;
9913-
return result;
9917+
if (relation !== definitelyAssignableRelation) {
9918+
const constraint = getConstraintOfDistributiveConditionalType(<ConditionalType>source);
9919+
if (constraint) {
9920+
if (result = isRelatedTo(constraint, target, reportErrors)) {
9921+
errorInfo = saveErrorInfo;
9922+
return result;
9923+
}
99149924
}
99159925
}
99169926
if (result = isRelatedTo(getDefaultConstraintOfConditionalType(<ConditionalType>source), target, reportErrors)) {

0 commit comments

Comments
 (0)