-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Unify substitution type any
handling into costruction and instantiation
#30592
Merged
RyanCavanaugh
merged 6 commits into
microsoft:master
from
weswigham:unify-substitution-handling
Mar 27, 2019
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
cbbbda4
Unify substitution type `any` handling into costruction and instantia…
weswigham d5f32bd
Strengthen supertype reduction check to reduce breakage
weswigham d28ad01
Rename conditional type fields per convention
weswigham 73e3733
Explicitly handle anyish signatures in compareSignaturesRelated so st…
weswigham a2fcddc
Merge branch 'master' into unify-substitution-handling
weswigham 34cc427
Allow tuple expansions to an `any` rest to be considered an `any` sig…
weswigham File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3492,8 +3492,8 @@ namespace ts { | |
context.inferTypeParameters = (<ConditionalType>type).root.inferTypeParameters; | ||
const extendsTypeNode = typeToTypeNodeHelper((<ConditionalType>type).extendsType, context); | ||
context.inferTypeParameters = saveInferTypeParameters; | ||
const trueTypeNode = typeToTypeNodeHelper(getTrueTypeFromConditionalType(<ConditionalType>type), context); | ||
const falseTypeNode = typeToTypeNodeHelper(getFalseTypeFromConditionalType(<ConditionalType>type), context); | ||
const trueTypeNode = typeToTypeNodeHelper((<ConditionalType>type).trueType, context); | ||
const falseTypeNode = typeToTypeNodeHelper((<ConditionalType>type).falseType, context); | ||
context.approximateLength += 15; | ||
return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode); | ||
} | ||
|
@@ -7477,6 +7477,10 @@ namespace ts { | |
} | ||
|
||
function getConstraintOfIndexedAccess(type: IndexedAccessType) { | ||
return hasNonCircularBaseConstraint(type) ? getConstraintFromIndexedAccess(type) : undefined; | ||
} | ||
|
||
function getConstraintFromIndexedAccess(type: IndexedAccessType) { | ||
const objectType = getConstraintOfType(type.objectType) || type.objectType; | ||
if (objectType !== type.objectType) { | ||
const constraint = getIndexedAccessType(objectType, type.indexType, /*accessNode*/ undefined, errorType); | ||
|
@@ -7488,24 +7492,14 @@ namespace ts { | |
return baseConstraint && baseConstraint !== type ? baseConstraint : undefined; | ||
} | ||
|
||
function getDefaultConstraintOfTrueBranchOfConditionalType(root: ConditionalRoot, combinedMapper: TypeMapper | undefined, mapper: TypeMapper | undefined) { | ||
const rootTrueType = root.trueType; | ||
const rootTrueConstraint = !(rootTrueType.flags & TypeFlags.Substitution) | ||
? rootTrueType | ||
: instantiateType(((<SubstitutionType>rootTrueType).substitute), combinedMapper || mapper).flags & TypeFlags.AnyOrUnknown | ||
? (<SubstitutionType>rootTrueType).typeVariable | ||
: getIntersectionType([(<SubstitutionType>rootTrueType).substitute, (<SubstitutionType>rootTrueType).typeVariable]); | ||
return instantiateType(rootTrueConstraint, combinedMapper || mapper); | ||
} | ||
|
||
weswigham marked this conversation as resolved.
Show resolved
Hide resolved
|
||
function getDefaultConstraintOfConditionalType(type: ConditionalType) { | ||
if (!type.resolvedDefaultConstraint) { | ||
// An `any` branch of a conditional type would normally be viral - specifically, without special handling here, | ||
// a conditional type with a single branch of type `any` would be assignable to anything, since it's constraint would simplify to | ||
// just `any`. This result is _usually_ unwanted - so instead here we elide an `any` branch from the constraint type, | ||
// in effect treating `any` like `never` rather than `unknown` in this location. | ||
const trueConstraint = getDefaultConstraintOfTrueBranchOfConditionalType(type.root, type.combinedMapper, type.mapper); | ||
const falseConstraint = getFalseTypeFromConditionalType(type); | ||
const trueConstraint = getInferredTrueTypeFromConditionalType(type); | ||
const falseConstraint = type.falseType; | ||
type.resolvedDefaultConstraint = isTypeAny(trueConstraint) ? falseConstraint : isTypeAny(falseConstraint) ? trueConstraint : getUnionType([trueConstraint, falseConstraint]); | ||
} | ||
return type.resolvedDefaultConstraint; | ||
|
@@ -7537,10 +7531,14 @@ namespace ts { | |
return undefined; | ||
} | ||
|
||
function getConstraintOfConditionalType(type: ConditionalType) { | ||
function getConstraintFromConditionalType(type: ConditionalType) { | ||
return getConstraintOfDistributiveConditionalType(type) || getDefaultConstraintOfConditionalType(type); | ||
} | ||
|
||
function getConstraintOfConditionalType(type: ConditionalType) { | ||
return hasNonCircularBaseConstraint(type) ? getConstraintFromConditionalType(type) : undefined; | ||
} | ||
|
||
function getUnionConstraintOfIntersection(type: IntersectionType, targetIsUnion: boolean) { | ||
let constraints: Type[] | undefined; | ||
let hasDisjointDomainType = false; | ||
|
@@ -7617,7 +7615,7 @@ namespace ts { | |
if (!pushTypeResolution(t, TypeSystemPropertyName.ImmediateBaseConstraint)) { | ||
return circularConstraintType; | ||
} | ||
if (constraintDepth === 50) { | ||
if (constraintDepth >= 50) { | ||
// We have reached 50 recursive invocations of getImmediateBaseConstraint and there is a | ||
// very high likelyhood we're dealing with an infinite generic type that perpetually generates | ||
// new type identities as we descend into it. We stop the recursion here and mark this type | ||
|
@@ -7684,8 +7682,11 @@ namespace ts { | |
return baseIndexedAccess && baseIndexedAccess !== errorType ? getBaseConstraint(baseIndexedAccess) : undefined; | ||
} | ||
if (t.flags & TypeFlags.Conditional) { | ||
const constraint = getConstraintOfConditionalType(<ConditionalType>t); | ||
return constraint && getBaseConstraint(constraint); | ||
const constraint = getConstraintFromConditionalType(<ConditionalType>t); | ||
constraintDepth++; // Penalize repeating conditional types (this captures the recursion within getConstraintFromConditionalType and carries it forward) | ||
weswigham marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const result = constraint && getBaseConstraint(constraint); | ||
constraintDepth--; | ||
return result; | ||
} | ||
if (t.flags & TypeFlags.Substitution) { | ||
return getBaseConstraint((<SubstitutionType>t).substitute); | ||
|
@@ -8866,6 +8867,9 @@ namespace ts { | |
} | ||
|
||
function getSubstitutionType(typeVariable: TypeVariable, substitute: Type) { | ||
if (substitute.flags & TypeFlags.AnyOrUnknown) { | ||
return typeVariable; | ||
} | ||
const result = <SubstitutionType>createType(TypeFlags.Substitution); | ||
result.typeVariable = typeVariable; | ||
result.substitute = substitute; | ||
|
@@ -10161,7 +10165,7 @@ namespace ts { | |
// Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`. | ||
if (falseType.flags & TypeFlags.Never && isTypeIdenticalTo(getActualTypeVariable(trueType), getActualTypeVariable(checkType))) { | ||
if (checkType.flags & TypeFlags.Any || isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(extendsType))) { // Always true | ||
return getDefaultConstraintOfTrueBranchOfConditionalType(root, /*combinedMapper*/ undefined, mapper); | ||
return trueType; | ||
} | ||
else if (isIntersectionEmpty(checkType, extendsType)) { // Always false | ||
return neverType; | ||
|
@@ -10172,7 +10176,7 @@ namespace ts { | |
return neverType; | ||
} | ||
else if (checkType.flags & TypeFlags.Any || isIntersectionEmpty(checkType, extendsType)) { // Always false | ||
return falseType; // TODO: Intersect negated `extends` type here | ||
return falseType; | ||
} | ||
} | ||
|
||
|
@@ -10227,21 +10231,15 @@ namespace ts { | |
result.extendsType = extendsType; | ||
result.mapper = mapper; | ||
result.combinedMapper = combinedMapper; | ||
if (!combinedMapper) { | ||
result.resolvedTrueType = trueType; | ||
result.resolvedFalseType = falseType; | ||
} | ||
result.trueType = trueType; | ||
result.falseType = falseType; | ||
result.aliasSymbol = root.aliasSymbol; | ||
result.aliasTypeArguments = instantiateTypes(root.aliasTypeArguments, mapper!); // TODO: GH#18217 | ||
return result; | ||
} | ||
|
||
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 getInferredTrueTypeFromConditionalType(type: ConditionalType) { | ||
return type.resolvedInferredTrueType || (type.resolvedInferredTrueType = instantiateType(type.root.trueType, type.combinedMapper || type.mapper)); | ||
} | ||
|
||
function getInferTypeParameters(node: ConditionalTypeNode): TypeParameter[] | undefined { | ||
|
@@ -11182,7 +11180,11 @@ namespace ts { | |
return getSubstitutionType(maybeVariable as TypeVariable, instantiateType((<SubstitutionType>type).substitute, mapper)); | ||
} | ||
else { | ||
return maybeVariable; | ||
const sub = instantiateType((<SubstitutionType>type).substitute, mapper); | ||
if (sub.flags & TypeFlags.AnyOrUnknown || isTypeSubtypeOf(getRestrictiveInstantiation(maybeVariable), getRestrictiveInstantiation(sub))) { | ||
weswigham marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return maybeVariable; | ||
} | ||
return sub; | ||
} | ||
} | ||
return type; | ||
|
@@ -11727,6 +11729,15 @@ namespace ts { | |
|
||
type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void; | ||
|
||
/** | ||
* Returns true if `s` is `(...args: any[]) => any` or `(this: any, ...args: any[]) => any` | ||
*/ | ||
function isAnySignature(s: Signature) { | ||
return !s.typeParameters && (!s.thisParameter || isTypeAny(getTypeOfParameter(s.thisParameter))) && s.parameters.length === 1 && | ||
s.hasRestParameter && (getTypeOfParameter(s.parameters[0]) === anyArrayType || isTypeAny(getTypeOfParameter(s.parameters[0]))) && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also allow for |
||
isTypeAny(getReturnTypeOfSignature(s)); | ||
} | ||
|
||
/** | ||
* See signatureRelatedTo, compareSignaturesIdentical | ||
*/ | ||
|
@@ -11742,6 +11753,10 @@ namespace ts { | |
return Ternary.True; | ||
} | ||
|
||
if (isAnySignature(target)) { | ||
return Ternary.True; | ||
} | ||
|
||
const targetCount = getParameterCount(target); | ||
if (!hasEffectiveRestParameter(target) && getMinArgumentCount(source) > targetCount) { | ||
return Ternary.False; | ||
|
@@ -12706,8 +12721,8 @@ namespace ts { | |
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)) { | ||
if (result &= isRelatedTo((<ConditionalType>source).trueType, (<ConditionalType>target).trueType, /*reportErrors*/ false)) { | ||
if (result &= isRelatedTo((<ConditionalType>source).falseType, (<ConditionalType>target).falseType, /*reportErrors*/ false)) { | ||
return result; | ||
} | ||
} | ||
|
@@ -12828,7 +12843,7 @@ namespace ts { | |
return result; | ||
} | ||
} | ||
const constraint = getConstraintOfType(<TypeParameter>source); | ||
const constraint = getConstraintOfType(<TypeVariable>source); | ||
if (!constraint || (source.flags & TypeFlags.TypeParameter && constraint.flags & TypeFlags.Any)) { | ||
// A type variable with no constraint is not related to the non-primitive object type. | ||
if (result = isRelatedTo(emptyObjectType, extractTypesOfKind(target, ~TypeFlags.NonPrimitive))) { | ||
|
@@ -12860,8 +12875,8 @@ namespace ts { | |
// and Y1 is related to Y2. | ||
if (isTypeIdenticalTo((<ConditionalType>source).extendsType, (<ConditionalType>target).extendsType) && | ||
(isRelatedTo((<ConditionalType>source).checkType, (<ConditionalType>target).checkType) || isRelatedTo((<ConditionalType>target).checkType, (<ConditionalType>source).checkType))) { | ||
if (result = isRelatedTo(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target), reportErrors)) { | ||
result &= isRelatedTo(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target), reportErrors); | ||
if (result = isRelatedTo((<ConditionalType>source).trueType, (<ConditionalType>target).trueType, reportErrors)) { | ||
result &= isRelatedTo((<ConditionalType>source).falseType, (<ConditionalType>target).falseType, reportErrors); | ||
} | ||
if (result) { | ||
errorInfo = saveErrorInfo; | ||
|
@@ -14696,12 +14711,12 @@ 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(getTrueTypeFromConditionalType(<ConditionalType>source), getTrueTypeFromConditionalType(<ConditionalType>target)); | ||
inferFromTypes(getFalseTypeFromConditionalType(<ConditionalType>source), getFalseTypeFromConditionalType(<ConditionalType>target)); | ||
inferFromTypes((<ConditionalType>source).trueType, (<ConditionalType>target).trueType); | ||
inferFromTypes((<ConditionalType>source).falseType, (<ConditionalType>target).falseType); | ||
} | ||
else if (target.flags & TypeFlags.Conditional && !contravariant) { | ||
inferFromTypes(source, getTrueTypeFromConditionalType(<ConditionalType>target)); | ||
inferFromTypes(source, getFalseTypeFromConditionalType(<ConditionalType>target)); | ||
inferFromTypes(source, (<ConditionalType>target).trueType); | ||
inferFromTypes(source, (<ConditionalType>target).falseType); | ||
} | ||
else if (target.flags & TypeFlags.UnionOrIntersection) { | ||
for (const t of (<UnionOrIntersectionType>target).types) { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought
<
angle brackets>
casting is meant to be phased out in favour ofas
casting?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The angle brackets were already here~