Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,8 @@ func (n *Node) TypeParameterList() *NodeList {
return n.AsTypeAliasDeclaration().TypeParameters
case KindJSDocTemplateTag:
return n.AsJSDocTemplateTag().TypeParameters
case KindQuantifiedType:
return n.AsQuantifiedTypeNode().TypeParameters
default:
funcLike := n.FunctionLikeData()
if funcLike != nil {
Expand Down Expand Up @@ -1750,6 +1752,10 @@ func (n *Node) AsConstructorTypeNode() *ConstructorTypeNode {
return n.data.(*ConstructorTypeNode)
}

func (n *Node) AsQuantifiedTypeNode() *QuantifiedTypeNode {
return n.data.(*QuantifiedTypeNode)
}

func (n *Node) AsTypeQueryNode() *TypeQueryNode {
return n.data.(*TypeQueryNode)
}
Expand Down Expand Up @@ -8871,6 +8877,41 @@ func IsTemplateLiteralTypeSpan(node *Node) bool {
return node.Kind == KindTemplateLiteralTypeSpan
}

// QuantifiedTypeNode

type QuantifiedTypeNode struct {
TypeNodeBase
LocalsContainerBase
TypeParameters *NodeList // NodeList[*TypeParameterDeclarationNode]
BaseType *TypeNode
}

func (f *NodeFactory) NewQuantifiedTypeNode(typeParameters *NodeList, baseTypeNode *TypeNode) *Node {
data := &QuantifiedTypeNode{}
data.TypeParameters = typeParameters
data.BaseType = baseTypeNode
return f.newNode(KindQuantifiedType, data)
}

func (f *NodeFactory) UpdateQuantifiedTypeNode(node *QuantifiedTypeNode, typeParameters *NodeList, baseTypeNode *TypeNode) *Node {
if typeParameters != node.TypeParameters || baseTypeNode != node.BaseType {
return updateNode(f.NewQuantifiedTypeNode(typeParameters, baseTypeNode), node.AsNode(), f.hooks)
}
return node.AsNode()
}

func (node *QuantifiedTypeNode) ForEachChild(v Visitor) bool {
return visitNodeList(v, node.TypeParameters) || visit(v, node.BaseType)
}

func (node *QuantifiedTypeNode) VisitEachChild(v *NodeVisitor) *Node {
return v.Factory.UpdateQuantifiedTypeNode(node, v.visitNodes(node.TypeParameters), v.visitNode(node.BaseType))
}

func (node *QuantifiedTypeNode) Clone(f NodeFactoryCoercible) *Node {
return cloneNode(f.AsNodeFactory().NewQuantifiedTypeNode(node.TypeParameters, node.BaseType), node.AsNode(), f.AsNodeFactory().hooks)
}

// SyntheticExpression

type SyntheticExpression struct {
Expand Down
1 change: 1 addition & 0 deletions internal/ast/kind.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ const (
KindNamedTupleMember
KindTemplateLiteralType
KindTemplateLiteralTypeSpan
KindQuantifiedType
KindImportType
// Binding patterns
KindObjectBindingPattern
Expand Down
303 changes: 152 additions & 151 deletions internal/ast/kind_stringer_generated.go

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion internal/ast/precedence.go
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,8 @@ const (
// Gets the precedence of a TypeNode
func GetTypeNodePrecedence(n *TypeNode) TypePrecedence {
switch n.Kind {
case KindConditionalType:
case KindConditionalType,
KindQuantifiedType:
return TypePrecedenceConditional
case KindJSDocOptionalType, KindJSDocVariadicType:
return TypePrecedenceJSDoc
Expand Down
5 changes: 3 additions & 2 deletions internal/binder/binder.go
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,8 @@ func (b *Binder) declareSymbolAndAddToSymbolTable(node *ast.Node, symbolFlags as
case ast.KindFunctionType, ast.KindConstructorType, ast.KindCallSignature, ast.KindConstructSignature,
ast.KindIndexSignature, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor, ast.KindGetAccessor,
ast.KindSetAccessor, ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindArrowFunction,
ast.KindClassStaticBlockDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindMappedType:
ast.KindClassStaticBlockDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindMappedType,
ast.KindQuantifiedType:
return b.declareSymbol(ast.GetLocals(b.container), nil /*parent*/, node, symbolFlags, symbolExcludes)
}
panic("Unhandled case in declareSymbolAndAddToSymbolTable")
Expand Down Expand Up @@ -2467,7 +2468,7 @@ func GetContainerFlags(node *ast.Node) ContainerFlags {
return ContainerFlagsIsContainer
case ast.KindInterfaceDeclaration:
return ContainerFlagsIsContainer | ContainerFlagsIsInterface
case ast.KindModuleDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindMappedType, ast.KindIndexSignature:
case ast.KindModuleDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindMappedType, ast.KindIndexSignature, ast.KindQuantifiedType:
return ContainerFlagsIsContainer | ContainerFlagsHasLocals
case ast.KindSourceFile:
return ContainerFlagsIsContainer | ContainerFlagsIsControlFlowContainer | ContainerFlagsHasLocals
Expand Down
173 changes: 158 additions & 15 deletions internal/checker/checker.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion internal/checker/exports.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func (c *Checker) HasEffectiveRestParameter(signature *Signature) bool {
}

func (c *Checker) GetLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol *ast.Symbol) []*Type {
return c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
return c.getLocalTypeParametersOfClassOrInterfaceOrTypeAliasOrQuantifiedType(symbol)
}

func (c *Checker) GetContextualTypeForObjectLiteralElement(element *ast.Node, contextFlags ContextFlags) *Type {
Expand Down
2 changes: 1 addition & 1 deletion internal/checker/jsx.go
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,7 @@ func (c *Checker) getJsxPropsTypeFromClassType(sig *Signature, context *ast.Node
apparentAttributesType := attributesType
intrinsicClassAttribs := c.getJsxType(JsxNames.IntrinsicClassAttributes, context)
if !c.isErrorType(intrinsicClassAttribs) {
typeParams := c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(intrinsicClassAttribs.symbol)
typeParams := c.getLocalTypeParametersOfClassOrInterfaceOrTypeAliasOrQuantifiedType(intrinsicClassAttribs.symbol)
hostClassType := c.getReturnTypeOfSignature(sig)
var libraryManagedAttributeType *Type
if typeParams != nil {
Expand Down
14 changes: 12 additions & 2 deletions internal/checker/nodebuilderimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -897,7 +897,7 @@ func (b *NodeBuilderImpl) getNameOfSymbolAsWritten(symbol *ast.Symbol) string {
func (b *NodeBuilderImpl) getTypeParametersOfClassOrInterface(symbol *ast.Symbol) []*Type {
result := make([]*Type, 0)
result = append(result, b.ch.getOuterTypeParametersOfClassOrInterface(symbol)...)
result = append(result, b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)...)
result = append(result, b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAliasOrQuantifiedType(symbol)...)
return result
}

Expand Down Expand Up @@ -1518,7 +1518,7 @@ func (b *NodeBuilderImpl) typeParametersToTypeParameterDeclarations(symbol *ast.
targetSymbol := b.ch.getTargetSymbol(symbol)
if targetSymbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface|ast.SymbolFlagsAlias) != 0 {
var results []*ast.Node
params := b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
params := b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAliasOrQuantifiedType(symbol)
for _, param := range params {
results = append(results, b.typeParameterToDeclaration(param))
}
Expand Down Expand Up @@ -3091,6 +3091,16 @@ func (b *NodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode {
return typeNode
}
}
if t.flags&TypeFlagsQuantified != 0 {
return b.f.NewQuantifiedTypeNode(
b.f.NewNodeList(
core.Map(t.AsQuantifiedType().typeParameters, func(typeParamter *TypeParameter) *ast.Node {
return b.typeParameterToDeclaration(typeParamter.AsType())
}),
),
b.typeToTypeNode(t.AsQuantifiedType().baseType),
)
}

panic("Should be unreachable.")
}
Expand Down
71 changes: 65 additions & 6 deletions internal/checker/relater.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,31 @@ func (c *Checker) areTypesComparable(type1 *Type, type2 *Type) bool {
}

func (c *Checker) isTypeRelatedTo(source *Type, target *Type, relation *Relation) bool {
if target.flags&TypeFlagsQuantified != 0 {
if source.flags&TypeFlagsQuantified != 0 {
return source == target
}
baseType := target.AsQuantifiedType().baseType
typeParameters := core.Map(target.AsQuantifiedType().typeParameters, func(t *TypeParameter) *Type { return t.AsType() })

if source.flags&TypeFlagsUnion == 0 {
inferenceContext := c.newInferenceContext(typeParameters, nil, InferenceFlagsNone, nil)
c.inferTypes(inferenceContext.inferences, source, baseType, InferencePriorityNone, false)
return c.isTypeRelatedTo(source, c.instantiateType(baseType, inferenceContext.mapper), relation)
}

for _, sourceMember := range source.AsUnionType().types {
inferenceContext := c.newInferenceContext(typeParameters, nil, InferenceFlagsNone, nil)
c.inferTypes(inferenceContext.inferences, sourceMember, baseType, InferencePriorityNone, false)
result := c.isTypeRelatedTo(sourceMember, c.instantiateType(baseType, inferenceContext.mapper), relation)
if result {
continue
}
return result
}

return true
}
if isFreshLiteralType(source) {
source = source.AsLiteralType().regularType
}
Expand Down Expand Up @@ -2553,6 +2578,32 @@ func (r *Relater) isRelatedTo(source *Type, target *Type, recursionFlags Recursi
}

func (r *Relater) isRelatedToEx(originalSource *Type, originalTarget *Type, recursionFlags RecursionFlags, reportErrors bool, headMessage *diagnostics.Message, intersectionState IntersectionState) Ternary {
if originalTarget.flags&TypeFlagsQuantified != 0 {
if originalSource.flags&TypeFlagsQuantified != 0 && originalSource == originalTarget {
return TernaryTrue
}
baseType := originalTarget.AsQuantifiedType().baseType
typeParameters := core.Map(originalTarget.AsQuantifiedType().typeParameters, func(t *TypeParameter) *Type { return t.AsType() })

if originalSource.flags&TypeFlagsUnion == 0 {
inferenceContext := r.c.newInferenceContext(typeParameters, nil, InferenceFlagsNone, nil)
r.c.inferTypes(inferenceContext.inferences, originalSource, baseType, InferencePriorityNone, false)
return r.isRelatedToEx(originalSource, r.c.instantiateType(baseType, inferenceContext.mapper), recursionFlags, reportErrors, headMessage, intersectionState)
}

for _, originalSourceMember := range originalSource.AsUnionType().types {
inferenceContext := r.c.newInferenceContext(typeParameters, nil, InferenceFlagsNone, nil)
r.c.inferTypes(inferenceContext.inferences, originalSourceMember, baseType, InferencePriorityNone, false)
result := r.isRelatedToEx(originalSourceMember, r.c.instantiateType(baseType, inferenceContext.mapper), recursionFlags, reportErrors, headMessage, intersectionState)
if result == TernaryTrue {
continue
}
return result
}

return TernaryTrue
}

if originalSource == originalTarget {
return TernaryTrue
}
Expand Down Expand Up @@ -4672,12 +4723,17 @@ func (r *Relater) reportErrorResults(originalSource *Type, originalTarget *Type,
}
}
r.reportRelationError(headMessage, source, target)
if source.flags&TypeFlagsTypeParameter != 0 && source.symbol != nil && len(source.symbol.Declarations) != 0 && r.c.getConstraintOfType(source) == nil {
syntheticParam := r.c.cloneTypeParameter(source)
syntheticParam.AsTypeParameter().constraint = r.c.instantiateType(target, newSimpleTypeMapper(source, syntheticParam))
if r.c.hasNonCircularBaseConstraint(syntheticParam) {
targetConstraintString := r.c.TypeToString(target)
r.relatedInfo = append(r.relatedInfo, NewDiagnosticForNode(source.symbol.Declarations[0], diagnostics.This_type_parameter_might_need_an_extends_0_constraint, targetConstraintString))
if source.objectFlags&ObjectFlagsQuantifiedTypeParameter != 0 && target.objectFlags&ObjectFlagsQuantifiedTypeParameter != 0 {
r.relatedInfo = append(r.relatedInfo, NewDiagnosticForNode(source.AsTypeParameter().boundedTo.Declarations[0], diagnostics.Type_parameter_0_is_bounded_to_this_variable, r.c.TypeToString(source)))
r.relatedInfo = append(r.relatedInfo, NewDiagnosticForNode(target.AsTypeParameter().boundedTo.Declarations[0], diagnostics.Type_parameter_0_is_bounded_to_this_variable, r.c.TypeToString(target)))
} else {
if source.flags&TypeFlagsTypeParameter != 0 && source.symbol != nil && len(source.symbol.Declarations) != 0 && r.c.getConstraintOfType(source) == nil {
syntheticParam := r.c.cloneTypeParameter(source)
syntheticParam.AsTypeParameter().constraint = r.c.instantiateType(target, newSimpleTypeMapper(source, syntheticParam))
if r.c.hasNonCircularBaseConstraint(syntheticParam) {
targetConstraintString := r.c.TypeToString(target)
r.relatedInfo = append(r.relatedInfo, NewDiagnosticForNode(source.symbol.Declarations[0], diagnostics.This_type_parameter_might_need_an_extends_0_constraint, targetConstraintString))
}
}
}
}
Expand Down Expand Up @@ -4712,6 +4768,9 @@ func (r *Relater) reportRelationError(message *diagnostics.Message, source *Type
r.reportError(diagnostics.X_0_could_be_instantiated_with_an_arbitrary_type_which_could_be_unrelated_to_1, targetType, generalizedSourceType)
}
}
if target.objectFlags&ObjectFlagsQuantifiedTypeParameter != 0 && generalizedSource.objectFlags&ObjectFlagsQuantifiedTypeParameter != 0 {
r.reportError(diagnostics.Both_type_parameters_are_bounded_to_different_variables)
}
if message == nil {
switch {
case r.relation == r.c.comparableRelation:
Expand Down
17 changes: 14 additions & 3 deletions internal/checker/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ type SignatureLinks struct {
decoratorSignature *Signature // Signature for decorator as if invoked by the runtime
}

type TypeFlags uint32
type TypeFlags uint64

// Note that for types of different kinds, the numeric values of TypeFlags determine the order
// computed by the CompareTypes function and therefore the order of constituent types in union types.
Expand Down Expand Up @@ -421,6 +421,7 @@ const (
TypeFlagsReserved1 TypeFlags = 1 << 29 // Used by union/intersection type construction
TypeFlagsReserved2 TypeFlags = 1 << 30 // Used by union/intersection type construction
TypeFlagsReserved3 TypeFlags = 1 << 31
TypeFlagsQuantified TypeFlags = 1 << 32

TypeFlagsAnyOrUnknown = TypeFlagsAny | TypeFlagsUnknown
TypeFlagsNullable = TypeFlagsUndefined | TypeFlagsNull
Expand All @@ -445,7 +446,7 @@ const (
TypeFlagsUnionOrIntersection = TypeFlagsUnion | TypeFlagsIntersection
TypeFlagsStructuredType = TypeFlagsObject | TypeFlagsUnion | TypeFlagsIntersection
TypeFlagsTypeVariable = TypeFlagsTypeParameter | TypeFlagsIndexedAccess
TypeFlagsInstantiableNonPrimitive = TypeFlagsTypeVariable | TypeFlagsConditional | TypeFlagsSubstitution
TypeFlagsInstantiableNonPrimitive = TypeFlagsTypeVariable | TypeFlagsConditional | TypeFlagsSubstitution | TypeFlagsQuantified
TypeFlagsInstantiablePrimitive = TypeFlagsIndex | TypeFlagsTemplateLiteral | TypeFlagsStringMapping
TypeFlagsInstantiable = TypeFlagsInstantiableNonPrimitive | TypeFlagsInstantiablePrimitive
TypeFlagsStructuredOrInstantiable = TypeFlagsStructuredType | TypeFlagsInstantiable
Expand Down Expand Up @@ -497,10 +498,12 @@ const (
ObjectFlagsCouldContainTypeVariablesComputed ObjectFlags = 1 << 19 // CouldContainTypeVariables flag has been computed
ObjectFlagsCouldContainTypeVariables ObjectFlags = 1 << 20 // Type could contain a type variable
ObjectFlagsMembersResolved ObjectFlags = 1 << 21 // Members have been resolved
ObjectFlagsContainsQuantifiedType ObjectFlags = 1 << 22
ObjectFlagsQuantifiedTypeParameter ObjectFlags = 1 << 23

ObjectFlagsClassOrInterface = ObjectFlagsClass | ObjectFlagsInterface
ObjectFlagsRequiresWidening = ObjectFlagsContainsWideningType | ObjectFlagsContainsObjectOrArrayLiteral
ObjectFlagsPropagatingFlags = ObjectFlagsContainsWideningType | ObjectFlagsContainsObjectOrArrayLiteral | ObjectFlagsNonInferrableType
ObjectFlagsPropagatingFlags = ObjectFlagsContainsWideningType | ObjectFlagsContainsObjectOrArrayLiteral | ObjectFlagsNonInferrableType | ObjectFlagsContainsQuantifiedType
ObjectFlagsInstantiatedMapped = ObjectFlagsMapped | ObjectFlagsInstantiated
// Object flags that uniquely identify the kind of ObjectType
ObjectFlagsObjectTypeKindMask = ObjectFlagsClassOrInterface | ObjectFlagsReference | ObjectFlagsTuple | ObjectFlagsAnonymous | ObjectFlagsMapped | ObjectFlagsReverseMapped | ObjectFlagsEvolvingArray | ObjectFlagsInstantiationExpressionType | ObjectFlagsSingleSignatureType
Expand Down Expand Up @@ -594,6 +597,7 @@ func (t *Type) AsTemplateLiteralType() *TemplateLiteralType { return t.data.(*Te
func (t *Type) AsStringMappingType() *StringMappingType { return t.data.(*StringMappingType) }
func (t *Type) AsSubstitutionType() *SubstitutionType { return t.data.(*SubstitutionType) }
func (t *Type) AsConditionalType() *ConditionalType { return t.data.(*ConditionalType) }
func (t *Type) AsQuantifiedType() *QuantifiedType { return t.data.(*QuantifiedType) }

// Casts for embedded struct types

Expand Down Expand Up @@ -1028,6 +1032,7 @@ type TypeParameter struct {
mapper *TypeMapper
isThisType bool
resolvedDefaultType *Type
boundedTo *ast.Symbol
}

// IndexFlags
Expand Down Expand Up @@ -1100,6 +1105,12 @@ type ConditionalType struct {
combinedMapper *TypeMapper
}

type QuantifiedType struct {
ConstrainedType
typeParameters []*TypeParameter
baseType *Type
}

// SignatureFlags

type SignatureFlags uint32
Expand Down
8 changes: 8 additions & 0 deletions internal/diagnostics/diagnostics_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions internal/diagnostics/extraDiagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,13 @@
"Option '{0}' requires value to be greater than '{1}'.": {
"category": "Error",
"code": 5002
},
"Both type parameters are bounded to different variables": {
"category": "Error",
"code": 5113
},
"Type parameter '{0}' is bounded to this variable": {
"category": "Error",
"code": 5114
}
}
15 changes: 15 additions & 0 deletions internal/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2480,6 +2480,21 @@ func (p *Parser) parseType() *ast.TypeNode {
saveContextFlags := p.contextFlags
p.setContextFlags(ast.NodeFlagsTypeExcludesFlags, false)
var typeNode *ast.TypeNode
if p.token == ast.KindLessThanToken {
state := p.mark()
p.parseFunctionOrConstructorType()
couldParseFunctionType := len(p.diagnostics) == state.diagnosticsLen
// TODO: see if there's a more standard way to do "try" parse
p.rewind(state)
if !couldParseFunctionType {
pos := p.nodePos()
typeParameters := p.parseTypeParameters()
baseType := p.parseType()
typeNode = p.finishNode(p.factory.NewQuantifiedTypeNode(typeParameters, baseType), pos)
p.contextFlags = saveContextFlags
return typeNode
}
}
if p.isStartOfFunctionTypeOrConstructorType() {
typeNode = p.parseFunctionOrConstructorType()
} else {
Expand Down
Loading