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
102 changes: 90 additions & 12 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@ type Checker struct {
reverseMappedCache map[ReverseMappedTypeKey]*Type
reverseHomomorphicMappedCache map[ReverseMappedTypeKey]*Type
iterationTypesCache map[IterationTypesKey]IterationTypes
contextualTypeStack map[*ast.Node]bool
markerTypes collections.Set[*Type]
undefinedSymbol *ast.Symbol
argumentsSymbol *ast.Symbol
Expand Down Expand Up @@ -915,6 +916,7 @@ func NewChecker(program Program) (*Checker, *sync.Mutex) {
c.reverseMappedCache = make(map[ReverseMappedTypeKey]*Type)
c.reverseHomomorphicMappedCache = make(map[ReverseMappedTypeKey]*Type)
c.iterationTypesCache = make(map[IterationTypesKey]IterationTypes)
c.contextualTypeStack = make(map[*ast.Node]bool)
c.undefinedSymbol = c.newSymbol(ast.SymbolFlagsProperty, "undefined")
c.argumentsSymbol = c.newSymbol(ast.SymbolFlagsProperty, "arguments")
c.requireSymbol = c.newSymbol(ast.SymbolFlagsProperty, "require")
Expand Down Expand Up @@ -7246,6 +7248,22 @@ func (c *Checker) reportObjectPossiblyNullOrUndefinedError(node *ast.Node, facts
}

func (c *Checker) checkExpressionWithContextualType(node *ast.Node, contextualType *Type, inferenceContext *InferenceContext, checkMode CheckMode) *Type {
if contextualType.flags&TypeFlagsQuantified == 0 {
return c.checkExpressionWithContextualTypeWorker(node, contextualType, inferenceContext, checkMode)
}
t0 := c.checkExpressionWithContextualTypeWorker(node, contextualType.AsQuantifiedType().baseType, nil, checkMode|CheckModeSkipContextSensitive|CheckModeSkipGenericFunctions)
ctx := c.newInferenceContext(
core.Map(contextualType.AsQuantifiedType().typeParameters, func(t *TypeParameter) *Type { return t.AsType() }),
nil,
InferenceFlagsNone,
nil,
)
c.inferTypes(ctx.inferences, t0, contextualType.AsQuantifiedType().baseType, InferencePriorityNoConstraints|InferencePriorityAlwaysStrict, false)
newContextualType := c.instantiateType(contextualType.AsQuantifiedType().baseType, ctx.mapper)
return c.checkExpressionWithContextualTypeWorker(node, newContextualType, inferenceContext, checkMode)
}

func (c *Checker) checkExpressionWithContextualTypeWorker(node *ast.Node, contextualType *Type, inferenceContext *InferenceContext, checkMode CheckMode) *Type {
contextNode := c.getContextNode(node)
c.pushContextualType(contextNode, contextualType, false /*isCache*/)
c.pushInferenceContext(contextNode, inferenceContext)
Expand Down Expand Up @@ -7322,8 +7340,30 @@ func (c *Checker) checkExpressionEx(node *ast.Node, checkMode CheckMode) *Type {
saveCurrentNode := c.currentNode
c.currentNode = node
c.instantiationCount = 0
uninstantiatedType := c.checkExpressionWorker(node, checkMode)
t := c.instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode)
var t *Type
var contextualType *Type
if !c.contextualTypeStack[node] {
// quickfix to avoid recursion TODO: come up with something better
c.contextualTypeStack[node] = true
contextualType = c.getContextualType(node, ContextFlagsNone)
delete(c.contextualTypeStack, node)
}
isAlreadyContextuallyChecking := core.Some(c.contextualInfos, func(info ContextualInfo) bool { return info.node == node })
if contextualType != nil && contextualType.flags&TypeFlagsQuantified != 0 && !isAlreadyContextuallyChecking {
t0 := c.checkExpressionWithContextualType(node, contextualType.AsQuantifiedType().baseType, nil, checkMode|CheckModeSkipContextSensitive|CheckModeSkipGenericFunctions)
ctx := c.newInferenceContext(
core.Map(contextualType.AsQuantifiedType().typeParameters, func(t *TypeParameter) *Type { return t.AsType() }),
nil,
InferenceFlagsNone,
nil,
)
c.inferTypes(ctx.inferences, t0, contextualType.AsQuantifiedType().baseType, InferencePriorityNoConstraints|InferencePriorityAlwaysStrict, false)
newContextualType := c.instantiateType(contextualType.AsQuantifiedType().baseType, ctx.mapper)
t = c.checkExpressionWithContextualType(node, newContextualType, nil, checkMode)
} else {
uninstantiatedType := c.checkExpressionWorker(node, checkMode)
t = c.instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode)
}
if isConstEnumObjectType(t) {
c.checkConstEnumAccess(node, t)
}
Expand Down Expand Up @@ -16821,7 +16861,7 @@ func (c *Checker) getDeclaredTypeOfClassOrInterface(symbol *ast.Symbol) *Type {
t := c.newObjectType(kind, symbol)
links.declaredType = t
outerTypeParameters := c.getOuterTypeParametersOfClassOrInterface(symbol)
typeParameters := c.appendLocalTypeParametersOfClassOrInterfaceOrTypeAlias(outerTypeParameters, symbol)
typeParameters := c.appendLocalTypeParametersOfClassOrInterfaceOrTypeAliasOrQuantifiedType(outerTypeParameters, symbol)
// A class or interface is generic if it has type parameters or a "this" type. We always give classes a "this" type
// because it is not feasible to analyze all members to determine if the "this" type escapes the class (in particular,
// property types inferred from initializers and method return types inferred from return statements are very hard
Expand Down Expand Up @@ -20877,7 +20917,7 @@ func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name s
// If we merged instantiations of a generic type, we replicate the symbol parent resetting behavior we used
// to do when we recorded multiple distinct symbols so that we still get, eg, `Array<T>.length` printed
// back and not `Array<string>.length` when we're looking at a `.length` access on a `string[] | number[]`
mergedInstantiations = singleProp.Parent != nil && len(c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(singleProp.Parent)) != 0
mergedInstantiations = singleProp.Parent != nil && len(c.getLocalTypeParametersOfClassOrInterfaceOrTypeAliasOrQuantifiedType(singleProp.Parent)) != 0
} else {
if propSet.Size() == 0 {
propSet.Add(singleProp)
Expand Down Expand Up @@ -21606,6 +21646,17 @@ func (c *Checker) instantiateTypeWorker(t *Type, m *TypeMapper, alias *TypeAlias
switch {
case flags&TypeFlagsTypeParameter != 0:
return m.Map(t)
case flags&TypeFlagsQuantified != 0:
return c.newQuantifiedType(
t.AsQuantifiedType().typeParameters,
// TODO: the following does not work figure out a way to do it
/*core.Map(t.AsQuantifiedType().typeParameters, func(tp *TypeParameter) *TypeParameter {
newTp := c.cloneTypeParameter(tp.AsType())
newTp.AsTypeParameter().constraint = c.instantiateType(newTp.AsTypeParameter().constraint, m)
return newTp.AsTypeParameter()
}),*/
c.instantiateType(t.AsQuantifiedType().baseType, m),
)
case flags&TypeFlagsObject != 0:
objectFlags := t.objectFlags
if objectFlags&(ObjectFlagsReference|ObjectFlagsAnonymous|ObjectFlagsMapped) != 0 {
Expand Down Expand Up @@ -22277,6 +22328,8 @@ func (c *Checker) getTypeFromTypeNodeWorker(node *ast.Node) *Type {
return c.getTypeFromInferTypeNode(node)
case ast.KindImportType:
return c.getTypeFromImportTypeNode(node)
case ast.KindQuantifiedType:
return c.getTypeFromQuantifiedTypeNode(node)
default:
return c.errorType
}
Expand Down Expand Up @@ -23043,7 +23096,7 @@ func (c *Checker) getAliasSymbolForTypeNode(node *ast.Node) *ast.Symbol {

func (c *Checker) getTypeArgumentsForAliasSymbol(symbol *ast.Symbol) []*Type {
if symbol != nil {
return c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
return c.getLocalTypeParametersOfClassOrInterfaceOrTypeAliasOrQuantifiedType(symbol)
}
return nil
}
Expand Down Expand Up @@ -23078,7 +23131,7 @@ func (c *Checker) getOuterTypeParameters(node *ast.Node, includeThisTypes bool)
case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration, ast.KindCallSignature, ast.KindConstructSignature,
ast.KindMethodSignature, ast.KindFunctionType, ast.KindConstructorType, ast.KindFunctionDeclaration,
ast.KindMethodDeclaration, ast.KindFunctionExpression, ast.KindArrowFunction, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindMappedType,
ast.KindConditionalType:
ast.KindConditionalType, ast.KindQuantifiedType:
outerTypeParameters := c.getOuterTypeParameters(node, includeThisTypes)
if (kind == ast.KindFunctionExpression || kind == ast.KindArrowFunction || ast.IsObjectLiteralMethod(node)) && c.isContextSensitive(node) {
signature := core.FirstOrNil(c.getSignaturesOfType(c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)), SignatureKindCall))
Expand Down Expand Up @@ -23116,14 +23169,14 @@ func (c *Checker) getInferTypeParameters(node *ast.Node) []*Type {
}

// The local type parameters are the combined set of type parameters from all declarations of the class,
// interface, or type alias.
func (c *Checker) getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol *ast.Symbol) []*Type {
return c.appendLocalTypeParametersOfClassOrInterfaceOrTypeAlias(nil, symbol)
// interface, type alias or quantified type
func (c *Checker) getLocalTypeParametersOfClassOrInterfaceOrTypeAliasOrQuantifiedType(symbol *ast.Symbol) []*Type {
return c.appendLocalTypeParametersOfClassOrInterfaceOrTypeAliasOrQuantifiedType(nil, symbol)
}

func (c *Checker) appendLocalTypeParametersOfClassOrInterfaceOrTypeAlias(types []*Type, symbol *ast.Symbol) []*Type {
func (c *Checker) appendLocalTypeParametersOfClassOrInterfaceOrTypeAliasOrQuantifiedType(types []*Type, symbol *ast.Symbol) []*Type {
for _, node := range symbol.Declarations {
if ast.NodeKindIs(node, ast.KindInterfaceDeclaration, ast.KindClassDeclaration, ast.KindClassExpression) || isTypeAlias(node) {
if ast.NodeKindIs(node, ast.KindInterfaceDeclaration, ast.KindClassDeclaration, ast.KindClassExpression, ast.KindQuantifiedType) || isTypeAlias(node) {
types = c.appendTypeParameters(types, node.TypeParameters())
}
}
Expand Down Expand Up @@ -23160,7 +23213,7 @@ func (c *Checker) getDeclaredTypeOfTypeAlias(symbol *ast.Symbol) *Type {
typeNode := declaration.Type()
t := c.getTypeFromTypeNode(typeNode)
if c.popTypeResolution() {
typeParameters := c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
typeParameters := c.getLocalTypeParametersOfClassOrInterfaceOrTypeAliasOrQuantifiedType(symbol)
if len(typeParameters) != 0 {
// Initialize the instantiation cache for generic type aliases. The declared type corresponds to
// an instantiation of the type alias with the type parameters supplied as type arguments.
Expand Down Expand Up @@ -23954,6 +24007,19 @@ func (c *Checker) getTypeFromImportTypeNode(node *ast.Node) *Type {
return links.resolvedType
}

func (c *Checker) getTypeFromQuantifiedTypeNode(node *ast.Node) *Type {
links := c.typeNodeLinks.Get(node)
if links.resolvedType == nil {
links.resolvedType = c.newQuantifiedType(
core.Map(node.AsQuantifiedTypeNode().TypeParameters.Nodes, func(typeParameterNode *ast.Node) *TypeParameter {
return c.getDeclaredTypeOfTypeParameter(typeParameterNode.Symbol()).AsTypeParameter()
}),
c.getTypeFromTypeNode(node.AsQuantifiedTypeNode().BaseType),
)
}
return links.resolvedType
}

func (c *Checker) getIdentifierChain(node *ast.Node) []*ast.Node {
if ast.IsIdentifier(node) {
return []*ast.Node{node}
Expand Down Expand Up @@ -24545,6 +24611,13 @@ func (c *Checker) newSubstitutionType(baseType *Type, constraint *Type) *Type {
return c.newType(TypeFlagsSubstitution, ObjectFlagsNone, data)
}

func (c *Checker) newQuantifiedType(typeParamters []*TypeParameter, baseType *Type) *Type {
data := &QuantifiedType{}
data.typeParameters = typeParamters
data.baseType = baseType
return c.newType(TypeFlagsQuantified, ObjectFlagsNone, data)
}

func (c *Checker) newSignature(flags SignatureFlags, declaration *ast.Node, typeParameters []*Type, thisParameter *ast.Symbol, parameters []*ast.Symbol, resolvedReturnType *Type, resolvedTypePredicate *TypePredicate, minArgumentCount int) *Signature {
sig := c.signaturePool.New()
sig.flags = flags
Expand Down Expand Up @@ -26681,6 +26754,11 @@ func (c *Checker) getBaseConstraintOrType(t *Type) *Type {
}

func (c *Checker) getBaseConstraintOfType(t *Type) *Type {
if t.flags&TypeFlagsQuantified != 0 {
return c.getBaseConstraintOfType(
t.AsQuantifiedType().baseType,
)
}
if t.flags&(TypeFlagsInstantiableNonPrimitive|TypeFlagsUnionOrIntersection|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 || c.isGenericTupleType(t) {
constraint := c.getResolvedBaseConstraint(t, nil)
if constraint != c.noConstraintType && constraint != c.circularConstraintType {
Expand Down
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
Loading