Introduce substitution types to use for constrained type parameters

This commit is contained in:
Anders Hejlsberg
2018-01-15 11:06:18 -08:00
parent 925da86496
commit e8d1740da8
2 changed files with 57 additions and 9 deletions

View File

@@ -2648,6 +2648,9 @@ namespace ts {
const falseTypeNode = typeToTypeNodeHelper((<ConditionalType>type).falseType, context);
return createConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode);
}
if (type.flags & TypeFlags.Substitution) {
return typeToTypeNodeHelper((<SubstitutionType>type).typeParameter, context);
}
Debug.fail("Should be unreachable.");
@@ -3431,6 +3434,9 @@ namespace ts {
writeSpace(writer);
writeType((<ConditionalType>type).falseType, TypeFormatFlags.InElementType);
}
else if (type.flags & TypeFlags.Substitution) {
writeType((<SubstitutionType>type).typeParameter, TypeFormatFlags.None);
}
else {
// Should never get here
// { ... }
@@ -6480,6 +6486,9 @@ namespace ts {
if (t.flags & TypeFlags.Conditional) {
return getBaseConstraint(getConstraintOfConditionalType(<ConditionalType>t));
}
if (t.flags & TypeFlags.Substitution) {
return getBaseConstraint((<SubstitutionType>t).substitute);
}
if (isGenericMappedType(t)) {
return emptyObjectType;
}
@@ -7458,7 +7467,7 @@ namespace ts {
error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol));
return unknownType;
}
return res;
return res.flags & TypeFlags.TypeParameter ? getConstrainedTypeParameter(<TypeParameter>res, node) : res;
}
if (!(symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node))) {
@@ -7497,6 +7506,26 @@ namespace ts {
}
}
function getConstrainedTypeParameter(typeParameter: TypeParameter, node: Node) {
let constraints: Type[];
while (isTypeNode(node)) {
const parent = node.parent;
if (parent.kind === SyntaxKind.ConditionalType && node === (<ConditionalTypeNode>parent).trueType) {
if (getTypeFromTypeNode((<ConditionalTypeNode>parent).checkType) === typeParameter) {
constraints = append(constraints, getTypeFromTypeNode((<ConditionalTypeNode>parent).extendsType));
}
}
node = parent;
}
if (constraints) {
const result = <SubstitutionType>createType(TypeFlags.Substitution);
result.typeParameter = typeParameter;
result.substitute = getIntersectionType(append(constraints, typeParameter));
return result;
}
return typeParameter;
}
function isJSDocTypeReference(node: TypeReferenceType): node is TypeReferenceNode {
return node.flags & NodeFlags.JSDoc && node.kind === SyntaxKind.TypeReference;
}
@@ -8395,13 +8424,14 @@ namespace ts {
return instantiateType(falseType, mapper);
}
// Otherwise return a deferred conditional type
const resCheckType = checkType.flags & TypeFlags.Substitution ? (<SubstitutionType>checkType).typeParameter : checkType;
const resTrueType = instantiateType(trueType, mapper);
const resFalseType = instantiateType(falseType, mapper);
const resTypeArguments = instantiateTypes(aliasTypeArguments, mapper);
const id = checkType.id + "," + extendsType.id + "," + resTrueType.id + "," + resFalseType.id;
const id = resCheckType.id + "," + extendsType.id + "," + resTrueType.id + "," + resFalseType.id;
let type = conditionalTypes.get(id);
if (!type) {
conditionalTypes.set(id, type = createConditionalType(checkType, extendsType, resTrueType, resFalseType, aliasSymbol, resTypeArguments));
conditionalTypes.set(id, type = createConditionalType(resCheckType, extendsType, resTrueType, resFalseType, aliasSymbol, resTypeArguments));
}
return type;
}
@@ -9034,6 +9064,9 @@ namespace ts {
if (type.flags & TypeFlags.Conditional) {
return getConditionalTypeInstantiation(<ConditionalType>type, mapper);
}
if (type.flags & TypeFlags.Substitution) {
return instantiateType((<SubstitutionType>type).typeParameter, mapper);
}
}
return type;
}
@@ -9619,6 +9652,13 @@ namespace ts {
if (target.flags & TypeFlags.StringOrNumberLiteral && target.flags & TypeFlags.FreshLiteral) {
target = (<LiteralType>target).regularType;
}
if (source.flags & TypeFlags.Substitution) {
source = (<SubstitutionType>source).substitute;
}
if (target.flags & TypeFlags.Substitution) {
target = (<SubstitutionType>target).typeParameter;
}
// both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases
if (source === target) return Ternary.True;

View File

@@ -3401,15 +3401,16 @@ namespace ts {
Index = 1 << 19, // keyof T
IndexedAccess = 1 << 20, // T[K]
Conditional = 1 << 21, // T extends U ? X : Y
Substitution = 1 << 22, // Type parameter substitution
/* @internal */
FreshLiteral = 1 << 22, // Fresh literal or unique type
FreshLiteral = 1 << 23, // Fresh literal or unique type
/* @internal */
ContainsWideningType = 1 << 23, // Type is or contains undefined or null widening type
ContainsWideningType = 1 << 24, // Type is or contains undefined or null widening type
/* @internal */
ContainsObjectLiteral = 1 << 24, // Type is or contains object literal type
ContainsObjectLiteral = 1 << 25, // Type is or contains object literal type
/* @internal */
ContainsAnyFunctionType = 1 << 25, // Type is or contains the anyFunctionType
NonPrimitive = 1 << 26, // intrinsic object type
ContainsAnyFunctionType = 1 << 26, // Type is or contains the anyFunctionType
NonPrimitive = 1 << 27, // intrinsic object type
/* @internal */
GenericMappedType = 1 << 29, // Flag used by maybeTypeOfKind
@@ -3435,7 +3436,7 @@ namespace ts {
UnionOrIntersection = Union | Intersection,
StructuredType = Object | Union | Intersection,
TypeVariable = TypeParameter | IndexedAccess,
InstantiableNonPrimitive = TypeVariable | Conditional,
InstantiableNonPrimitive = TypeVariable | Conditional | Substitution,
InstantiablePrimitive = Index,
Instantiable = InstantiableNonPrimitive | InstantiablePrimitive,
StructuredOrInstantiable = StructuredType | Instantiable,
@@ -3693,6 +3694,7 @@ namespace ts {
type: InstantiableType | UnionOrIntersectionType;
}
// T extends U ? X : Y (TypeFlags.Conditional)
export interface ConditionalType extends InstantiableType {
checkType: Type;
extendsType: Type;
@@ -3700,6 +3702,12 @@ namespace ts {
falseType: Type;
}
// Type parameter substitution (TypeFlags.Substitution)
export interface SubstitutionType extends InstantiableType {
typeParameter: TypeParameter; // Target type parameter
substitute: Type; // Type to substitute for type parameter
}
export const enum SignatureKind {
Call,
Construct,