Make substitution types even if the substitution base isnt a type variable (#37348)

* Make substitution types even if the substitution base isnt a type variable

* Broaden usage of getConditionalFlowTypeOfType to _all_ type lookups

* Align comments
This commit is contained in:
Wesley Wigham
2020-03-13 11:02:11 -07:00
committed by GitHub
parent db44231e38
commit 47b60ece0b
8 changed files with 175 additions and 24 deletions

View File

@@ -4278,7 +4278,7 @@ namespace ts {
return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode);
}
if (type.flags & TypeFlags.Substitution) {
return typeToTypeNodeHelper((<SubstitutionType>type).typeVariable, context);
return typeToTypeNodeHelper((<SubstitutionType>type).baseType, context);
}
return Debug.fail("Should be unreachable.");
@@ -11327,9 +11327,7 @@ namespace ts {
// Get type from reference to named type that cannot be generic (enum or type parameter)
const res = tryGetDeclaredTypeOfSymbol(symbol);
if (res) {
return checkNoTypeArguments(node, symbol) ?
res.flags & TypeFlags.TypeParameter ? getConstrainedTypeVariable(<TypeParameter>res, node) : getRegularTypeOfLiteralType(res) :
errorType;
return checkNoTypeArguments(node, symbol) ? getRegularTypeOfLiteralType(res) : errorType;
}
if (symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node)) {
const jsdocType = getTypeFromJSDocValueReference(node, symbol);
@@ -11377,17 +11375,17 @@ namespace ts {
return links.resolvedJSDocType;
}
function getSubstitutionType(typeVariable: TypeVariable, substitute: Type) {
if (substitute.flags & TypeFlags.AnyOrUnknown || substitute === typeVariable) {
return typeVariable;
function getSubstitutionType(baseType: Type, substitute: Type) {
if (substitute.flags & TypeFlags.AnyOrUnknown || substitute === baseType) {
return baseType;
}
const id = `${getTypeId(typeVariable)}>${getTypeId(substitute)}`;
const id = `${getTypeId(baseType)}>${getTypeId(substitute)}`;
const cached = substitutionTypes.get(id);
if (cached) {
return cached;
}
const result = <SubstitutionType>createType(TypeFlags.Substitution);
result.typeVariable = typeVariable;
result.baseType = baseType;
result.substitute = substitute;
substitutionTypes.set(id, result);
return result;
@@ -11397,25 +11395,25 @@ namespace ts {
return node.kind === SyntaxKind.TupleType && (<TupleTypeNode>node).elementTypes.length === 1;
}
function getImpliedConstraint(typeVariable: TypeVariable, checkNode: TypeNode, extendsNode: TypeNode): Type | undefined {
return isUnaryTupleTypeNode(checkNode) && isUnaryTupleTypeNode(extendsNode) ? getImpliedConstraint(typeVariable, (<TupleTypeNode>checkNode).elementTypes[0], (<TupleTypeNode>extendsNode).elementTypes[0]) :
getActualTypeVariable(getTypeFromTypeNode(checkNode)) === typeVariable ? getTypeFromTypeNode(extendsNode) :
function getImpliedConstraint(type: Type, checkNode: TypeNode, extendsNode: TypeNode): Type | undefined {
return isUnaryTupleTypeNode(checkNode) && isUnaryTupleTypeNode(extendsNode) ? getImpliedConstraint(type, (<TupleTypeNode>checkNode).elementTypes[0], (<TupleTypeNode>extendsNode).elementTypes[0]) :
getActualTypeVariable(getTypeFromTypeNode(checkNode)) === type ? getTypeFromTypeNode(extendsNode) :
undefined;
}
function getConstrainedTypeVariable(typeVariable: TypeVariable, node: Node) {
function getConditionalFlowTypeOfType(type: Type, node: Node) {
let constraints: Type[] | undefined;
while (node && !isStatement(node) && node.kind !== SyntaxKind.JSDocComment) {
const parent = node.parent;
if (parent.kind === SyntaxKind.ConditionalType && node === (<ConditionalTypeNode>parent).trueType) {
const constraint = getImpliedConstraint(typeVariable, (<ConditionalTypeNode>parent).checkType, (<ConditionalTypeNode>parent).extendsType);
const constraint = getImpliedConstraint(type, (<ConditionalTypeNode>parent).checkType, (<ConditionalTypeNode>parent).extendsType);
if (constraint) {
constraints = append(constraints, constraint);
}
}
node = parent;
}
return constraints ? getSubstitutionType(typeVariable, getIntersectionType(append(constraints, typeVariable))) : typeVariable;
return constraints ? getSubstitutionType(type, getIntersectionType(append(constraints, type))) : type;
}
function isJSDocTypeReference(node: Node): node is TypeReferenceNode {
@@ -12858,7 +12856,7 @@ namespace ts {
links.resolvedType = resolved.flags & TypeFlags.IndexedAccess &&
(<IndexedAccessType>resolved).objectType === objectType &&
(<IndexedAccessType>resolved).indexType === indexType ?
getConstrainedTypeVariable(<IndexedAccessType>resolved, node) : resolved;
getConditionalFlowTypeOfType(resolved, node) : resolved;
}
return links.resolvedType;
}
@@ -12880,7 +12878,7 @@ namespace ts {
function getActualTypeVariable(type: Type): Type {
if (type.flags & TypeFlags.Substitution) {
return (<SubstitutionType>type).typeVariable;
return (<SubstitutionType>type).baseType;
}
if (type.flags & TypeFlags.IndexedAccess && (
(<IndexedAccessType>type).objectType.flags & TypeFlags.Substitution ||
@@ -13428,6 +13426,10 @@ namespace ts {
}
function getTypeFromTypeNode(node: TypeNode): Type {
return getConditionalFlowTypeOfType(getTypeFromTypeNodeWorker(node), node);
}
function getTypeFromTypeNodeWorker(node: TypeNode): Type {
switch (node.kind) {
case SyntaxKind.AnyKeyword:
case SyntaxKind.JSDocAllType:
@@ -13775,7 +13777,7 @@ namespace ts {
return !!tp.isThisType;
case SyntaxKind.Identifier:
return !tp.isThisType && isPartOfTypeNode(node) && maybeTypeParameterReference(node) &&
getTypeFromTypeNode(<TypeNode>node) === tp;
getTypeFromTypeNodeWorker(<TypeNode>node) === tp; // use worker because we're looking for === equality
case SyntaxKind.TypeQuery:
return true;
}
@@ -13976,7 +13978,7 @@ namespace ts {
return getConditionalTypeInstantiation(<ConditionalType>type, combineTypeMappers((<ConditionalType>type).mapper, mapper));
}
if (flags & TypeFlags.Substitution) {
const maybeVariable = instantiateType((<SubstitutionType>type).typeVariable, mapper);
const maybeVariable = instantiateType((<SubstitutionType>type).baseType, mapper);
if (maybeVariable.flags & TypeFlags.TypeVariable) {
return getSubstitutionType(maybeVariable as TypeVariable, instantiateType((<SubstitutionType>type).substitute, mapper));
}
@@ -14985,7 +14987,7 @@ namespace ts {
const t = isFreshLiteralType(type) ? (<FreshableType>type).regularType :
getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).node ? createTypeReference((<TypeReference>type).target, getTypeArguments(<TypeReference>type)) :
type.flags & TypeFlags.UnionOrIntersection ? getReducedType(type) :
type.flags & TypeFlags.Substitution ? writing ? (<SubstitutionType>type).typeVariable : (<SubstitutionType>type).substitute :
type.flags & TypeFlags.Substitution ? writing ? (<SubstitutionType>type).baseType : (<SubstitutionType>type).substitute :
type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) :
type;
if (t === type) break;

View File

@@ -4808,8 +4808,8 @@ namespace ts {
// Thus, if Foo has a 'string' constraint on its type parameter, T will satisfy it. Substitution
// types disappear upon instantiation (just like type parameters).
export interface SubstitutionType extends InstantiableType {
typeVariable: TypeVariable; // Target type variable
substitute: Type; // Type to substitute for type parameter
baseType: Type; // Target type
substitute: Type; // Type to substitute for type parameter
}
/* @internal */