|
// https://github.com/microsoft/TypeScript/blob/ba5e86f1406f39e89d56d4b32fd6ff8de09a0bf3/src/compiler/checker.ts |
|
|
|
// 1. add this line to ln:3 |
|
|
|
export const _conditionalTypes: any = {} |
|
|
|
// 2. then replace ln:12303 to ln:12360 |
|
|
|
function trackConditionalType() { |
|
// one time stuff |
|
if (_conditionalTypes._callStack === undefined) _conditionalTypes._callStack = [] |
|
if (_conditionalTypes._results === undefined) _conditionalTypes._results = [] |
|
|
|
const top: any[] = [] |
|
_conditionalTypes._callStack.push(top) |
|
} |
|
|
|
function pushTop(index: number, elm: any) { |
|
const top = _conditionalTypes._callStack[_conditionalTypes._callStack.length - 1] |
|
if (_conditionalTypes._fetchMe) { |
|
top[index] = _conditionalTypes._fetchMe |
|
_conditionalTypes._fetchMe = null |
|
} else { |
|
top[index] = elm |
|
} |
|
return elm |
|
} |
|
|
|
function untrackConditionalType() { |
|
const popped = _conditionalTypes._callStack.pop() |
|
_conditionalTypes._fetchMe = popped |
|
if (_conditionalTypes._callStack.length === 0) { |
|
_conditionalTypes._results.push(_conditionalTypes._fetchMe) |
|
_conditionalTypes._fetchMe = null |
|
} |
|
} |
|
|
|
function getConditionalType(root: ConditionalRoot, mapper: TypeMapper | undefined): Type { |
|
try { |
|
trackConditionalType() |
|
return _getConditionalType(root, mapper) |
|
} finally { |
|
untrackConditionalType() |
|
} |
|
} |
|
|
|
function _getConditionalType(root: ConditionalRoot, mapper: TypeMapper | undefined): Type { |
|
const checkType = instantiateType(root.checkType, mapper); |
|
pushTop(0, checkType) |
|
|
|
const extendsType = instantiateType(root.extendsType, mapper); |
|
pushTop(1, extendsType) |
|
|
|
if (checkType === wildcardType || extendsType === wildcardType) { |
|
return wildcardType; |
|
} |
|
const checkTypeInstantiable = maybeTypeOfKind(checkType, TypeFlags.Instantiable | TypeFlags.GenericMappedType); |
|
let combinedMapper: TypeMapper | undefined; |
|
if (root.inferTypeParameters) { |
|
const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None); |
|
if (!checkTypeInstantiable) { |
|
// 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(context.inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict); |
|
} |
|
combinedMapper = combineTypeMappers(mapper, context.mapper); |
|
} |
|
// Instantiate the extends type including inferences for 'infer T' type parameters |
|
const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType; |
|
// We attempt to resolve the conditional type only when the check and extends types are non-generic |
|
if (!checkTypeInstantiable && !maybeTypeOfKind(inferredExtendsType, TypeFlags.Instantiable | TypeFlags.GenericMappedType)) { |
|
if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown) { |
|
return pushTop(2, instantiateType(root.trueType, combinedMapper || mapper)); |
|
} |
|
// Return union of trueType and falseType for 'any' since it matches anything |
|
if (checkType.flags & TypeFlags.Any) { |
|
return pushTop(4, getUnionType([instantiateType(root.trueType, combinedMapper || mapper), instantiateType(root.falseType, mapper)]) ); |
|
} |
|
// Return falseType for a definitely false extends check. We check an instantiations 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 instantiations will be and we can just return the false branch type. |
|
if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) { |
|
return pushTop(3, instantiateType(root.falseType, mapper)); |
|
} |
|
// Return trueType for a definitely true extends check. We check instantiations of the two |
|
// types with type parameters mapped to their restrictive form, i.e. a form of the type parameter |
|
// that has no constraint. This ensures that, for example, the type |
|
// type Foo<T extends { x: any }> = T extends { x: string } ? string : number |
|
// doesn't immediately resolve to 'string' instead of being deferred. |
|
if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(inferredExtendsType))) { |
|
return pushTop(2, instantiateType(root.trueType, combinedMapper || mapper)); |
|
} |
|
} |
|
// Return a deferred type for a check that is neither definitely true nor definitely false |
|
const erasedCheckType = getActualTypeVariable(checkType); |
|
const result = <ConditionalType>createType(TypeFlags.Conditional); |
|
result.root = root; |
|
result.checkType = erasedCheckType; |
|
result.extendsType = extendsType; |
|
result.mapper = mapper; |
|
result.combinedMapper = combinedMapper; |
|
result.aliasSymbol = root.aliasSymbol; |
|
result.aliasTypeArguments = instantiateTypes(root.aliasTypeArguments, mapper!); // TODO: GH#18217 |
|
pushTop(5, result); |
|
return result; |
|
} |