mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-19 10:41:56 -05:00
Introduce substitution types to use for constrained type parameters
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user