3113 lines
128 KiB
Go
3113 lines
128 KiB
Go
package checker
|
|
|
|
import (
|
|
"fmt"
|
|
"maps"
|
|
"slices"
|
|
"strings"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/module"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/modulespecifiers"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/nodebuilder"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
)
|
|
|
|
type CompositeSymbolIdentity struct {
|
|
isConstructorNode bool
|
|
symbolId ast.SymbolId
|
|
nodeId ast.NodeId
|
|
}
|
|
|
|
type TrackedSymbolArgs struct {
|
|
symbol *ast.Symbol
|
|
enclosingDeclaration *ast.Node
|
|
meaning ast.SymbolFlags
|
|
}
|
|
|
|
type SerializedTypeEntry struct {
|
|
node *ast.Node
|
|
truncating bool
|
|
addedLength int
|
|
trackedSymbols []*TrackedSymbolArgs
|
|
}
|
|
|
|
type CompositeTypeCacheIdentity struct {
|
|
typeId TypeId
|
|
flags nodebuilder.Flags
|
|
internalFlags nodebuilder.InternalFlags
|
|
}
|
|
|
|
type NodeBuilderLinks struct {
|
|
serializedTypes map[CompositeTypeCacheIdentity]*SerializedTypeEntry // Collection of types serialized at this location
|
|
fakeScopeForSignatureDeclaration *string // If present, this is a fake scope injected into an enclosing declaration chain.
|
|
}
|
|
|
|
type NodeBuilderSymbolLinks struct {
|
|
specifierCache module.ModeAwareCache[string]
|
|
}
|
|
type NodeBuilderContext struct {
|
|
tracker nodebuilder.SymbolTracker
|
|
approximateLength int
|
|
encounteredError bool
|
|
truncating bool
|
|
reportedDiagnostic bool
|
|
flags nodebuilder.Flags
|
|
internalFlags nodebuilder.InternalFlags
|
|
depth int
|
|
enclosingDeclaration *ast.Node
|
|
enclosingFile *ast.SourceFile
|
|
inferTypeParameters []*Type
|
|
visitedTypes collections.Set[TypeId]
|
|
symbolDepth map[CompositeSymbolIdentity]int
|
|
trackedSymbols []*TrackedSymbolArgs
|
|
mapper *TypeMapper
|
|
reverseMappedStack []*ast.Symbol
|
|
enclosingSymbolTypes map[ast.SymbolId]*Type
|
|
suppressReportInferenceFallback bool
|
|
remappedSymbolReferences map[ast.SymbolId]*ast.Symbol
|
|
|
|
// per signature scope state
|
|
hasCreatedTypeParameterSymbolList bool
|
|
hasCreatedTypeParametersNamesLookups bool
|
|
typeParameterNames map[TypeId]*ast.Identifier
|
|
typeParameterNamesByText map[string]struct{}
|
|
typeParameterNamesByTextNextNameCount map[string]int
|
|
typeParameterSymbolList map[ast.SymbolId]struct{}
|
|
}
|
|
|
|
type nodeBuilderImpl struct {
|
|
// host members
|
|
f *ast.NodeFactory
|
|
ch *Checker
|
|
e *printer.EmitContext
|
|
|
|
// cache
|
|
links core.LinkStore[*ast.Node, NodeBuilderLinks]
|
|
symbolLinks core.LinkStore[*ast.Symbol, NodeBuilderSymbolLinks]
|
|
|
|
// state
|
|
ctx *NodeBuilderContext
|
|
|
|
// reusable visitor
|
|
cloneBindingNameVisitor *ast.NodeVisitor
|
|
}
|
|
|
|
const (
|
|
defaultMaximumTruncationLength = 160
|
|
noTruncationMaximumTruncationLength = 1_000_000
|
|
)
|
|
|
|
// Node builder utility functions
|
|
|
|
func newNodeBuilderImpl(ch *Checker, e *printer.EmitContext) *nodeBuilderImpl {
|
|
b := &nodeBuilderImpl{f: e.Factory.AsNodeFactory(), ch: ch, e: e}
|
|
b.cloneBindingNameVisitor = ast.NewNodeVisitor(b.cloneBindingName, b.f, ast.NodeVisitorHooks{})
|
|
return b
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) saveRestoreFlags() func() {
|
|
flags := b.ctx.flags
|
|
internalFlags := b.ctx.internalFlags
|
|
depth := b.ctx.depth
|
|
|
|
return func() {
|
|
b.ctx.flags = flags
|
|
b.ctx.internalFlags = internalFlags
|
|
b.ctx.depth = depth
|
|
}
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) checkTruncationLength() bool {
|
|
if b.ctx.truncating {
|
|
return b.ctx.truncating
|
|
}
|
|
b.ctx.truncating = b.ctx.approximateLength > (core.IfElse((b.ctx.flags&nodebuilder.FlagsNoTruncation != 0), noTruncationMaximumTruncationLength, defaultMaximumTruncationLength))
|
|
return b.ctx.truncating
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) appendReferenceToType(root *ast.TypeNode, ref *ast.TypeNode) *ast.TypeNode {
|
|
if ast.IsImportTypeNode(root) {
|
|
// first shift type arguments
|
|
|
|
// !!! In the old emitter, an Identifier could have type arguments for use with quickinfo:
|
|
// typeArguments := root.TypeArguments
|
|
// qualifier := root.AsImportTypeNode().Qualifier
|
|
// if qualifier != nil {
|
|
// if ast.IsIdentifier(qualifier) {
|
|
// if typeArguments != getIdentifierTypeArguments(qualifier) {
|
|
// qualifier = setIdentifierTypeArguments(b.f.CloneNode(qualifier), typeArguments)
|
|
// }
|
|
// } else {
|
|
// if typeArguments != getIdentifierTypeArguments(qualifier.Right) {
|
|
// qualifier = b.f.UpdateQualifiedName(qualifier, qualifier.Left, setIdentifierTypeArguments(b.f.cloneNode(qualifier.Right), typeArguments))
|
|
// }
|
|
// }
|
|
// }
|
|
// !!! Without the above, nested type args are silently elided
|
|
imprt := root.AsImportTypeNode()
|
|
// then move qualifiers
|
|
ids := getAccessStack(ref)
|
|
var qualifier *ast.Node
|
|
for _, id := range ids {
|
|
if qualifier != nil {
|
|
qualifier = b.f.NewQualifiedName(qualifier, id)
|
|
} else {
|
|
qualifier = id
|
|
}
|
|
}
|
|
return b.f.UpdateImportTypeNode(imprt, imprt.IsTypeOf, imprt.Argument, imprt.Attributes, qualifier, ref.AsTypeReferenceNode().TypeArguments)
|
|
} else {
|
|
// first shift type arguments
|
|
// !!! In the old emitter, an Identifier could have type arguments for use with quickinfo:
|
|
// typeArguments := root.TypeArguments
|
|
// typeName := root.AsTypeReferenceNode().TypeName
|
|
// if ast.IsIdentifier(typeName) {
|
|
// if typeArguments != getIdentifierTypeArguments(typeName) {
|
|
// typeName = setIdentifierTypeArguments(b.f.cloneNode(typeName), typeArguments)
|
|
// }
|
|
// } else {
|
|
// if typeArguments != getIdentifierTypeArguments(typeName.Right) {
|
|
// typeName = b.f.UpdateQualifiedName(typeName, typeName.Left, setIdentifierTypeArguments(b.f.cloneNode(typeName.Right), typeArguments))
|
|
// }
|
|
// }
|
|
// !!! Without the above, nested type args are silently elided
|
|
// then move qualifiers
|
|
ids := getAccessStack(ref)
|
|
var typeName *ast.Node = root.AsTypeReferenceNode().TypeName
|
|
for _, id := range ids {
|
|
typeName = b.f.NewQualifiedName(typeName, id)
|
|
}
|
|
return b.f.UpdateTypeReferenceNode(root.AsTypeReferenceNode(), typeName, ref.AsTypeReferenceNode().TypeArguments)
|
|
}
|
|
}
|
|
|
|
func getAccessStack(ref *ast.Node) []*ast.Node {
|
|
var state *ast.Node = ref.AsTypeReferenceNode().TypeName
|
|
ids := []*ast.Node{}
|
|
for !ast.IsIdentifier(state) {
|
|
entity := state.AsQualifiedName()
|
|
ids = append([]*ast.Node{entity.Right}, ids...)
|
|
state = entity.Left
|
|
}
|
|
ids = append([]*ast.Node{state}, ids...)
|
|
return ids
|
|
}
|
|
|
|
func isClassInstanceSide(c *Checker, t *Type) bool {
|
|
return t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsClass != 0 && (t == c.getDeclaredTypeOfClassOrInterface(t.symbol) || (t.flags&TypeFlagsObject != 0 && t.objectFlags&ObjectFlagsIsClassInstanceClone != 0))
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) createElidedInformationPlaceholder() *ast.TypeNode {
|
|
b.ctx.approximateLength += 3
|
|
if b.ctx.flags&nodebuilder.FlagsNoTruncation == 0 {
|
|
return b.f.NewTypeReferenceNode(b.f.NewIdentifier("..."), nil /*typeArguments*/)
|
|
}
|
|
return b.e.AddSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, "elided", false /*hasTrailingNewLine*/)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) mapToTypeNodes(list []*Type, isBareList bool) *ast.NodeList {
|
|
if len(list) == 0 {
|
|
return nil
|
|
}
|
|
|
|
if b.checkTruncationLength() {
|
|
if !isBareList {
|
|
var node *ast.Node
|
|
if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 {
|
|
node = b.e.AddSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, "elided", false /*hasTrailingNewLine*/)
|
|
} else {
|
|
node = b.f.NewTypeReferenceNode(b.f.NewIdentifier("..."), nil /*typeArguments*/)
|
|
}
|
|
return b.f.NewNodeList([]*ast.Node{node})
|
|
} else if len(list) > 2 {
|
|
nodes := []*ast.Node{
|
|
b.typeToTypeNode(list[0]),
|
|
nil,
|
|
b.typeToTypeNode(list[len(list)-1]),
|
|
}
|
|
|
|
if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 {
|
|
nodes[1] = b.e.AddSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, fmt.Sprintf("... %d more elided ...", len(list)-2), false /*hasTrailingNewLine*/)
|
|
} else {
|
|
text := fmt.Sprintf("... %d more ...", len(list)-2)
|
|
nodes[1] = b.f.NewTypeReferenceNode(b.f.NewIdentifier(text), nil /*typeArguments*/)
|
|
}
|
|
return b.f.NewNodeList(nodes)
|
|
}
|
|
}
|
|
|
|
mayHaveNameCollisions := b.ctx.flags&nodebuilder.FlagsUseFullyQualifiedType == 0
|
|
type seenName struct {
|
|
t *Type
|
|
i int
|
|
}
|
|
var seenNames *collections.MultiMap[string, seenName]
|
|
if mayHaveNameCollisions {
|
|
seenNames = &collections.MultiMap[string, seenName]{}
|
|
}
|
|
|
|
result := make([]*ast.Node, 0, len(list))
|
|
|
|
for i, t := range list {
|
|
if b.checkTruncationLength() && (i+2 < len(list)-1) {
|
|
if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 {
|
|
result = append(result, b.e.AddSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, fmt.Sprintf("... %d more elided ...", len(list)-i), false /*hasTrailingNewLine*/))
|
|
} else {
|
|
text := fmt.Sprintf("... %d more ...", len(list)-i)
|
|
result = append(result, b.f.NewTypeReferenceNode(b.f.NewIdentifier(text), nil /*typeArguments*/))
|
|
}
|
|
typeNode := b.typeToTypeNode(list[len(list)-1])
|
|
if typeNode != nil {
|
|
result = append(result, typeNode)
|
|
}
|
|
break
|
|
}
|
|
b.ctx.approximateLength += 2 // Account for whitespace + separator
|
|
typeNode := b.typeToTypeNode(t)
|
|
if typeNode != nil {
|
|
result = append(result, typeNode)
|
|
if seenNames != nil && isIdentifierTypeReference(typeNode) {
|
|
seenNames.Add(typeNode.AsTypeReferenceNode().TypeName.Text(), seenName{t, len(result) - 1})
|
|
}
|
|
}
|
|
}
|
|
|
|
if seenNames != nil {
|
|
// To avoid printing types like `[Foo, Foo]` or `Bar & Bar` where
|
|
// occurrences of the same name actually come from different
|
|
// namespaces, go through the single-identifier type reference nodes
|
|
// we just generated, and see if any names were generated more than
|
|
// once while referring to different types. If so, regenerate the
|
|
// type node for each entry by that name with the
|
|
// `UseFullyQualifiedType` flag enabled.
|
|
restoreFlags := b.saveRestoreFlags()
|
|
b.ctx.flags |= nodebuilder.FlagsUseFullyQualifiedType
|
|
for types := range seenNames.Values() {
|
|
if !arrayIsHomogeneous(types, func(a, b seenName) bool {
|
|
return typesAreSameReference(a.t, b.t)
|
|
}) {
|
|
for _, seen := range types {
|
|
result[seen.i] = b.typeToTypeNode(seen.t)
|
|
}
|
|
}
|
|
}
|
|
restoreFlags()
|
|
}
|
|
|
|
return b.f.NewNodeList(result)
|
|
}
|
|
|
|
func isIdentifierTypeReference(node *ast.Node) bool {
|
|
return ast.IsTypeReferenceNode(node) && ast.IsIdentifier(node.AsTypeReferenceNode().TypeName)
|
|
}
|
|
|
|
func arrayIsHomogeneous[T any](array []T, comparer func(a, B T) bool) bool {
|
|
if len(array) < 2 {
|
|
return true
|
|
}
|
|
first := array[0]
|
|
for i := 1; i < len(array); i++ {
|
|
target := array[i]
|
|
if !comparer(first, target) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func typesAreSameReference(a, b *Type) bool {
|
|
return a == b || a.symbol != nil && a.symbol == b.symbol || a.alias != nil && a.alias == b.alias
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) setCommentRange(node *ast.Node, range_ *ast.Node) {
|
|
if range_ != nil && b.ctx.enclosingFile != nil && b.ctx.enclosingFile == ast.GetSourceFileOfNode(range_) {
|
|
// Copy comments to node for declaration emit
|
|
b.e.AssignCommentRange(node, range_)
|
|
}
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) tryReuseExistingTypeNodeHelper(existing *ast.TypeNode) *ast.TypeNode {
|
|
return nil // !!!
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) tryReuseExistingTypeNode(typeNode *ast.TypeNode, t *Type, host *ast.Node, addUndefined bool) *ast.TypeNode {
|
|
originalType := t
|
|
if addUndefined {
|
|
t = b.ch.getOptionalType(t, !ast.IsParameter(host))
|
|
}
|
|
clone := b.tryReuseExistingNonParameterTypeNode(typeNode, t, host, nil)
|
|
if clone != nil {
|
|
// explicitly add `| undefined` if it's missing from the input type nodes and the type contains `undefined` (and not the missing type)
|
|
if addUndefined && containsNonMissingUndefinedType(b.ch, t) && !someType(b.getTypeFromTypeNode(typeNode, false), func(t *Type) bool {
|
|
return t.flags&TypeFlagsUndefined != 0
|
|
}) {
|
|
return b.f.NewUnionTypeNode(b.f.NewNodeList([]*ast.TypeNode{clone, b.f.NewKeywordTypeNode(ast.KindUndefinedKeyword)}))
|
|
}
|
|
return clone
|
|
}
|
|
if addUndefined && originalType != t {
|
|
cloneMissingUndefined := b.tryReuseExistingNonParameterTypeNode(typeNode, originalType, host, nil)
|
|
if cloneMissingUndefined != nil {
|
|
return b.f.NewUnionTypeNode(b.f.NewNodeList([]*ast.TypeNode{cloneMissingUndefined, b.f.NewKeywordTypeNode(ast.KindUndefinedKeyword)}))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) typeNodeIsEquivalentToType(annotatedDeclaration *ast.Node, t *Type, typeFromTypeNode *Type) bool {
|
|
if typeFromTypeNode == t {
|
|
return true
|
|
}
|
|
if annotatedDeclaration == nil {
|
|
return false
|
|
}
|
|
// !!!
|
|
// used to be hasEffectiveQuestionToken for JSDoc
|
|
if isOptionalDeclaration(annotatedDeclaration) {
|
|
return b.ch.getTypeWithFacts(t, TypeFactsNEUndefined) == typeFromTypeNode
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing *ast.TypeNode, t *Type) bool {
|
|
// In JS, you can say something like `Foo` and get a `Foo<any>` implicitly - we don't want to preserve that original `Foo` in these cases, though.
|
|
if t.objectFlags&ObjectFlagsReference == 0 {
|
|
return true
|
|
}
|
|
if !ast.IsTypeReferenceNode(existing) {
|
|
return true
|
|
}
|
|
// `type` is a reference type, and `existing` is a type reference node, but we still need to make sure they refer to the _same_ target type
|
|
// before we go comparing their type argument counts.
|
|
b.ch.getTypeFromTypeReference(existing)
|
|
// call to ensure symbol is resolved
|
|
links := b.ch.symbolNodeLinks.TryGet(existing)
|
|
if links == nil {
|
|
return true
|
|
}
|
|
symbol := links.resolvedSymbol
|
|
if symbol == nil {
|
|
return true
|
|
}
|
|
existingTarget := b.ch.getDeclaredTypeOfSymbol(symbol)
|
|
if existingTarget == nil || existingTarget != t.AsTypeReference().target {
|
|
return true
|
|
}
|
|
return len(existing.TypeArguments()) >= b.ch.getMinTypeArgumentCount(t.AsTypeReference().target.AsInterfaceType().TypeParameters())
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) tryReuseExistingNonParameterTypeNode(existing *ast.TypeNode, t *Type, host *ast.Node, annotationType *Type) *ast.TypeNode {
|
|
if host == nil {
|
|
host = b.ctx.enclosingDeclaration
|
|
}
|
|
if annotationType == nil {
|
|
annotationType = b.getTypeFromTypeNode(existing, true)
|
|
}
|
|
if annotationType != nil && b.typeNodeIsEquivalentToType(host, t, annotationType) && b.existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing, t) {
|
|
result := b.tryReuseExistingTypeNodeHelper(existing)
|
|
if result != nil {
|
|
return result
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) getResolvedTypeWithoutAbstractConstructSignatures(t *StructuredType) *Type {
|
|
if len(t.ConstructSignatures()) == 0 {
|
|
return t.AsType()
|
|
}
|
|
if t.objectTypeWithoutAbstractConstructSignatures != nil {
|
|
return t.objectTypeWithoutAbstractConstructSignatures
|
|
}
|
|
constructSignatures := core.Filter(t.ConstructSignatures(), func(signature *Signature) bool {
|
|
return signature.flags&SignatureFlagsAbstract == 0
|
|
})
|
|
if len(constructSignatures) == len(t.ConstructSignatures()) {
|
|
t.objectTypeWithoutAbstractConstructSignatures = t.AsType()
|
|
return t.AsType()
|
|
}
|
|
typeCopy := b.ch.newAnonymousType(t.symbol, t.members, t.CallSignatures(), core.IfElse(len(constructSignatures) > 0, constructSignatures, []*Signature{}), t.indexInfos)
|
|
t.objectTypeWithoutAbstractConstructSignatures = typeCopy
|
|
typeCopy.AsStructuredType().objectTypeWithoutAbstractConstructSignatures = typeCopy
|
|
return typeCopy
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) symbolToNode(symbol *ast.Symbol, meaning ast.SymbolFlags) *ast.Node {
|
|
if b.ctx.internalFlags&nodebuilder.InternalFlagsWriteComputedProps != 0 {
|
|
if symbol.ValueDeclaration != nil {
|
|
name := ast.GetNameOfDeclaration(symbol.ValueDeclaration)
|
|
if name != nil && ast.IsComputedPropertyName(name) {
|
|
return name
|
|
}
|
|
}
|
|
if b.ch.valueSymbolLinks.Has(symbol) {
|
|
nameType := b.ch.valueSymbolLinks.Get(symbol).nameType
|
|
if nameType != nil && nameType.flags&(TypeFlagsEnumLiteral|TypeFlagsUniqueESSymbol) != 0 {
|
|
oldEnclosing := b.ctx.enclosingDeclaration
|
|
b.ctx.enclosingDeclaration = nameType.symbol.ValueDeclaration
|
|
result := b.f.NewComputedPropertyName(b.symbolToExpression(nameType.symbol, meaning))
|
|
b.ctx.enclosingDeclaration = oldEnclosing
|
|
return result
|
|
}
|
|
}
|
|
}
|
|
return b.symbolToExpression(symbol, meaning)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) symbolToName(symbol *ast.Symbol, meaning ast.SymbolFlags, expectsIdentifier bool) *ast.Node {
|
|
chain := b.lookupSymbolChain(symbol, meaning, false)
|
|
if expectsIdentifier && len(chain) != 1 && !b.ctx.encounteredError && (b.ctx.flags&nodebuilder.FlagsAllowQualifiedNameInPlaceOfIdentifier != 0) {
|
|
b.ctx.encounteredError = true
|
|
}
|
|
return b.createEntityNameFromSymbolChain(chain, len(chain)-1)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) createEntityNameFromSymbolChain(chain []*ast.Symbol, index int) *ast.Node {
|
|
// typeParameterNodes := b.lookupTypeParameterNodes(chain, index)
|
|
symbol := chain[index]
|
|
|
|
if index == 0 {
|
|
b.ctx.flags |= nodebuilder.FlagsInInitialEntityName
|
|
}
|
|
symbolName := b.getNameOfSymbolAsWritten(symbol)
|
|
if index == 0 {
|
|
b.ctx.flags ^= nodebuilder.FlagsInInitialEntityName
|
|
}
|
|
|
|
identifier := b.f.NewIdentifier(symbolName)
|
|
b.e.AddEmitFlags(identifier, printer.EFNoAsciiEscaping)
|
|
// !!! TODO: smuggle type arguments out
|
|
// if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray<TypeNode | TypeParameterDeclaration>(typeParameterNodes));
|
|
// identifier.symbol = symbol;
|
|
// expression = identifier;
|
|
if index > 0 {
|
|
return b.f.NewQualifiedName(
|
|
b.createEntityNameFromSymbolChain(chain, index-1),
|
|
identifier,
|
|
)
|
|
}
|
|
return identifier
|
|
}
|
|
|
|
// TODO: Audit usages of symbolToEntityNameNode - they should probably all be symbolToName
|
|
func (b *nodeBuilderImpl) symbolToEntityNameNode(symbol *ast.Symbol) *ast.EntityName {
|
|
identifier := b.f.NewIdentifier(symbol.Name)
|
|
if symbol.Parent != nil {
|
|
return b.f.NewQualifiedName(b.symbolToEntityNameNode(symbol.Parent), identifier)
|
|
}
|
|
return identifier
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) symbolToTypeNode(symbol *ast.Symbol, mask ast.SymbolFlags, typeArguments *ast.NodeList) *ast.TypeNode {
|
|
chain := b.lookupSymbolChain(symbol, mask, (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope == 0)) // If we're using aliases outside the current scope, dont bother with the module
|
|
if len(chain) == 0 {
|
|
return nil // TODO: shouldn't be possible, `lookupSymbolChain` should always at least return the input symbol and issue an error
|
|
}
|
|
isTypeOf := mask == ast.SymbolFlagsValue
|
|
if core.Some(chain[0].Declarations, hasNonGlobalAugmentationExternalModuleSymbol) {
|
|
// module is root, must use `ImportTypeNode`
|
|
var nonRootParts *ast.Node
|
|
if len(chain) > 1 {
|
|
nonRootParts = b.createAccessFromSymbolChain(chain, len(chain)-1, 1, typeArguments)
|
|
}
|
|
typeParameterNodes := typeArguments
|
|
if typeParameterNodes == nil {
|
|
typeParameterNodes = b.lookupTypeParameterNodes(chain, 0)
|
|
}
|
|
contextFile := ast.GetSourceFileOfNode(b.e.MostOriginal(b.ctx.enclosingDeclaration)) // TODO: Just use b.ctx.enclosingFile ? Or is the delayed lookup important for context moves?
|
|
targetFile := ast.GetSourceFileOfModule(chain[0])
|
|
var specifier string
|
|
var attributes *ast.Node
|
|
if b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNode16 || b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNodeNext {
|
|
// An `import` type directed at an esm format file is only going to resolve in esm mode - set the esm mode assertion
|
|
if targetFile != nil && contextFile != nil && b.ch.program.GetEmitModuleFormatOfFile(targetFile) == core.ModuleKindESNext && b.ch.program.GetEmitModuleFormatOfFile(targetFile) != b.ch.program.GetEmitModuleFormatOfFile(contextFile) {
|
|
specifier = b.getSpecifierForModuleSymbol(chain[0], core.ModuleKindESNext)
|
|
attributes = b.f.NewImportAttributes(
|
|
ast.KindWithKeyword,
|
|
b.f.NewNodeList([]*ast.Node{b.f.NewImportAttribute(b.f.NewStringLiteral("resolution-mode"), b.f.NewStringLiteral("import"))}),
|
|
false,
|
|
)
|
|
}
|
|
}
|
|
if len(specifier) == 0 {
|
|
specifier = b.getSpecifierForModuleSymbol(chain[0], core.ResolutionModeNone)
|
|
}
|
|
if (b.ctx.flags&nodebuilder.FlagsAllowNodeModulesRelativePaths == 0) /* && b.ch.compilerOptions.GetModuleResolutionKind() != core.ModuleResolutionKindClassic */ && strings.Contains(specifier, "/node_modules/") {
|
|
oldSpecifier := specifier
|
|
|
|
if b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNode16 || b.ch.compilerOptions.GetModuleResolutionKind() == core.ModuleResolutionKindNodeNext {
|
|
// We might be able to write a portable import type using a mode override; try specifier generation again, but with a different mode set
|
|
swappedMode := core.ModuleKindESNext
|
|
if b.ch.program.GetEmitModuleFormatOfFile(contextFile) == core.ModuleKindESNext {
|
|
swappedMode = core.ModuleKindCommonJS
|
|
}
|
|
specifier = b.getSpecifierForModuleSymbol(chain[0], swappedMode)
|
|
|
|
if strings.Contains(specifier, "/node_modules/") {
|
|
// Still unreachable :(
|
|
specifier = oldSpecifier
|
|
} else {
|
|
modeStr := "require"
|
|
if swappedMode == core.ModuleKindESNext {
|
|
modeStr = "import"
|
|
}
|
|
attributes = b.f.NewImportAttributes(
|
|
ast.KindWithKeyword,
|
|
b.f.NewNodeList([]*ast.Node{b.f.NewImportAttribute(b.f.NewStringLiteral("resolution-mode"), b.f.NewStringLiteral(modeStr))}),
|
|
false,
|
|
)
|
|
}
|
|
}
|
|
|
|
if attributes == nil {
|
|
// If ultimately we can only name the symbol with a reference that dives into a `node_modules` folder, we should error
|
|
// since declaration files with these kinds of references are liable to fail when published :(
|
|
b.ctx.encounteredError = true
|
|
b.ctx.tracker.ReportLikelyUnsafeImportRequiredError(oldSpecifier)
|
|
}
|
|
}
|
|
|
|
lit := b.f.NewLiteralTypeNode(b.f.NewStringLiteral(specifier))
|
|
b.ctx.approximateLength += len(specifier) + 10 // specifier + import("")
|
|
if nonRootParts == nil || ast.IsEntityName(nonRootParts) {
|
|
if nonRootParts != nil {
|
|
// !!! TODO: smuggle type arguments out
|
|
// const lastId = isIdentifier(nonRootParts) ? nonRootParts : nonRootParts.right;
|
|
// setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined);
|
|
}
|
|
return b.f.NewImportTypeNode(isTypeOf, lit, attributes, nonRootParts, typeParameterNodes)
|
|
}
|
|
|
|
splitNode := getTopmostIndexedAccessType(nonRootParts.AsIndexedAccessTypeNode())
|
|
qualifier := splitNode.ObjectType.AsTypeReference().TypeName
|
|
return b.f.NewIndexedAccessTypeNode(
|
|
b.f.NewImportTypeNode(isTypeOf, lit, attributes, qualifier, typeParameterNodes),
|
|
splitNode.IndexType,
|
|
)
|
|
|
|
}
|
|
|
|
entityName := b.createAccessFromSymbolChain(chain, len(chain)-1, 0, typeArguments)
|
|
if ast.IsIndexedAccessTypeNode(entityName) {
|
|
return entityName // Indexed accesses can never be `typeof`
|
|
}
|
|
if isTypeOf {
|
|
return b.f.NewTypeQueryNode(entityName, nil)
|
|
}
|
|
// !!! TODO: smuggle type arguments out
|
|
// Move type arguments from last identifier on chain to type reference
|
|
// const lastId = isIdentifier(entityName) ? entityName : entityName.right;
|
|
// const lastTypeArgs = getIdentifierTypeArguments(lastId);
|
|
// setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined);
|
|
return b.f.NewTypeReferenceNode(entityName, typeArguments)
|
|
}
|
|
|
|
func getTopmostIndexedAccessType(node *ast.IndexedAccessTypeNode) *ast.IndexedAccessTypeNode {
|
|
if ast.IsIndexedAccessTypeNode(node.ObjectType) {
|
|
return getTopmostIndexedAccessType(node.ObjectType.AsIndexedAccessTypeNode())
|
|
}
|
|
return node
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) createAccessFromSymbolChain(chain []*ast.Symbol, index int, stopper int, overrideTypeArguments *ast.NodeList) *ast.Node {
|
|
// !!! TODO: smuggle type arguments out
|
|
typeParameterNodes := overrideTypeArguments
|
|
if index != (len(chain) - 1) {
|
|
typeParameterNodes = b.lookupTypeParameterNodes(chain, index)
|
|
}
|
|
symbol := chain[index]
|
|
var parent *ast.Symbol
|
|
if index > 0 {
|
|
parent = chain[index-1]
|
|
}
|
|
|
|
var symbolName string
|
|
if index == 0 {
|
|
b.ctx.flags |= nodebuilder.FlagsInInitialEntityName
|
|
symbolName = b.getNameOfSymbolAsWritten(symbol)
|
|
b.ctx.approximateLength += len(symbolName) + 1
|
|
b.ctx.flags ^= nodebuilder.FlagsInInitialEntityName
|
|
} else {
|
|
// lookup a ref to symbol within parent to handle export aliases
|
|
if parent != nil {
|
|
exports := b.ch.getExportsOfSymbol(parent)
|
|
if exports != nil {
|
|
// avoid exhaustive iteration in the common case
|
|
res, ok := exports[symbol.Name]
|
|
if symbol.Name != ast.InternalSymbolNameExportEquals && !isLateBoundName(symbol.Name) && ok && res != nil && b.ch.getSymbolIfSameReference(res, symbol) != nil {
|
|
symbolName = symbol.Name
|
|
} else {
|
|
results := make(map[*ast.Symbol]string, 1)
|
|
for name, ex := range exports {
|
|
if b.ch.getSymbolIfSameReference(ex, symbol) != nil && !isLateBoundName(name) && name != ast.InternalSymbolNameExportEquals {
|
|
results[ex] = name
|
|
// break // must collect all results and sort them - exports are randomly iterated
|
|
}
|
|
}
|
|
resultSymbols := slices.Collect(maps.Keys(results))
|
|
if len(resultSymbols) > 0 {
|
|
b.ch.sortSymbols(resultSymbols)
|
|
symbolName = results[resultSymbols[0]]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(symbolName) == 0 {
|
|
var name *ast.Node
|
|
for _, d := range symbol.Declarations {
|
|
name = ast.GetNameOfDeclaration(d)
|
|
if name != nil {
|
|
break
|
|
}
|
|
}
|
|
if name != nil && ast.IsComputedPropertyName(name) && ast.IsEntityName(name.AsComputedPropertyName().Expression) {
|
|
lhs := b.createAccessFromSymbolChain(chain, index-1, stopper, overrideTypeArguments)
|
|
if ast.IsEntityName(lhs) {
|
|
return b.f.NewIndexedAccessTypeNode(
|
|
b.f.NewParenthesizedTypeNode(b.f.NewTypeQueryNode(lhs, nil)),
|
|
b.f.NewTypeQueryNode(name.Expression(), nil),
|
|
)
|
|
}
|
|
return lhs
|
|
}
|
|
symbolName = b.getNameOfSymbolAsWritten(symbol)
|
|
}
|
|
b.ctx.approximateLength += len(symbolName) + 1
|
|
|
|
if (b.ctx.flags&nodebuilder.FlagsForbidIndexedAccessSymbolReferences == 0) && parent != nil &&
|
|
b.ch.getMembersOfSymbol(parent) != nil && b.ch.getMembersOfSymbol(parent)[symbol.Name] != nil &&
|
|
b.ch.getSymbolIfSameReference(b.ch.getMembersOfSymbol(parent)[symbol.Name], symbol) != nil {
|
|
// Should use an indexed access
|
|
lhs := b.createAccessFromSymbolChain(chain, index-1, stopper, overrideTypeArguments)
|
|
if ast.IsIndexedAccessTypeNode(lhs) {
|
|
return b.f.NewIndexedAccessTypeNode(
|
|
lhs,
|
|
b.f.NewLiteralTypeNode(b.f.NewStringLiteral(symbolName)),
|
|
)
|
|
}
|
|
return b.f.NewIndexedAccessTypeNode(
|
|
b.f.NewTypeReferenceNode(lhs, typeParameterNodes),
|
|
b.f.NewLiteralTypeNode(b.f.NewStringLiteral(symbolName)),
|
|
)
|
|
}
|
|
|
|
identifier := b.f.NewIdentifier(symbolName)
|
|
b.e.AddEmitFlags(identifier, printer.EFNoAsciiEscaping)
|
|
// !!! TODO: smuggle type arguments out
|
|
// if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray<TypeNode | TypeParameterDeclaration>(typeParameterNodes));
|
|
// identifier.symbol = symbol;
|
|
|
|
if index > stopper {
|
|
lhs := b.createAccessFromSymbolChain(chain, index-1, stopper, overrideTypeArguments)
|
|
if !ast.IsEntityName(lhs) {
|
|
panic("Impossible construct - an export of an indexed access cannot be reachable")
|
|
}
|
|
return b.f.NewQualifiedName(lhs, identifier)
|
|
}
|
|
|
|
return identifier
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) symbolToExpression(symbol *ast.Symbol, mask ast.SymbolFlags) *ast.Expression {
|
|
chain := b.lookupSymbolChain(symbol, mask, false)
|
|
return b.createExpressionFromSymbolChain(chain, len(chain)-1)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) createExpressionFromSymbolChain(chain []*ast.Symbol, index int) *ast.Expression {
|
|
// typeParameterNodes := b.lookupTypeParameterNodes(chain, index)
|
|
symbol := chain[index]
|
|
|
|
if index == 0 {
|
|
b.ctx.flags |= nodebuilder.FlagsInInitialEntityName
|
|
}
|
|
symbolName := b.getNameOfSymbolAsWritten(symbol)
|
|
if index == 0 {
|
|
b.ctx.flags ^= nodebuilder.FlagsInInitialEntityName
|
|
}
|
|
|
|
if startsWithSingleOrDoubleQuote(symbolName) && core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) {
|
|
return b.f.NewStringLiteral(b.getSpecifierForModuleSymbol(symbol, core.ResolutionModeNone))
|
|
}
|
|
|
|
if index == 0 || canUsePropertyAccess(symbolName) {
|
|
identifier := b.f.NewIdentifier(symbolName)
|
|
b.e.AddEmitFlags(identifier, printer.EFNoAsciiEscaping)
|
|
// !!! TODO: smuggle type arguments out
|
|
// if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray<TypeNode | TypeParameterDeclaration>(typeParameterNodes));
|
|
// identifier.symbol = symbol;
|
|
if index > 0 {
|
|
return b.f.NewPropertyAccessExpression(b.createExpressionFromSymbolChain(chain, index-1), nil, identifier, ast.NodeFlagsNone)
|
|
}
|
|
return identifier
|
|
}
|
|
|
|
if startsWithSquareBracket(symbolName) {
|
|
symbolName = symbolName[1 : len(symbolName)-1]
|
|
}
|
|
|
|
var expression *ast.Expression
|
|
if startsWithSingleOrDoubleQuote(symbolName) && symbol.Flags&ast.SymbolFlagsEnumMember == 0 {
|
|
expression = b.f.NewStringLiteral(stringutil.UnquoteString(symbolName))
|
|
} else if jsnum.FromString(symbolName).String() == symbolName {
|
|
// TODO: the follwing in strada would assert if the number is negative, but no such assertion exists here
|
|
// Moreover, what's even guaranteeing the name *isn't* -1 here anyway? Needs double-checking.
|
|
expression = b.f.NewNumericLiteral(symbolName)
|
|
}
|
|
if expression == nil {
|
|
expression = b.f.NewIdentifier(symbolName)
|
|
b.e.AddEmitFlags(expression, printer.EFNoAsciiEscaping)
|
|
// !!! TODO: smuggle type arguments out
|
|
// if (typeParameterNodes) setIdentifierTypeArguments(identifier, factory.createNodeArray<TypeNode | TypeParameterDeclaration>(typeParameterNodes));
|
|
// identifier.symbol = symbol;
|
|
// expression = identifier;
|
|
}
|
|
return b.f.NewElementAccessExpression(b.createExpressionFromSymbolChain(chain, index-1), nil, expression, ast.NodeFlagsNone)
|
|
}
|
|
|
|
func canUsePropertyAccess(name string) bool {
|
|
if len(name) == 0 {
|
|
return false
|
|
}
|
|
// TODO: in strada, this only used `isIdentifierStart` on the first character, while this checks the whole string for validity
|
|
// - possible strada bug?
|
|
if strings.HasPrefix(name, "#") {
|
|
return len(name) > 1 && scanner.IsIdentifierText(name[1:], core.LanguageVariantStandard)
|
|
}
|
|
return scanner.IsIdentifierText(name, core.LanguageVariantStandard)
|
|
}
|
|
|
|
func startsWithSingleOrDoubleQuote(str string) bool {
|
|
return strings.HasPrefix(str, "'") || strings.HasPrefix(str, "\"")
|
|
}
|
|
|
|
func startsWithSquareBracket(str string) bool {
|
|
return strings.HasPrefix(str, "[")
|
|
}
|
|
|
|
func isDefaultBindingContext(location *ast.Node) bool {
|
|
return location.Kind == ast.KindSourceFile || ast.IsAmbientModule(location)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) getNameOfSymbolFromNameType(symbol *ast.Symbol) string {
|
|
if b.ch.valueSymbolLinks.Has(symbol) {
|
|
nameType := b.ch.valueSymbolLinks.Get(symbol).nameType
|
|
if nameType == nil {
|
|
return ""
|
|
}
|
|
if nameType.flags&TypeFlagsStringOrNumberLiteral != 0 {
|
|
var name string
|
|
switch v := nameType.AsLiteralType().value.(type) {
|
|
case string:
|
|
name = v
|
|
case jsnum.Number:
|
|
name = v.String()
|
|
}
|
|
if !scanner.IsIdentifierText(name, core.LanguageVariantStandard) && !isNumericLiteralName(name) {
|
|
return b.ch.valueToString(nameType.AsLiteralType().value)
|
|
}
|
|
if isNumericLiteralName(name) && strings.HasPrefix(name, "-") {
|
|
return fmt.Sprintf("[%s]", name)
|
|
}
|
|
return name
|
|
}
|
|
if nameType.flags&TypeFlagsUniqueESSymbol != 0 {
|
|
text := b.getNameOfSymbolAsWritten(nameType.AsUniqueESSymbolType().symbol)
|
|
return fmt.Sprintf("[%s]", text)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
/**
|
|
* Gets a human-readable name for a symbol.
|
|
* Should *not* be used for the right-hand side of a `.` -- use `symbolName(symbol)` for that instead.
|
|
*
|
|
* Unlike `symbolName(symbol)`, this will include quotes if the name is from a string literal.
|
|
* It will also use a representation of a number as written instead of a decimal form, e.g. `0o11` instead of `9`.
|
|
*/
|
|
func (b *nodeBuilderImpl) getNameOfSymbolAsWritten(symbol *ast.Symbol) string {
|
|
result, ok := b.ctx.remappedSymbolReferences[ast.GetSymbolId(symbol)]
|
|
if ok {
|
|
symbol = result
|
|
}
|
|
if symbol.Name == ast.InternalSymbolNameDefault && (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope == 0) &&
|
|
// If it's not the first part of an entity name, it must print as `default`
|
|
((b.ctx.flags&nodebuilder.FlagsInInitialEntityName == 0) ||
|
|
// if the symbol is synthesized, it will only be referenced externally it must print as `default`
|
|
len(symbol.Declarations) == 0 ||
|
|
// if not in the same binding context (source file, module declaration), it must print as `default`
|
|
(b.ctx.enclosingDeclaration != nil && ast.FindAncestor(symbol.Declarations[0], isDefaultBindingContext) != ast.FindAncestor(b.ctx.enclosingDeclaration, isDefaultBindingContext))) {
|
|
return "default"
|
|
}
|
|
if len(symbol.Declarations) > 0 {
|
|
name := core.FirstNonNil(symbol.Declarations, ast.GetNameOfDeclaration) // Try using a declaration with a name, first
|
|
if name != nil {
|
|
// !!! TODO: JS Object.defineProperty declarations
|
|
// if ast.IsCallExpression(declaration) && ast.IsBindableObjectDefinePropertyCall(declaration) {
|
|
// return symbol.Name
|
|
// }
|
|
if ast.IsComputedPropertyName(name) && symbol.CheckFlags&ast.CheckFlagsLate == 0 {
|
|
if b.ch.valueSymbolLinks.Has(symbol) && b.ch.valueSymbolLinks.Get(symbol).nameType != nil && b.ch.valueSymbolLinks.Get(symbol).nameType.flags&TypeFlagsStringOrNumberLiteral != 0 {
|
|
result := b.getNameOfSymbolFromNameType(symbol)
|
|
if len(result) > 0 {
|
|
return result
|
|
}
|
|
}
|
|
}
|
|
return scanner.DeclarationNameToString(name)
|
|
}
|
|
declaration := symbol.Declarations[0] // Declaration may be nameless, but we'll try anyway
|
|
if declaration.Parent != nil && declaration.Parent.Kind == ast.KindVariableDeclaration {
|
|
return scanner.DeclarationNameToString(declaration.Parent.AsVariableDeclaration().Name())
|
|
}
|
|
if ast.IsClassExpression(declaration) || ast.IsFunctionExpression(declaration) || ast.IsArrowFunction(declaration) {
|
|
if b.ctx != nil && !b.ctx.encounteredError && b.ctx.flags&nodebuilder.FlagsAllowAnonymousIdentifier == 0 {
|
|
b.ctx.encounteredError = true
|
|
}
|
|
switch declaration.Kind {
|
|
case ast.KindClassExpression:
|
|
return "(Anonymous class)"
|
|
case ast.KindFunctionExpression, ast.KindArrowFunction:
|
|
return "(Anonymous function)"
|
|
}
|
|
}
|
|
}
|
|
name := b.getNameOfSymbolFromNameType(symbol)
|
|
if len(name) > 0 {
|
|
return name
|
|
}
|
|
return symbol.Name
|
|
}
|
|
|
|
// The full set of type parameters for a generic class or interface type consists of its outer type parameters plus
|
|
// its locally declared type parameters.
|
|
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)...)
|
|
return result
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) lookupTypeParameterNodes(chain []*ast.Symbol, index int) *ast.TypeParameterList {
|
|
debug.Assert(chain != nil && 0 <= index && index < len(chain))
|
|
symbol := chain[index]
|
|
symbolId := ast.GetSymbolId(symbol)
|
|
if !b.ctx.hasCreatedTypeParameterSymbolList {
|
|
b.ctx.hasCreatedTypeParameterSymbolList = true
|
|
b.ctx.typeParameterSymbolList = make(map[ast.SymbolId]struct{})
|
|
}
|
|
_, ok := b.ctx.typeParameterSymbolList[symbolId]
|
|
if ok {
|
|
return nil
|
|
}
|
|
b.ctx.typeParameterSymbolList[symbolId] = struct{}{}
|
|
|
|
if b.ctx.flags&nodebuilder.FlagsWriteTypeParametersInQualifiedName != 0 && index < (len(chain)-1) {
|
|
parentSymbol := symbol
|
|
nextSymbol := chain[index+1]
|
|
|
|
if nextSymbol.CheckFlags&ast.CheckFlagsInstantiated != 0 {
|
|
targetSymbol := parentSymbol
|
|
if parentSymbol.Flags&ast.SymbolFlagsAlias != 0 {
|
|
targetSymbol = b.ch.resolveAlias(parentSymbol)
|
|
}
|
|
params := b.getTypeParametersOfClassOrInterface(targetSymbol)
|
|
targetMapper := b.ch.valueSymbolLinks.Get(nextSymbol).mapper
|
|
if targetMapper != nil {
|
|
params = core.Map(params, targetMapper.Map)
|
|
}
|
|
return b.mapToTypeNodes(params, false /*isBareList*/)
|
|
} else {
|
|
typeParameterNodes := b.typeParametersToTypeParameterDeclarations(symbol)
|
|
if len(typeParameterNodes) > 0 {
|
|
return b.f.NewNodeList(typeParameterNodes)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// TODO: move `lookupSymbolChain` and co to `symbolaccessibility.go` (but getSpecifierForModuleSymbol uses much context which makes that hard?)
|
|
func (b *nodeBuilderImpl) lookupSymbolChain(symbol *ast.Symbol, meaning ast.SymbolFlags, yieldModuleSymbol bool) []*ast.Symbol {
|
|
b.ctx.tracker.TrackSymbol(symbol, b.ctx.enclosingDeclaration, meaning)
|
|
return b.lookupSymbolChainWorker(symbol, meaning, yieldModuleSymbol)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) lookupSymbolChainWorker(symbol *ast.Symbol, meaning ast.SymbolFlags, yieldModuleSymbol bool) []*ast.Symbol {
|
|
// Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration.
|
|
var chain []*ast.Symbol
|
|
isTypeParameter := symbol.Flags&ast.SymbolFlagsTypeParameter != 0
|
|
if !isTypeParameter && (b.ctx.enclosingDeclaration != nil || b.ctx.flags&nodebuilder.FlagsUseFullyQualifiedType != 0) && (b.ctx.internalFlags&nodebuilder.InternalFlagsDoNotIncludeSymbolChain == 0) {
|
|
res := b.getSymbolChain(symbol, meaning /*endOfChain*/, true, yieldModuleSymbol)
|
|
chain = res
|
|
debug.CheckDefined(chain)
|
|
debug.Assert(len(chain) > 0)
|
|
} else {
|
|
chain = append(chain, symbol)
|
|
}
|
|
return chain
|
|
}
|
|
|
|
type sortedSymbolNamePair struct {
|
|
sym *ast.Symbol
|
|
name string
|
|
}
|
|
|
|
/** @param endOfChain Set to false for recursive calls; non-recursive calls should always output something. */
|
|
func (b *nodeBuilderImpl) getSymbolChain(symbol *ast.Symbol, meaning ast.SymbolFlags, endOfChain bool, yieldModuleSymbol bool) []*ast.Symbol {
|
|
accessibleSymbolChain := b.ch.getAccessibleSymbolChain(symbol, b.ctx.enclosingDeclaration, meaning, b.ctx.flags&nodebuilder.FlagsUseOnlyExternalAliasing != 0)
|
|
qualifierMeaning := meaning
|
|
if len(accessibleSymbolChain) > 1 {
|
|
qualifierMeaning = getQualifiedLeftMeaning(meaning)
|
|
}
|
|
if len(accessibleSymbolChain) == 0 ||
|
|
b.ch.needsQualification(accessibleSymbolChain[0], b.ctx.enclosingDeclaration, qualifierMeaning) {
|
|
// Go up and add our parent.
|
|
root := symbol
|
|
if len(accessibleSymbolChain) > 0 {
|
|
root = accessibleSymbolChain[0]
|
|
}
|
|
parents := b.ch.getContainersOfSymbol(root, b.ctx.enclosingDeclaration, meaning)
|
|
if len(parents) > 0 {
|
|
parentSpecifiers := core.Map(parents, func(symbol *ast.Symbol) sortedSymbolNamePair {
|
|
if core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) {
|
|
return sortedSymbolNamePair{symbol, b.getSpecifierForModuleSymbol(symbol, core.ResolutionModeNone)}
|
|
}
|
|
return sortedSymbolNamePair{symbol, ""}
|
|
})
|
|
slices.SortStableFunc(parentSpecifiers, b.sortByBestName)
|
|
for _, pair := range parentSpecifiers {
|
|
parent := pair.sym
|
|
parentChain := b.getSymbolChain(parent, getQualifiedLeftMeaning(meaning), false, yieldModuleSymbol)
|
|
if len(parentChain) > 0 {
|
|
if parent.Exports != nil {
|
|
exported, ok := parent.Exports[ast.InternalSymbolNameExportEquals]
|
|
if ok && b.ch.getSymbolIfSameReference(exported, symbol) != nil {
|
|
// parentChain root _is_ symbol - symbol is a module export=, so it kinda looks like it's own parent
|
|
// No need to lookup an alias for the symbol in itself
|
|
accessibleSymbolChain = parentChain
|
|
break
|
|
}
|
|
}
|
|
nextSyms := accessibleSymbolChain
|
|
if len(nextSyms) == 0 {
|
|
fallback := b.ch.getAliasForSymbolInContainer(parent, symbol)
|
|
if fallback == nil {
|
|
fallback = symbol
|
|
}
|
|
nextSyms = append(nextSyms, fallback)
|
|
}
|
|
accessibleSymbolChain = append(parentChain, nextSyms...)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if len(accessibleSymbolChain) > 0 {
|
|
return accessibleSymbolChain
|
|
}
|
|
if
|
|
// If this is the last part of outputting the symbol, always output. The cases apply only to parent symbols.
|
|
endOfChain ||
|
|
// If a parent symbol is an anonymous type, don't write it.
|
|
(symbol.Flags&(ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsObjectLiteral) == 0) {
|
|
// If a parent symbol is an external module, don't write it. (We prefer just `x` vs `"foo/bar".x`.)
|
|
if !endOfChain && !yieldModuleSymbol && core.Some(symbol.Declarations, hasNonGlobalAugmentationExternalModuleSymbol) {
|
|
return nil
|
|
}
|
|
return []*ast.Symbol{symbol}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b_ *nodeBuilderImpl) sortByBestName(a sortedSymbolNamePair, b sortedSymbolNamePair) int {
|
|
specifierA := a.name
|
|
specifierB := b.name
|
|
if len(specifierA) > 0 && len(specifierB) > 0 {
|
|
isBRelative := tspath.PathIsRelative(specifierB)
|
|
if tspath.PathIsRelative(specifierA) == isBRelative {
|
|
// Both relative or both non-relative, sort by number of parts
|
|
return modulespecifiers.CountPathComponents(specifierA) - modulespecifiers.CountPathComponents(specifierB)
|
|
}
|
|
if isBRelative {
|
|
// A is non-relative, B is relative: prefer A
|
|
return -1
|
|
}
|
|
// A is relative, B is non-relative: prefer B
|
|
return 1
|
|
}
|
|
return b_.ch.compareSymbols(a.sym, b.sym) // must sort symbols for stable ordering
|
|
}
|
|
|
|
func isAmbientModuleSymbolName(s string) bool {
|
|
return strings.HasPrefix(s, "\"") && strings.HasSuffix(s, "\"")
|
|
}
|
|
|
|
func canHaveModuleSpecifier(node *ast.Node) bool {
|
|
if node == nil {
|
|
return false
|
|
}
|
|
switch node.Kind {
|
|
case ast.KindVariableDeclaration,
|
|
ast.KindBindingElement,
|
|
ast.KindImportDeclaration,
|
|
ast.KindExportDeclaration,
|
|
ast.KindImportEqualsDeclaration,
|
|
ast.KindImportClause,
|
|
ast.KindNamespaceExport,
|
|
ast.KindNamespaceImport,
|
|
ast.KindExportSpecifier,
|
|
ast.KindImportSpecifier,
|
|
ast.KindImportType:
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func TryGetModuleSpecifierFromDeclaration(node *ast.Node) *ast.Node {
|
|
res := tryGetModuleSpecifierFromDeclarationWorker(node)
|
|
if res == nil || !ast.IsStringLiteral(res) {
|
|
return nil
|
|
}
|
|
return res
|
|
}
|
|
|
|
func tryGetModuleSpecifierFromDeclarationWorker(node *ast.Node) *ast.Node {
|
|
switch node.Kind {
|
|
case ast.KindVariableDeclaration, ast.KindBindingElement:
|
|
requireCall := ast.FindAncestor(node.Initializer(), func(node *ast.Node) bool {
|
|
return ast.IsRequireCall(node, true /*requireStringLiteralLikeArgument*/)
|
|
})
|
|
if requireCall == nil {
|
|
return nil
|
|
}
|
|
return requireCall.AsCallExpression().Arguments.Nodes[0]
|
|
case ast.KindImportDeclaration:
|
|
return node.AsImportDeclaration().ModuleSpecifier
|
|
case ast.KindExportDeclaration:
|
|
return node.AsExportDeclaration().ModuleSpecifier
|
|
case ast.KindJSDocImportTag:
|
|
return node.AsJSDocImportTag().ModuleSpecifier
|
|
case ast.KindImportEqualsDeclaration:
|
|
ref := node.AsImportEqualsDeclaration().ModuleReference
|
|
if ref.Kind != ast.KindExternalModuleReference {
|
|
return nil
|
|
}
|
|
return ref.AsExternalModuleReference().Expression
|
|
case ast.KindImportClause:
|
|
if ast.IsImportDeclaration(node.Parent) {
|
|
return node.Parent.AsImportDeclaration().ModuleSpecifier
|
|
}
|
|
return node.Parent.AsJSDocImportTag().ModuleSpecifier
|
|
case ast.KindNamespaceExport:
|
|
return node.Parent.AsExportDeclaration().ModuleSpecifier
|
|
case ast.KindNamespaceImport:
|
|
if ast.IsImportDeclaration(node.Parent.Parent) {
|
|
return node.Parent.Parent.AsImportDeclaration().ModuleSpecifier
|
|
}
|
|
return node.Parent.Parent.AsJSDocImportTag().ModuleSpecifier
|
|
case ast.KindExportSpecifier:
|
|
return node.Parent.Parent.AsExportDeclaration().ModuleSpecifier
|
|
case ast.KindImportSpecifier:
|
|
if ast.IsImportDeclaration(node.Parent.Parent.Parent) {
|
|
return node.Parent.Parent.Parent.AsImportDeclaration().ModuleSpecifier
|
|
}
|
|
return node.Parent.Parent.Parent.AsJSDocImportTag().ModuleSpecifier
|
|
case ast.KindImportType:
|
|
if ast.IsLiteralImportTypeNode(node) {
|
|
return node.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal
|
|
}
|
|
return nil
|
|
default:
|
|
debug.AssertNever(node)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) getSpecifierForModuleSymbol(symbol *ast.Symbol, overrideImportMode core.ResolutionMode) string {
|
|
file := ast.GetDeclarationOfKind(symbol, ast.KindSourceFile)
|
|
if file == nil {
|
|
equivalentSymbol := core.FirstNonNil(symbol.Declarations, func(d *ast.Node) *ast.Symbol {
|
|
return b.ch.getFileSymbolIfFileSymbolExportEqualsContainer(d, symbol)
|
|
})
|
|
if equivalentSymbol != nil {
|
|
file = ast.GetDeclarationOfKind(equivalentSymbol, ast.KindSourceFile)
|
|
}
|
|
}
|
|
|
|
if file == nil {
|
|
if isAmbientModuleSymbolName(symbol.Name) {
|
|
return stringutil.StripQuotes(symbol.Name)
|
|
}
|
|
}
|
|
if b.ctx.enclosingFile == nil || b.ctx.tracker.GetModuleSpecifierGenerationHost() == nil {
|
|
if isAmbientModuleSymbolName(symbol.Name) {
|
|
return stringutil.StripQuotes(symbol.Name)
|
|
}
|
|
return ast.GetSourceFileOfModule(symbol).FileName()
|
|
}
|
|
|
|
enclosingDeclaration := b.e.MostOriginal(b.ctx.enclosingDeclaration)
|
|
var originalModuleSpecifier *ast.Node
|
|
if canHaveModuleSpecifier(enclosingDeclaration) {
|
|
originalModuleSpecifier = TryGetModuleSpecifierFromDeclaration(enclosingDeclaration)
|
|
}
|
|
contextFile := b.ctx.enclosingFile
|
|
resolutionMode := overrideImportMode
|
|
if resolutionMode == core.ResolutionModeNone && originalModuleSpecifier != nil {
|
|
resolutionMode = b.ch.program.GetModeForUsageLocation(contextFile, originalModuleSpecifier)
|
|
} else if resolutionMode == core.ResolutionModeNone && contextFile != nil {
|
|
resolutionMode = b.ch.program.GetDefaultResolutionModeForFile(contextFile)
|
|
}
|
|
cacheKey := module.ModeAwareCacheKey{Name: string(contextFile.Path()), Mode: resolutionMode}
|
|
links := b.symbolLinks.Get(symbol)
|
|
if links.specifierCache == nil {
|
|
links.specifierCache = make(module.ModeAwareCache[string])
|
|
}
|
|
result, ok := links.specifierCache[cacheKey]
|
|
if ok {
|
|
return result
|
|
}
|
|
isBundle := false // !!! remove me
|
|
// For declaration bundles, we need to generate absolute paths relative to the common source dir for imports,
|
|
// just like how the declaration emitter does for the ambient module declarations - we can easily accomplish this
|
|
// using the `baseUrl` compiler option (which we would otherwise never use in declaration emit) and a non-relative
|
|
// specifier preference
|
|
host := b.ctx.tracker.GetModuleSpecifierGenerationHost()
|
|
specifierCompilerOptions := b.ch.compilerOptions
|
|
specifierPref := modulespecifiers.ImportModuleSpecifierPreferenceProjectRelative
|
|
endingPref := modulespecifiers.ImportModuleSpecifierEndingPreferenceNone
|
|
if resolutionMode == core.ResolutionModeESM {
|
|
endingPref = modulespecifiers.ImportModuleSpecifierEndingPreferenceJs
|
|
}
|
|
if isBundle {
|
|
// !!! relies on option cloning and specifier host implementation
|
|
// specifierCompilerOptions = &core.CompilerOptions{BaseUrl: host.CommonSourceDirectory()}
|
|
// TODO: merge with b.ch.compilerOptions
|
|
specifierPref = modulespecifiers.ImportModuleSpecifierPreferenceNonRelative
|
|
endingPref = modulespecifiers.ImportModuleSpecifierEndingPreferenceMinimal
|
|
}
|
|
|
|
allSpecifiers := modulespecifiers.GetModuleSpecifiers(
|
|
symbol,
|
|
b.ch,
|
|
specifierCompilerOptions,
|
|
contextFile,
|
|
host,
|
|
modulespecifiers.UserPreferences{
|
|
ImportModuleSpecifierPreference: specifierPref,
|
|
ImportModuleSpecifierEnding: endingPref,
|
|
},
|
|
modulespecifiers.ModuleSpecifierOptions{
|
|
OverrideImportMode: overrideImportMode,
|
|
},
|
|
false, /*forAutoImports*/
|
|
)
|
|
specifier := allSpecifiers[0]
|
|
links.specifierCache[cacheKey] = specifier
|
|
return specifier
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) typeParameterToDeclarationWithConstraint(typeParameter *Type, constraintNode *ast.TypeNode) *ast.TypeParameterDeclarationNode {
|
|
restoreFlags := b.saveRestoreFlags()
|
|
b.ctx.flags &= ^nodebuilder.FlagsWriteTypeParametersInQualifiedName // Avoids potential infinite loop when building for a claimspace with a generic
|
|
modifiers := ast.CreateModifiersFromModifierFlags(b.ch.getTypeParameterModifiers(typeParameter), b.f.NewModifier)
|
|
var modifiersList *ast.ModifierList
|
|
if len(modifiers) > 0 {
|
|
modifiersList = b.f.NewModifierList(modifiers)
|
|
}
|
|
name := b.typeParameterToName(typeParameter)
|
|
defaultParameter := b.ch.getDefaultFromTypeParameter(typeParameter)
|
|
var defaultParameterNode *ast.Node
|
|
if defaultParameter != nil {
|
|
defaultParameterNode = b.typeToTypeNode(defaultParameter)
|
|
}
|
|
restoreFlags()
|
|
return b.f.NewTypeParameterDeclaration(
|
|
modifiersList,
|
|
name.AsNode(),
|
|
constraintNode,
|
|
defaultParameterNode,
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Unlike the utilities `setTextRange`, this checks if the `location` we're trying to set on `range` is within the
|
|
* same file as the active context. If not, the range is not applied. This prevents us from copying ranges across files,
|
|
* which will confuse the node printer (as it assumes all node ranges are within the current file).
|
|
* Additionally, if `range` _isn't synthetic_, or isn't in the current file, it will _copy_ it to _remove_ its' position
|
|
* information.
|
|
*
|
|
* It also calls `setOriginalNode` to setup a `.original` pointer, since you basically *always* want these in the node builder.
|
|
*/
|
|
func (b *nodeBuilderImpl) setTextRange(range_ *ast.Node, location *ast.Node) *ast.Node {
|
|
if range_ == nil {
|
|
return range_
|
|
}
|
|
if !ast.NodeIsSynthesized(range_) || (range_.Flags&ast.NodeFlagsSynthesized == 0) || b.ctx.enclosingFile == nil || b.ctx.enclosingFile != ast.GetSourceFileOfNode(b.e.MostOriginal(range_)) {
|
|
range_ = range_.Clone(b.f) // if `range` is synthesized or originates in another file, copy it so it definitely has synthetic positions
|
|
}
|
|
if range_ == location || location == nil {
|
|
return range_
|
|
}
|
|
// Don't overwrite the original node if `range` has an `original` node that points either directly or indirectly to `location`
|
|
original := b.e.Original(range_)
|
|
for original != nil && original != location {
|
|
original = b.e.Original(original)
|
|
}
|
|
if original == nil {
|
|
b.e.SetOriginalEx(range_, location, true)
|
|
}
|
|
|
|
// only set positions if range comes from the same file since copying text across files isn't supported by the emitter
|
|
if b.ctx.enclosingFile != nil && b.ctx.enclosingFile == ast.GetSourceFileOfNode(b.e.MostOriginal(range_)) {
|
|
range_.Loc = location.Loc
|
|
return range_
|
|
}
|
|
return range_
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) typeParameterShadowsOtherTypeParameterInScope(name string, typeParameter *Type) bool {
|
|
result := b.ch.resolveName(b.ctx.enclosingDeclaration, name, ast.SymbolFlagsType, nil, false, false)
|
|
if result != nil && result.Flags&ast.SymbolFlagsTypeParameter != 0 {
|
|
return result != typeParameter.symbol
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) typeParameterToName(typeParameter *Type) *ast.Identifier {
|
|
if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.ctx.typeParameterNames != nil {
|
|
cached, ok := b.ctx.typeParameterNames[typeParameter.id]
|
|
if ok {
|
|
return cached
|
|
}
|
|
}
|
|
result := b.symbolToName(typeParameter.symbol, ast.SymbolFlagsType /*expectsIdentifier*/, true)
|
|
if !ast.IsIdentifier(result) {
|
|
return b.f.NewIdentifier("(Missing type parameter)").AsIdentifier()
|
|
}
|
|
if typeParameter.symbol != nil && len(typeParameter.symbol.Declarations) > 0 {
|
|
decl := typeParameter.symbol.Declarations[0]
|
|
if decl != nil && ast.IsTypeParameterDeclaration(decl) {
|
|
result = b.setTextRange(result, decl.Name())
|
|
}
|
|
}
|
|
if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 {
|
|
if !b.ctx.hasCreatedTypeParametersNamesLookups {
|
|
b.ctx.hasCreatedTypeParametersNamesLookups = true
|
|
b.ctx.typeParameterNames = make(map[TypeId]*ast.Identifier)
|
|
b.ctx.typeParameterNamesByText = make(map[string]struct{})
|
|
b.ctx.typeParameterNamesByTextNextNameCount = make(map[string]int)
|
|
}
|
|
|
|
rawText := result.AsIdentifier().Text
|
|
i := 0
|
|
cached, ok := b.ctx.typeParameterNamesByTextNextNameCount[rawText]
|
|
if ok {
|
|
i = cached
|
|
}
|
|
text := rawText
|
|
|
|
for true {
|
|
_, present := b.ctx.typeParameterNamesByText[text]
|
|
if !present && !b.typeParameterShadowsOtherTypeParameterInScope(text, typeParameter) {
|
|
break
|
|
}
|
|
i++
|
|
text = fmt.Sprintf("%s_%d", rawText, i)
|
|
}
|
|
|
|
if text != rawText {
|
|
// !!! TODO: smuggle type arguments out
|
|
// const typeArguments = getIdentifierTypeArguments(result);
|
|
result = b.f.NewIdentifier(text)
|
|
// setIdentifierTypeArguments(result, typeArguments);
|
|
}
|
|
|
|
// avoiding iterations of the above loop turns out to be worth it when `i` starts to get large, so we cache the max
|
|
// `i` we've used thus far, to save work later
|
|
b.ctx.typeParameterNamesByTextNextNameCount[rawText] = i
|
|
b.ctx.typeParameterNames[typeParameter.id] = result.AsIdentifier()
|
|
b.ctx.typeParameterNamesByText[text] = struct{}{}
|
|
}
|
|
|
|
return result.AsIdentifier()
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) isMappedTypeHomomorphic(mapped *Type) bool {
|
|
return b.ch.getHomomorphicTypeVariable(mapped) != nil
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped *MappedType) bool {
|
|
return mapped.target != nil && !b.isMappedTypeHomomorphic(mapped.AsType()) && b.isMappedTypeHomomorphic(mapped.target)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) createMappedTypeNodeFromType(t *Type) *ast.TypeNode {
|
|
debug.Assert(t.Flags()&TypeFlagsObject != 0)
|
|
mapped := t.AsMappedType()
|
|
var readonlyToken *ast.Node
|
|
if mapped.declaration.ReadonlyToken != nil {
|
|
readonlyToken = b.f.NewToken(mapped.declaration.ReadonlyToken.Kind)
|
|
}
|
|
var questionToken *ast.Node
|
|
if mapped.declaration.QuestionToken != nil {
|
|
questionToken = b.f.NewToken(mapped.declaration.QuestionToken.Kind)
|
|
}
|
|
var appropriateConstraintTypeNode *ast.Node
|
|
var newTypeVariable *ast.Node
|
|
templateType := b.ch.getTemplateTypeFromMappedType(t)
|
|
typeParameter := b.ch.getTypeParameterFromMappedType(t)
|
|
|
|
// If the mapped type isn't `keyof` constraint-declared, _but_ still has modifiers preserved, and its naive instantiation won't preserve modifiers because its constraint isn't `keyof` constrained, we have work to do
|
|
needsModifierPreservingWrapper := !b.ch.isMappedTypeWithKeyofConstraintDeclaration(t) &&
|
|
b.ch.getModifiersTypeFromMappedType(t).flags&TypeFlagsUnknown == 0 &&
|
|
b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 &&
|
|
!(b.ch.getConstraintTypeFromMappedType(t).flags&TypeFlagsTypeParameter != 0 && b.ch.getConstraintOfTypeParameter(b.ch.getConstraintTypeFromMappedType(t)).flags&TypeFlagsIndex != 0)
|
|
|
|
if b.ch.isMappedTypeWithKeyofConstraintDeclaration(t) {
|
|
// We have a { [P in keyof T]: X }
|
|
// We do this to ensure we retain the toplevel keyof-ness of the type which may be lost due to keyof distribution during `getConstraintTypeFromMappedType`
|
|
if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped) {
|
|
newConstraintParam := b.ch.newTypeParameter(
|
|
b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T"),
|
|
)
|
|
name := b.typeParameterToName(newConstraintParam)
|
|
target := t.Target()
|
|
newTypeVariable = b.f.NewTypeReferenceNode(name.AsNode(), nil)
|
|
templateType = b.ch.instantiateType(b.ch.getTemplateTypeFromMappedType(target), newTypeMapper([]*Type{b.ch.getTypeParameterFromMappedType(target), b.ch.getModifiersTypeFromMappedType(target)}, []*Type{typeParameter, newConstraintParam}))
|
|
}
|
|
indexTarget := newTypeVariable
|
|
if indexTarget == nil {
|
|
indexTarget = b.typeToTypeNode(b.ch.getModifiersTypeFromMappedType(t))
|
|
}
|
|
appropriateConstraintTypeNode = b.f.NewTypeOperatorNode(ast.KindKeyOfKeyword, indexTarget)
|
|
} else if needsModifierPreservingWrapper {
|
|
// So, step 1: new type variable
|
|
newParam := b.ch.newTypeParameter(
|
|
b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T"),
|
|
)
|
|
name := b.typeParameterToName(newParam)
|
|
newTypeVariable = b.f.NewTypeReferenceNode(name.AsNode(), nil)
|
|
// step 2: make that new type variable itself the constraint node, making the mapped type `{[K in T_1]: Template}`
|
|
appropriateConstraintTypeNode = newTypeVariable
|
|
} else {
|
|
appropriateConstraintTypeNode = b.typeToTypeNode(b.ch.getConstraintTypeFromMappedType(t))
|
|
}
|
|
|
|
// nameType and templateType nodes have to be in the new scope
|
|
cleanup := b.enterNewScope(mapped.declaration.AsNode(), nil, []*Type{b.ch.getTypeParameterFromMappedType(t)}, nil, nil)
|
|
typeParameterNode := b.typeParameterToDeclarationWithConstraint(typeParameter, appropriateConstraintTypeNode)
|
|
var nameTypeNode *ast.Node
|
|
if mapped.declaration.NameType != nil {
|
|
nameTypeNode = b.typeToTypeNode(b.ch.getNameTypeFromMappedType(t))
|
|
}
|
|
templateTypeNode := b.typeToTypeNode(b.ch.removeMissingType(
|
|
templateType,
|
|
getMappedTypeModifiers(t)&MappedTypeModifiersIncludeOptional != 0,
|
|
))
|
|
cleanup()
|
|
result := b.f.NewMappedTypeNode(
|
|
readonlyToken,
|
|
typeParameterNode,
|
|
nameTypeNode,
|
|
questionToken,
|
|
templateTypeNode,
|
|
nil,
|
|
)
|
|
b.ctx.approximateLength += 10
|
|
b.e.AddEmitFlags(result, printer.EFSingleLine)
|
|
|
|
if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && b.isHomomorphicMappedTypeWithNonHomomorphicInstantiation(mapped) {
|
|
// homomorphic mapped type with a non-homomorphic naive inlining
|
|
// wrap it with a conditional like `SomeModifiersType extends infer U ? {..the mapped type...} : never` to ensure the resulting
|
|
// type stays homomorphic
|
|
|
|
rawConstraintTypeFromDeclaration := b.getTypeFromTypeNode(mapped.declaration.TypeParameter.AsTypeParameter().Constraint.AsTypeOperatorNode().Type, false)
|
|
if rawConstraintTypeFromDeclaration != nil {
|
|
rawConstraintTypeFromDeclaration = b.ch.getConstraintOfTypeParameter(rawConstraintTypeFromDeclaration)
|
|
}
|
|
if rawConstraintTypeFromDeclaration == nil {
|
|
rawConstraintTypeFromDeclaration = b.ch.unknownType
|
|
}
|
|
originalConstraint := b.ch.instantiateType(rawConstraintTypeFromDeclaration, mapped.mapper)
|
|
|
|
var originalConstraintNode *ast.Node
|
|
if originalConstraint.flags&TypeFlagsUnknown == 0 {
|
|
originalConstraintNode = b.typeToTypeNode(originalConstraint)
|
|
}
|
|
|
|
return b.f.NewConditionalTypeNode(
|
|
b.typeToTypeNode(b.ch.getModifiersTypeFromMappedType(t)),
|
|
b.f.NewInferTypeNode(b.f.NewTypeParameterDeclaration(nil, newTypeVariable.AsTypeReference().TypeName.Clone(b.f), originalConstraintNode, nil)),
|
|
result,
|
|
b.f.NewKeywordTypeNode(ast.KindNeverKeyword),
|
|
)
|
|
} else if needsModifierPreservingWrapper {
|
|
// and step 3: once the mapped type is reconstructed, create a `ConstraintType extends infer T_1 extends keyof ModifiersType ? {[K in T_1]: Template} : never`
|
|
// subtly different from the `keyof` constraint case, by including the `keyof` constraint on the `infer` type parameter, it doesn't rely on the constraint type being itself
|
|
// constrained to a `keyof` type to preserve its modifier-preserving behavior. This is all basically because we preserve modifiers for a wider set of mapped types than
|
|
// just homomorphic ones.
|
|
return b.f.NewConditionalTypeNode(
|
|
b.typeToTypeNode(b.ch.getConstraintTypeFromMappedType(t)),
|
|
b.f.NewInferTypeNode(b.f.NewTypeParameterDeclaration(nil, newTypeVariable.AsTypeReference().TypeName.Clone(b.f), b.f.NewTypeOperatorNode(ast.KindKeyOfKeyword, b.typeToTypeNode(b.ch.getModifiersTypeFromMappedType(t))), nil)),
|
|
result,
|
|
b.f.NewKeywordTypeNode(ast.KindNeverKeyword),
|
|
)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) typePredicateToTypePredicateNode(predicate *TypePredicate) *ast.Node {
|
|
var assertsModifier *ast.Node
|
|
if predicate.kind == TypePredicateKindAssertsIdentifier || predicate.kind == TypePredicateKindAssertsThis {
|
|
assertsModifier = b.f.NewToken(ast.KindAssertsKeyword)
|
|
}
|
|
var parameterName *ast.Node
|
|
if predicate.kind == TypePredicateKindIdentifier || predicate.kind == TypePredicateKindAssertsIdentifier {
|
|
parameterName = b.f.NewIdentifier(predicate.parameterName)
|
|
b.e.AddEmitFlags(parameterName, printer.EFNoAsciiEscaping)
|
|
} else {
|
|
parameterName = b.f.NewThisTypeNode()
|
|
}
|
|
var typeNode *ast.Node
|
|
if predicate.t != nil {
|
|
typeNode = b.typeToTypeNode(predicate.t)
|
|
}
|
|
return b.f.NewTypePredicateNode(
|
|
assertsModifier,
|
|
parameterName,
|
|
typeNode,
|
|
)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) typeToTypeNodeHelperWithPossibleReusableTypeNode(t *Type, typeNode *ast.TypeNode) *ast.TypeNode {
|
|
if t == nil {
|
|
return b.f.NewKeywordTypeNode(ast.KindAnyKeyword)
|
|
}
|
|
if typeNode != nil && b.getTypeFromTypeNode(typeNode, false) == t {
|
|
reused := b.tryReuseExistingTypeNodeHelper(typeNode)
|
|
if reused != nil {
|
|
return reused
|
|
}
|
|
}
|
|
return b.typeToTypeNode(t)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) typeParameterToDeclaration(parameter *Type) *ast.Node {
|
|
constraint := b.ch.getConstraintOfTypeParameter(parameter)
|
|
var constraintNode *ast.Node
|
|
if constraint != nil {
|
|
constraintNode = b.typeToTypeNodeHelperWithPossibleReusableTypeNode(constraint, b.ch.getConstraintDeclaration(parameter))
|
|
}
|
|
return b.typeParameterToDeclarationWithConstraint(parameter, constraintNode)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) symbolToTypeParameterDeclarations(symbol *ast.Symbol) []*ast.Node {
|
|
return b.typeParametersToTypeParameterDeclarations(symbol)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) typeParametersToTypeParameterDeclarations(symbol *ast.Symbol) []*ast.Node {
|
|
targetSymbol := b.ch.getTargetSymbol(symbol)
|
|
if targetSymbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface|ast.SymbolFlagsAlias) != 0 {
|
|
var results []*ast.Node
|
|
params := b.ch.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
|
|
for _, param := range params {
|
|
results = append(results, b.typeParameterToDeclaration(param))
|
|
}
|
|
return results
|
|
} else if targetSymbol.Flags&ast.SymbolFlagsFunction != 0 {
|
|
var results []*ast.Node
|
|
for _, param := range b.ch.getTypeParametersFromDeclaration(symbol.ValueDeclaration) {
|
|
results = append(results, b.typeParameterToDeclaration(param))
|
|
}
|
|
return results
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getEffectiveParameterDeclaration(symbol *ast.Symbol) *ast.Node {
|
|
parameterDeclaration := ast.GetDeclarationOfKind(symbol, ast.KindParameter)
|
|
if parameterDeclaration != nil {
|
|
return parameterDeclaration
|
|
}
|
|
if symbol.Flags&ast.SymbolFlagsTransient == 0 {
|
|
return ast.GetDeclarationOfKind(symbol, ast.KindJSDocParameterTag)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) symbolToParameterDeclaration(parameterSymbol *ast.Symbol, preserveModifierFlags bool) *ast.Node {
|
|
parameterDeclaration := getEffectiveParameterDeclaration(parameterSymbol)
|
|
|
|
parameterType := b.ch.getTypeOfSymbol(parameterSymbol)
|
|
parameterTypeNode := b.serializeTypeForDeclaration(parameterDeclaration, parameterType, parameterSymbol)
|
|
var modifiers *ast.ModifierList
|
|
if b.ctx.flags&nodebuilder.FlagsOmitParameterModifiers == 0 && preserveModifierFlags && parameterDeclaration != nil && ast.CanHaveModifiers(parameterDeclaration) {
|
|
originals := core.Filter(parameterDeclaration.Modifiers().Nodes, ast.IsModifier)
|
|
clones := core.Map(originals, func(node *ast.Node) *ast.Node { return node.Clone(b.f) })
|
|
if len(clones) > 0 {
|
|
modifiers = b.f.NewModifierList(clones)
|
|
}
|
|
}
|
|
isRest := parameterDeclaration != nil && isRestParameter(parameterDeclaration) || parameterSymbol.CheckFlags&ast.CheckFlagsRestParameter != 0
|
|
var dotDotDotToken *ast.Node
|
|
if isRest {
|
|
dotDotDotToken = b.f.NewToken(ast.KindDotDotDotToken)
|
|
}
|
|
name := b.parameterToParameterDeclarationName(parameterSymbol, parameterDeclaration)
|
|
// TODO: isOptionalParameter on emit resolver here is silly - hoist to checker and reexpose on emit resolver?
|
|
isOptional := parameterDeclaration != nil && b.ch.GetEmitResolver().isOptionalParameter(parameterDeclaration) || parameterSymbol.CheckFlags&ast.CheckFlagsOptionalParameter != 0
|
|
var questionToken *ast.Node
|
|
if isOptional {
|
|
questionToken = b.f.NewToken(ast.KindQuestionToken)
|
|
}
|
|
|
|
parameterNode := b.f.NewParameterDeclaration(
|
|
modifiers,
|
|
dotDotDotToken,
|
|
name,
|
|
questionToken,
|
|
parameterTypeNode,
|
|
/*initializer*/ nil,
|
|
)
|
|
b.ctx.approximateLength += len(parameterSymbol.Name) + 3
|
|
return parameterNode
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) parameterToParameterDeclarationName(parameterSymbol *ast.Symbol, parameterDeclaration *ast.Node) *ast.Node {
|
|
if parameterDeclaration == nil || parameterDeclaration.Name() == nil {
|
|
return b.f.NewIdentifier(parameterSymbol.Name)
|
|
}
|
|
|
|
name := parameterDeclaration.Name()
|
|
switch name.Kind {
|
|
case ast.KindIdentifier:
|
|
cloned := b.f.DeepCloneNode(name)
|
|
b.e.SetEmitFlags(cloned, printer.EFNoAsciiEscaping)
|
|
return cloned
|
|
case ast.KindQualifiedName:
|
|
cloned := b.f.DeepCloneNode(name.AsQualifiedName().Right)
|
|
b.e.SetEmitFlags(cloned, printer.EFNoAsciiEscaping)
|
|
return cloned
|
|
default:
|
|
return b.cloneBindingName(name)
|
|
}
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) cloneBindingName(node *ast.Node) *ast.Node {
|
|
if ast.IsComputedPropertyName(node) && b.ch.isLateBindableName(node) {
|
|
b.trackComputedName(node.Expression(), b.ctx.enclosingDeclaration)
|
|
}
|
|
|
|
visited := b.cloneBindingNameVisitor.VisitEachChild(node)
|
|
|
|
if ast.IsBindingElement(visited) {
|
|
bindingElement := visited.AsBindingElement()
|
|
visited = b.f.UpdateBindingElement(
|
|
bindingElement,
|
|
bindingElement.DotDotDotToken,
|
|
bindingElement.PropertyName,
|
|
bindingElement.Name(),
|
|
nil, // remove initializer
|
|
)
|
|
}
|
|
|
|
if !ast.NodeIsSynthesized(visited) {
|
|
visited = b.f.DeepCloneNode(visited)
|
|
}
|
|
|
|
b.e.SetEmitFlags(visited, printer.EFSingleLine|printer.EFNoAsciiEscaping)
|
|
return visited
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) symbolTableToDeclarationStatements(symbolTable *ast.SymbolTable) []*ast.Node {
|
|
panic("unimplemented") // !!!
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) serializeTypeForExpression(expr *ast.Node) *ast.Node {
|
|
// !!! TODO: shim, add node reuse
|
|
t := b.ch.instantiateType(b.ch.getWidenedType(b.ch.getRegularTypeOfExpression(expr)), b.ctx.mapper)
|
|
return b.typeToTypeNode(t)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) serializeInferredReturnTypeForSignature(signature *Signature, returnType *Type) *ast.Node {
|
|
oldSuppressReportInferenceFallback := b.ctx.suppressReportInferenceFallback
|
|
b.ctx.suppressReportInferenceFallback = true
|
|
typePredicate := b.ch.getTypePredicateOfSignature(signature)
|
|
var returnTypeNode *ast.Node
|
|
if typePredicate != nil {
|
|
var predicate *TypePredicate
|
|
if b.ctx.mapper != nil {
|
|
predicate = b.ch.instantiateTypePredicate(typePredicate, b.ctx.mapper)
|
|
} else {
|
|
predicate = typePredicate
|
|
}
|
|
returnTypeNode = b.typePredicateToTypePredicateNodeHelper(predicate)
|
|
} else {
|
|
returnTypeNode = b.typeToTypeNode(returnType)
|
|
}
|
|
b.ctx.suppressReportInferenceFallback = oldSuppressReportInferenceFallback
|
|
return returnTypeNode
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) typePredicateToTypePredicateNodeHelper(typePredicate *TypePredicate) *ast.Node {
|
|
var assertsModifier *ast.Node
|
|
if typePredicate.kind == TypePredicateKindAssertsThis || typePredicate.kind == TypePredicateKindAssertsIdentifier {
|
|
assertsModifier = b.f.NewToken(ast.KindAssertsKeyword)
|
|
} else {
|
|
assertsModifier = nil
|
|
}
|
|
var parameterName *ast.Node
|
|
if typePredicate.kind == TypePredicateKindIdentifier || typePredicate.kind == TypePredicateKindAssertsIdentifier {
|
|
parameterName = b.f.NewIdentifier(typePredicate.parameterName)
|
|
b.e.SetEmitFlags(parameterName, printer.EFNoAsciiEscaping)
|
|
} else {
|
|
parameterName = b.f.NewThisTypeNode()
|
|
}
|
|
var typeNode *ast.Node
|
|
if typePredicate.t != nil {
|
|
typeNode = b.typeToTypeNode(typePredicate.t)
|
|
}
|
|
return b.f.NewTypePredicateNode(assertsModifier, parameterName, typeNode)
|
|
}
|
|
|
|
type SignatureToSignatureDeclarationOptions struct {
|
|
modifiers []*ast.Node
|
|
name *ast.PropertyName
|
|
questionToken *ast.Node
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) signatureToSignatureDeclarationHelper(signature *Signature, kind ast.Kind, options *SignatureToSignatureDeclarationOptions) *ast.Node {
|
|
var typeParameters []*ast.Node
|
|
|
|
expandedParams := b.ch.getExpandedParameters(signature, true /*skipUnionExpanding*/)[0]
|
|
cleanup := b.enterNewScope(signature.declaration, expandedParams, signature.typeParameters, signature.parameters, signature.mapper)
|
|
b.ctx.approximateLength += 3
|
|
// Usually a signature contributes a few more characters than this, but 3 is the minimum
|
|
|
|
if b.ctx.flags&nodebuilder.FlagsWriteTypeArgumentsOfSignature != 0 && signature.target != nil && signature.mapper != nil && len(signature.target.typeParameters) != 0 {
|
|
for _, parameter := range signature.target.typeParameters {
|
|
typeParameters = append(typeParameters, b.typeToTypeNode(b.ch.instantiateType(parameter, signature.mapper)))
|
|
}
|
|
} else {
|
|
for _, parameter := range signature.typeParameters {
|
|
typeParameters = append(typeParameters, b.typeParameterToDeclaration(parameter))
|
|
}
|
|
}
|
|
|
|
restoreFlags := b.saveRestoreFlags()
|
|
b.ctx.flags &^= nodebuilder.FlagsSuppressAnyReturnType
|
|
// If the expanded parameter list had a variadic in a non-trailing position, don't expand it
|
|
parameters := core.Map(core.IfElse(core.Some(expandedParams, func(p *ast.Symbol) bool {
|
|
return p != expandedParams[len(expandedParams)-1] && p.CheckFlags&ast.CheckFlagsRestParameter != 0
|
|
}), signature.parameters, expandedParams), func(parameter *ast.Symbol) *ast.Node {
|
|
return b.symbolToParameterDeclaration(parameter, kind == ast.KindConstructor)
|
|
})
|
|
var thisParameter *ast.Node
|
|
if b.ctx.flags&nodebuilder.FlagsOmitThisParameter != 0 {
|
|
thisParameter = nil
|
|
} else {
|
|
thisParameter = b.tryGetThisParameterDeclaration(signature)
|
|
}
|
|
if thisParameter != nil {
|
|
parameters = append([]*ast.Node{thisParameter}, parameters...)
|
|
}
|
|
restoreFlags()
|
|
|
|
returnTypeNode := b.serializeReturnTypeForSignature(signature)
|
|
|
|
var modifiers []*ast.Node
|
|
if options != nil {
|
|
modifiers = options.modifiers
|
|
}
|
|
if (kind == ast.KindConstructorType) && signature.flags&SignatureFlagsAbstract != 0 {
|
|
flags := ast.ModifiersToFlags(modifiers)
|
|
modifiers = ast.CreateModifiersFromModifierFlags(flags|ast.ModifierFlagsAbstract, b.f.NewModifier)
|
|
}
|
|
|
|
paramList := b.f.NewNodeList(parameters)
|
|
var typeParamList *ast.NodeList
|
|
if len(typeParameters) != 0 {
|
|
typeParamList = b.f.NewNodeList(typeParameters)
|
|
}
|
|
var modifierList *ast.ModifierList
|
|
if len(modifiers) > 0 {
|
|
modifierList = b.f.NewModifierList(modifiers)
|
|
}
|
|
var name *ast.Node
|
|
if options != nil {
|
|
name = options.name
|
|
}
|
|
if name == nil {
|
|
name = b.f.NewIdentifier("")
|
|
}
|
|
|
|
var node *ast.Node
|
|
switch {
|
|
case kind == ast.KindCallSignature:
|
|
node = b.f.NewCallSignatureDeclaration(typeParamList, paramList, returnTypeNode)
|
|
case kind == ast.KindConstructSignature:
|
|
node = b.f.NewConstructSignatureDeclaration(typeParamList, paramList, returnTypeNode)
|
|
case kind == ast.KindMethodSignature:
|
|
var questionToken *ast.Node
|
|
if options != nil {
|
|
questionToken = options.questionToken
|
|
}
|
|
node = b.f.NewMethodSignatureDeclaration(modifierList, name, questionToken, typeParamList, paramList, returnTypeNode)
|
|
case kind == ast.KindMethodDeclaration:
|
|
node = b.f.NewMethodDeclaration(modifierList, nil /*asteriskToken*/, name, nil /*questionToken*/, typeParamList, paramList, returnTypeNode, nil /*fullSignature*/, nil /*body*/)
|
|
case kind == ast.KindConstructor:
|
|
node = b.f.NewConstructorDeclaration(modifierList, nil /*typeParamList*/, paramList, nil /*returnTypeNode*/, nil /*fullSignature*/, nil /*body*/)
|
|
case kind == ast.KindGetAccessor:
|
|
node = b.f.NewGetAccessorDeclaration(modifierList, name, nil /*typeParamList*/, paramList, returnTypeNode, nil /*fullSignature*/, nil /*body*/)
|
|
case kind == ast.KindSetAccessor:
|
|
node = b.f.NewSetAccessorDeclaration(modifierList, name, nil /*typeParamList*/, paramList, nil /*returnTypeNode*/, nil /*fullSignature*/, nil /*body*/)
|
|
case kind == ast.KindIndexSignature:
|
|
node = b.f.NewIndexSignatureDeclaration(modifierList, paramList, returnTypeNode)
|
|
// !!! JSDoc Support
|
|
// case kind == ast.KindJSDocFunctionType:
|
|
// node = b.f.NewJSDocFunctionType(parameters, returnTypeNode)
|
|
case kind == ast.KindFunctionType:
|
|
if returnTypeNode == nil {
|
|
returnTypeNode = b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), nil)
|
|
}
|
|
node = b.f.NewFunctionTypeNode(typeParamList, paramList, returnTypeNode)
|
|
case kind == ast.KindConstructorType:
|
|
if returnTypeNode == nil {
|
|
returnTypeNode = b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), nil)
|
|
}
|
|
node = b.f.NewConstructorTypeNode(modifierList, typeParamList, paramList, returnTypeNode)
|
|
case kind == ast.KindFunctionDeclaration:
|
|
// TODO: assert name is Identifier
|
|
node = b.f.NewFunctionDeclaration(modifierList, nil /*asteriskToken*/, name, typeParamList, paramList, returnTypeNode, nil /*fullSignature*/, nil /*body*/)
|
|
case kind == ast.KindFunctionExpression:
|
|
// TODO: assert name is Identifier
|
|
node = b.f.NewFunctionExpression(modifierList, nil /*asteriskToken*/, name, typeParamList, paramList, returnTypeNode, nil /*fullSignature*/, b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false))
|
|
case kind == ast.KindArrowFunction:
|
|
node = b.f.NewArrowFunction(modifierList, typeParamList, paramList, returnTypeNode, nil /*fullSignature*/, nil /*equalsGreaterThanToken*/, b.f.NewBlock(b.f.NewNodeList([]*ast.Node{}), false))
|
|
default:
|
|
panic("Unhandled kind in signatureToSignatureDeclarationHelper")
|
|
}
|
|
|
|
// !!! TODO: Smuggle type arguments of signatures out for quickinfo
|
|
// if typeArguments != nil {
|
|
// node.TypeArguments = b.f.NewNodeList(typeArguments)
|
|
// }
|
|
|
|
cleanup()
|
|
return node
|
|
}
|
|
|
|
func (c *Checker) getExpandedParameters(sig *Signature, skipUnionExpanding bool) [][]*ast.Symbol {
|
|
if signatureHasRestParameter(sig) {
|
|
restIndex := len(sig.parameters) - 1
|
|
restSymbol := sig.parameters[restIndex]
|
|
restType := c.getTypeOfSymbol(restSymbol)
|
|
getUniqAssociatedNamesFromTupleType := func(t *Type, restSymbol *ast.Symbol) []string {
|
|
names := core.MapIndex(t.Target().AsTupleType().elementInfos, func(info TupleElementInfo, i int) string {
|
|
return c.getTupleElementLabel(info, restSymbol, i)
|
|
})
|
|
if len(names) > 0 {
|
|
duplicates := []int{}
|
|
uniqueNames := make(map[string]bool)
|
|
for i, name := range names {
|
|
_, ok := uniqueNames[name]
|
|
if ok {
|
|
duplicates = append(duplicates, i)
|
|
} else {
|
|
uniqueNames[name] = true
|
|
}
|
|
}
|
|
counters := make(map[string]int)
|
|
for _, i := range duplicates {
|
|
counter, ok := counters[names[i]]
|
|
if !ok {
|
|
counter = 1
|
|
}
|
|
var name string
|
|
for true {
|
|
name = fmt.Sprintf("%s_%d", names[i], counter)
|
|
_, ok := uniqueNames[name]
|
|
if ok {
|
|
counter++
|
|
continue
|
|
} else {
|
|
uniqueNames[name] = true
|
|
break
|
|
}
|
|
}
|
|
names[i] = name
|
|
counters[names[i]] = counter + 1
|
|
}
|
|
}
|
|
return names
|
|
}
|
|
expandSignatureParametersWithTupleMembers := func(restType *Type, restIndex int, restSymbol *ast.Symbol) []*ast.Symbol {
|
|
elementTypes := c.getTypeArguments(restType)
|
|
associatedNames := getUniqAssociatedNamesFromTupleType(restType, restSymbol)
|
|
restParams := core.MapIndex(elementTypes, func(t *Type, i int) *ast.Symbol {
|
|
// Lookup the label from the individual tuple passed in before falling back to the signature `rest` parameter name
|
|
// TODO: getTupleElementLabel can no longer fail, investigate if this lack of falliability meaningfully changes output
|
|
// var name *string
|
|
// if associatedNames != nil && associatedNames[i] != nil {
|
|
// name = associatedNames[i]
|
|
// } else {
|
|
// name = c.getParameterNameAtPosition(sig, restIndex+i, restType)
|
|
// }
|
|
name := associatedNames[i]
|
|
flags := restType.Target().AsTupleType().elementInfos[i].flags
|
|
var checkFlags ast.CheckFlags
|
|
switch {
|
|
case flags&ElementFlagsVariable != 0:
|
|
checkFlags = ast.CheckFlagsRestParameter
|
|
case flags&ElementFlagsOptional != 0:
|
|
checkFlags = ast.CheckFlagsOptionalParameter
|
|
}
|
|
symbol := c.newSymbolEx(ast.SymbolFlagsFunctionScopedVariable, name, checkFlags)
|
|
links := c.valueSymbolLinks.Get(symbol)
|
|
if flags&ElementFlagsRest != 0 {
|
|
links.resolvedType = c.createArrayType(t)
|
|
} else {
|
|
links.resolvedType = t
|
|
}
|
|
return symbol
|
|
})
|
|
return core.Concatenate(sig.parameters[0:restIndex], restParams)
|
|
}
|
|
|
|
if isTupleType(restType) {
|
|
return [][]*ast.Symbol{expandSignatureParametersWithTupleMembers(restType, restIndex, restSymbol)}
|
|
} else if !skipUnionExpanding && restType.flags&TypeFlagsUnion != 0 && core.Every(restType.AsUnionType().types, isTupleType) {
|
|
return core.Map(restType.AsUnionType().types, func(t *Type) []*ast.Symbol {
|
|
return expandSignatureParametersWithTupleMembers(t, restIndex, restSymbol)
|
|
})
|
|
}
|
|
}
|
|
return [][]*ast.Symbol{sig.parameters}
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) tryGetThisParameterDeclaration(signature *Signature) *ast.Node {
|
|
if signature.thisParameter != nil {
|
|
return b.symbolToParameterDeclaration(signature.thisParameter, false)
|
|
}
|
|
if signature.declaration != nil && ast.IsInJSFile(signature.declaration) {
|
|
// !!! JSDoc Support
|
|
// thisTag := getJSDocThisTag(signature.declaration)
|
|
// if (thisTag && thisTag.typeExpression) {
|
|
// return factory.createParameterDeclaration(
|
|
// /*modifiers*/ undefined,
|
|
// /*dotDotDotToken*/ undefined,
|
|
// "this",
|
|
// /*questionToken*/ undefined,
|
|
// typeToTypeNodeHelper(getTypeFromTypeNode(context, thisTag.typeExpression), context),
|
|
// );
|
|
// }
|
|
}
|
|
return nil
|
|
}
|
|
|
|
/**
|
|
* Serializes the return type of the signature by first trying to use the syntactic printer if possible and falling back to the checker type if not.
|
|
*/
|
|
func (b *nodeBuilderImpl) serializeReturnTypeForSignature(signature *Signature) *ast.Node {
|
|
suppressAny := b.ctx.flags&nodebuilder.FlagsSuppressAnyReturnType != 0
|
|
restoreFlags := b.saveRestoreFlags()
|
|
if suppressAny {
|
|
b.ctx.flags &= ^nodebuilder.FlagsSuppressAnyReturnType // suppress only toplevel `any`s
|
|
}
|
|
var returnTypeNode *ast.Node
|
|
|
|
returnType := b.ch.getReturnTypeOfSignature(signature)
|
|
if !(suppressAny && IsTypeAny(returnType)) {
|
|
// !!! IsolatedDeclaration support
|
|
// if signature.declaration != nil && !ast.NodeIsSynthesized(signature.declaration) {
|
|
// declarationSymbol := b.ch.getSymbolOfDeclaration(signature.declaration)
|
|
// restore := addSymbolTypeToContext(declarationSymbol, returnType)
|
|
// returnTypeNode = syntacticNodeBuilder.serializeReturnTypeForSignature(signature.declaration, declarationSymbol)
|
|
// restore()
|
|
// }
|
|
if returnTypeNode == nil {
|
|
returnTypeNode = b.serializeInferredReturnTypeForSignature(signature, returnType)
|
|
}
|
|
}
|
|
|
|
if returnTypeNode == nil && !suppressAny {
|
|
returnTypeNode = b.f.NewKeywordTypeNode(ast.KindAnyKeyword)
|
|
}
|
|
restoreFlags()
|
|
return returnTypeNode
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) indexInfoToIndexSignatureDeclarationHelper(indexInfo *IndexInfo, typeNode *ast.TypeNode) *ast.Node {
|
|
name := getNameFromIndexInfo(indexInfo)
|
|
indexerTypeNode := b.typeToTypeNode(indexInfo.keyType)
|
|
|
|
indexingParameter := b.f.NewParameterDeclaration(nil, nil, b.f.NewIdentifier(name), nil, indexerTypeNode, nil)
|
|
if typeNode == nil {
|
|
if indexInfo.valueType == nil {
|
|
typeNode = b.f.NewKeywordTypeNode(ast.KindAnyKeyword)
|
|
} else {
|
|
typeNode = b.typeToTypeNode(indexInfo.valueType)
|
|
}
|
|
}
|
|
if indexInfo.valueType == nil && b.ctx.flags&nodebuilder.FlagsAllowEmptyIndexInfoType == 0 {
|
|
b.ctx.encounteredError = true
|
|
}
|
|
b.ctx.approximateLength += len(name) + 4
|
|
var modifiers *ast.ModifierList
|
|
if indexInfo.isReadonly {
|
|
b.ctx.approximateLength += 9
|
|
modifiers = b.f.NewModifierList([]*ast.Node{b.f.NewModifier(ast.KindReadonlyKeyword)})
|
|
}
|
|
return b.f.NewIndexSignatureDeclaration(modifiers, b.f.NewNodeList([]*ast.Node{indexingParameter}), typeNode)
|
|
}
|
|
|
|
/**
|
|
* Unlike `typeToTypeNodeHelper`, this handles setting up the `AllowUniqueESSymbolType` flag
|
|
* so a `unique symbol` is returned when appropriate for the input symbol, rather than `typeof sym`
|
|
* @param declaration - The preferred declaration to pull existing type nodes from (the symbol will be used as a fallback to find any annotated declaration)
|
|
* @param type - The type to write; an existing annotation must match this type if it's used, otherwise this is the type serialized as a new type node
|
|
* @param symbol - The symbol is used both to find an existing annotation if declaration is not provided, and to determine if `unique symbol` should be printed
|
|
*/
|
|
func (b *nodeBuilderImpl) serializeTypeForDeclaration(declaration *ast.Declaration, t *Type, symbol *ast.Symbol) *ast.Node {
|
|
// !!! node reuse logic
|
|
if symbol == nil {
|
|
symbol = b.ch.getSymbolOfDeclaration(declaration)
|
|
}
|
|
if t == nil {
|
|
t = b.ctx.enclosingSymbolTypes[ast.GetSymbolId(symbol)]
|
|
if t == nil {
|
|
if symbol.Flags&ast.SymbolFlagsAccessor != 0 && declaration.Kind == ast.KindSetAccessor {
|
|
t = b.ch.instantiateType(b.ch.getWriteTypeOfSymbol(symbol), b.ctx.mapper)
|
|
} else if symbol != nil && (symbol.Flags&(ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsSignature) == 0) {
|
|
t = b.ch.instantiateType(b.ch.getWidenedLiteralType(b.ch.getTypeOfSymbol(symbol)), b.ctx.mapper)
|
|
} else {
|
|
t = b.ch.errorType
|
|
}
|
|
}
|
|
}
|
|
|
|
// !!! TODO: JSDoc, getEmitResolver call is unfortunate layering for the helper - hoist it into checker
|
|
addUndefinedForParameter := declaration != nil && (ast.IsParameter(declaration) /*|| ast.IsJSDocParameterTag(declaration)*/) && b.ch.GetEmitResolver().requiresAddingImplicitUndefined(declaration, symbol, b.ctx.enclosingDeclaration)
|
|
if addUndefinedForParameter {
|
|
t = b.ch.getOptionalType(t, false)
|
|
}
|
|
|
|
restoreFlags := b.saveRestoreFlags()
|
|
if t.flags&TypeFlagsUniqueESSymbol != 0 && t.symbol == symbol && (b.ctx.enclosingDeclaration == nil || core.Some(symbol.Declarations, func(d *ast.Declaration) bool {
|
|
return ast.GetSourceFileOfNode(d) == b.ctx.enclosingFile
|
|
})) {
|
|
b.ctx.flags |= nodebuilder.FlagsAllowUniqueESSymbolType
|
|
}
|
|
result := b.typeToTypeNode(t) // !!! expressionOrTypeToTypeNode
|
|
restoreFlags()
|
|
return result
|
|
}
|
|
|
|
const MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH = 3
|
|
|
|
func (b *nodeBuilderImpl) shouldUsePlaceholderForProperty(propertySymbol *ast.Symbol) bool {
|
|
// Use placeholders for reverse mapped types we've either
|
|
// (1) already descended into, or
|
|
// (2) are nested reverse mappings within a mapping over a non-anonymous type, or
|
|
// (3) are deeply nested properties that originate from the same mapped type.
|
|
// Condition (2) is a restriction mostly just to
|
|
// reduce the blowup in printback size from doing, eg, a deep reverse mapping over `Window`.
|
|
// Since anonymous types usually come from expressions, this allows us to preserve the output
|
|
// for deep mappings which likely come from expressions, while truncating those parts which
|
|
// come from mappings over library functions.
|
|
// Condition (3) limits printing of possibly infinitely deep reverse mapped types.
|
|
if propertySymbol.CheckFlags&ast.CheckFlagsReverseMapped == 0 {
|
|
return false
|
|
}
|
|
// (1)
|
|
for _, elem := range b.ctx.reverseMappedStack {
|
|
if elem == propertySymbol {
|
|
return true
|
|
}
|
|
}
|
|
// (2)
|
|
if len(b.ctx.reverseMappedStack) > 0 {
|
|
last := b.ctx.reverseMappedStack[len(b.ctx.reverseMappedStack)-1]
|
|
if b.ch.ReverseMappedSymbolLinks.Has(last) {
|
|
links := b.ch.ReverseMappedSymbolLinks.TryGet(last)
|
|
propertyType := links.propertyType
|
|
if propertyType != nil && propertyType.objectFlags&ObjectFlagsAnonymous == 0 {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
// (3) - we only inspect the last MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH elements of the
|
|
// stack for approximate matches to catch tight infinite loops
|
|
// TODO: Why? Reasoning lost to time. this could probably stand to be improved?
|
|
if len(b.ctx.reverseMappedStack) < MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH {
|
|
return false
|
|
}
|
|
if !b.ch.ReverseMappedSymbolLinks.Has(propertySymbol) {
|
|
return false
|
|
}
|
|
propertyLinks := b.ch.ReverseMappedSymbolLinks.TryGet(propertySymbol)
|
|
propMappedType := propertyLinks.mappedType
|
|
if propMappedType == nil || propMappedType.symbol == nil {
|
|
return false
|
|
}
|
|
for i := range b.ctx.reverseMappedStack {
|
|
if i > MAX_REVERSE_MAPPED_NESTING_INSPECTION_DEPTH {
|
|
break
|
|
}
|
|
prop := b.ctx.reverseMappedStack[len(b.ctx.reverseMappedStack)-1-i]
|
|
if b.ch.ReverseMappedSymbolLinks.Has(prop) {
|
|
links := b.ch.ReverseMappedSymbolLinks.TryGet(prop)
|
|
mappedType := links.mappedType
|
|
if mappedType != nil && mappedType.symbol == propMappedType.symbol {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) trackComputedName(accessExpression *ast.Node, enclosingDeclaration *ast.Node) {
|
|
// get symbol of the first identifier of the entityName
|
|
firstIdentifier := ast.GetFirstIdentifier(accessExpression)
|
|
name := b.ch.resolveName(firstIdentifier, firstIdentifier.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsExportValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false)
|
|
if name != nil {
|
|
b.ctx.tracker.TrackSymbol(name, enclosingDeclaration, ast.SymbolFlagsValue)
|
|
}
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) createPropertyNameNodeForIdentifierOrLiteral(name string, _singleQuote bool, stringNamed bool, isMethod bool) *ast.Node {
|
|
isMethodNamedNew := isMethod && name == "new"
|
|
if !isMethodNamedNew && scanner.IsIdentifierText(name, core.LanguageVariantStandard) {
|
|
return b.f.NewIdentifier(name)
|
|
}
|
|
if !stringNamed && !isMethodNamedNew && isNumericLiteralName(name) && jsnum.FromString(name) >= 0 {
|
|
return b.f.NewNumericLiteral(name)
|
|
}
|
|
result := b.f.NewStringLiteral(name)
|
|
// !!! TODO: set singleQuote
|
|
return result
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) isStringNamed(d *ast.Declaration) bool {
|
|
name := ast.GetNameOfDeclaration(d)
|
|
if name == nil {
|
|
return false
|
|
}
|
|
if ast.IsComputedPropertyName(name) {
|
|
t := b.ch.checkExpression(name.AsComputedPropertyName().Expression)
|
|
return t.flags&TypeFlagsStringLike != 0
|
|
}
|
|
if ast.IsElementAccessExpression(name) {
|
|
t := b.ch.checkExpression(name.AsElementAccessExpression().ArgumentExpression)
|
|
return t.flags&TypeFlagsStringLike != 0
|
|
}
|
|
return ast.IsStringLiteral(name)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) isSingleQuotedStringNamed(d *ast.Declaration) bool {
|
|
return false // !!!
|
|
// TODO: actually support single-quote-style-maintenance
|
|
// name := ast.GetNameOfDeclaration(d)
|
|
// return name != nil && ast.IsStringLiteral(name) && (name.AsStringLiteral().SingleQuote || !nodeIsSynthesized(name) && startsWith(getTextOfNode(name, false /*includeTrivia*/), "'"))
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) getPropertyNameNodeForSymbol(symbol *ast.Symbol) *ast.Node {
|
|
stringNamed := len(symbol.Declarations) != 0 && core.Every(symbol.Declarations, b.isStringNamed)
|
|
singleQuote := len(symbol.Declarations) != 0 && core.Every(symbol.Declarations, b.isSingleQuotedStringNamed)
|
|
isMethod := symbol.Flags&ast.SymbolFlagsMethod != 0
|
|
fromNameType := b.getPropertyNameNodeForSymbolFromNameType(symbol, singleQuote, stringNamed, isMethod)
|
|
if fromNameType != nil {
|
|
return fromNameType
|
|
}
|
|
|
|
name := symbol.Name
|
|
const privateNamePrefix = ast.InternalSymbolNamePrefix + "#"
|
|
if strings.HasPrefix(name, privateNamePrefix) {
|
|
// symbol IDs are unstable - replace #nnn# with #private#
|
|
name = name[len(privateNamePrefix):]
|
|
name = strings.TrimLeftFunc(name, stringutil.IsDigit)
|
|
name = "__#private" + name
|
|
}
|
|
|
|
return b.createPropertyNameNodeForIdentifierOrLiteral(name, singleQuote, stringNamed, isMethod)
|
|
}
|
|
|
|
// See getNameForSymbolFromNameType for a stringy equivalent
|
|
func (b *nodeBuilderImpl) getPropertyNameNodeForSymbolFromNameType(symbol *ast.Symbol, singleQuote bool, stringNamed bool, isMethod bool) *ast.Node {
|
|
if !b.ch.valueSymbolLinks.Has(symbol) {
|
|
return nil
|
|
}
|
|
nameType := b.ch.valueSymbolLinks.TryGet(symbol).nameType
|
|
if nameType == nil {
|
|
return nil
|
|
}
|
|
if nameType.flags&TypeFlagsStringOrNumberLiteral != 0 {
|
|
var name string
|
|
switch nameType.AsLiteralType().value.(type) {
|
|
case jsnum.Number:
|
|
name = nameType.AsLiteralType().value.(jsnum.Number).String()
|
|
case string:
|
|
name = nameType.AsLiteralType().value.(string)
|
|
}
|
|
if !scanner.IsIdentifierText(name, core.LanguageVariantStandard) && (stringNamed || !isNumericLiteralName(name)) {
|
|
// !!! TODO: set singleQuote
|
|
return b.f.NewStringLiteral(name)
|
|
}
|
|
if isNumericLiteralName(name) && name[0] == '-' {
|
|
return b.f.NewComputedPropertyName(b.f.NewPrefixUnaryExpression(ast.KindMinusToken, b.f.NewNumericLiteral(name[1:])))
|
|
}
|
|
return b.createPropertyNameNodeForIdentifierOrLiteral(name, singleQuote, stringNamed, isMethod)
|
|
}
|
|
if nameType.flags&TypeFlagsUniqueESSymbol != 0 {
|
|
return b.f.NewComputedPropertyName(b.symbolToExpression(nameType.AsUniqueESSymbolType().symbol, ast.SymbolFlagsValue))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) addPropertyToElementList(propertySymbol *ast.Symbol, typeElements []*ast.TypeElement) []*ast.TypeElement {
|
|
propertyIsReverseMapped := propertySymbol.CheckFlags&ast.CheckFlagsReverseMapped != 0
|
|
var propertyType *Type
|
|
if b.shouldUsePlaceholderForProperty(propertySymbol) {
|
|
propertyType = b.ch.anyType
|
|
} else {
|
|
propertyType = b.ch.getNonMissingTypeOfSymbol(propertySymbol)
|
|
}
|
|
saveEnclosingDeclaration := b.ctx.enclosingDeclaration
|
|
b.ctx.enclosingDeclaration = nil
|
|
if isLateBoundName(propertySymbol.Name) {
|
|
if len(propertySymbol.Declarations) > 0 {
|
|
decl := propertySymbol.Declarations[0]
|
|
if b.ch.hasLateBindableName(decl) {
|
|
if ast.IsBinaryExpression(decl) {
|
|
name := ast.GetNameOfDeclaration(decl)
|
|
if name != nil && ast.IsElementAccessExpression(name) && ast.IsPropertyAccessEntityNameExpression(name.AsElementAccessExpression().ArgumentExpression, false /*allowJs*/) {
|
|
b.trackComputedName(name.AsElementAccessExpression().ArgumentExpression, saveEnclosingDeclaration)
|
|
}
|
|
} else {
|
|
b.trackComputedName(decl.Name().Expression(), saveEnclosingDeclaration)
|
|
}
|
|
}
|
|
} else {
|
|
b.ctx.tracker.ReportNonSerializableProperty(b.ch.symbolToString(propertySymbol))
|
|
}
|
|
}
|
|
if propertySymbol.ValueDeclaration != nil {
|
|
b.ctx.enclosingDeclaration = propertySymbol.ValueDeclaration
|
|
} else if len(propertySymbol.Declarations) > 0 && propertySymbol.Declarations[0] != nil {
|
|
b.ctx.enclosingDeclaration = propertySymbol.Declarations[0]
|
|
} else {
|
|
b.ctx.enclosingDeclaration = saveEnclosingDeclaration
|
|
}
|
|
propertyName := b.getPropertyNameNodeForSymbol(propertySymbol)
|
|
b.ctx.enclosingDeclaration = saveEnclosingDeclaration
|
|
b.ctx.approximateLength += len(ast.SymbolName(propertySymbol)) + 1
|
|
|
|
if propertySymbol.Flags&ast.SymbolFlagsAccessor != 0 {
|
|
writeType := b.ch.getWriteTypeOfSymbol(propertySymbol)
|
|
if !b.ch.isErrorType(propertyType) && !b.ch.isErrorType(writeType) {
|
|
propDeclaration := ast.GetDeclarationOfKind(propertySymbol, ast.KindPropertyDeclaration)
|
|
if propertyType != writeType || propertySymbol.Parent.Flags&ast.SymbolFlagsClass != 0 && propDeclaration == nil {
|
|
if getterDeclaration := ast.GetDeclarationOfKind(propertySymbol, ast.KindGetAccessor); getterDeclaration != nil {
|
|
getterSignature := b.ch.getSignatureFromDeclaration(getterDeclaration)
|
|
getter := b.signatureToSignatureDeclarationHelper(getterSignature, ast.KindGetAccessor, &SignatureToSignatureDeclarationOptions{
|
|
name: propertyName,
|
|
})
|
|
b.setCommentRange(getter, getterDeclaration)
|
|
typeElements = append(typeElements, getter)
|
|
}
|
|
if setterDeclaration := ast.GetDeclarationOfKind(propertySymbol, ast.KindSetAccessor); setterDeclaration != nil {
|
|
setterSignature := b.ch.getSignatureFromDeclaration(setterDeclaration)
|
|
setter := b.signatureToSignatureDeclarationHelper(setterSignature, ast.KindSetAccessor, &SignatureToSignatureDeclarationOptions{
|
|
name: propertyName,
|
|
})
|
|
b.setCommentRange(setter, setterDeclaration)
|
|
typeElements = append(typeElements, setter)
|
|
}
|
|
return typeElements
|
|
} else if propertySymbol.Parent.Flags&ast.SymbolFlagsClass != 0 && propDeclaration != nil && core.Find(propDeclaration.ModifierNodes(), func(m *ast.Node) bool {
|
|
return m.Kind == ast.KindAccessorKeyword
|
|
}) != nil {
|
|
fakeGetterSignature := b.ch.newSignature(SignatureFlagsNone, nil, nil, nil, nil, propertyType, nil, 0)
|
|
fakeGetterDeclaration := b.signatureToSignatureDeclarationHelper(fakeGetterSignature, ast.KindGetAccessor, &SignatureToSignatureDeclarationOptions{
|
|
name: propertyName,
|
|
})
|
|
b.setCommentRange(fakeGetterDeclaration, propDeclaration)
|
|
typeElements = append(typeElements, fakeGetterDeclaration)
|
|
|
|
setterParam := b.ch.newSymbol(ast.SymbolFlagsFunctionScopedVariable, "arg")
|
|
b.ch.valueSymbolLinks.Get(setterParam).resolvedType = writeType
|
|
fakeSetterSignature := b.ch.newSignature(SignatureFlagsNone, nil, nil, nil, []*ast.Symbol{setterParam}, b.ch.voidType, nil, 0)
|
|
fakeSetterDeclaration := b.signatureToSignatureDeclarationHelper(fakeSetterSignature, ast.KindSetAccessor, &SignatureToSignatureDeclarationOptions{
|
|
name: propertyName,
|
|
})
|
|
typeElements = append(typeElements, fakeSetterDeclaration)
|
|
return typeElements
|
|
}
|
|
}
|
|
}
|
|
|
|
var optionalToken *ast.Node
|
|
if propertySymbol.Flags&ast.SymbolFlagsOptional != 0 {
|
|
optionalToken = b.f.NewToken(ast.KindQuestionToken)
|
|
} else {
|
|
optionalToken = nil
|
|
}
|
|
if propertySymbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0 && len(b.ch.getPropertiesOfObjectType(propertyType)) == 0 && !b.ch.isReadonlySymbol(propertySymbol) {
|
|
signatures := b.ch.getSignaturesOfType(b.ch.filterType(propertyType, func(t *Type) bool {
|
|
return t.flags&TypeFlagsUndefined == 0
|
|
}), SignatureKindCall)
|
|
for _, signature := range signatures {
|
|
methodDeclaration := b.signatureToSignatureDeclarationHelper(signature, ast.KindMethodSignature, &SignatureToSignatureDeclarationOptions{
|
|
name: propertyName,
|
|
questionToken: optionalToken,
|
|
})
|
|
b.setCommentRange(methodDeclaration, core.Coalesce(signature.declaration, propertySymbol.ValueDeclaration))
|
|
typeElements = append(typeElements, methodDeclaration)
|
|
}
|
|
if len(signatures) != 0 || optionalToken == nil {
|
|
return typeElements
|
|
}
|
|
}
|
|
var propertyTypeNode *ast.TypeNode
|
|
if b.shouldUsePlaceholderForProperty(propertySymbol) {
|
|
propertyTypeNode = b.createElidedInformationPlaceholder()
|
|
} else {
|
|
if propertyIsReverseMapped {
|
|
b.ctx.reverseMappedStack = append(b.ctx.reverseMappedStack, propertySymbol)
|
|
}
|
|
if propertyType != nil {
|
|
propertyTypeNode = b.serializeTypeForDeclaration(nil /*declaration*/, propertyType, propertySymbol)
|
|
} else {
|
|
propertyTypeNode = b.f.NewKeywordTypeNode(ast.KindAnyKeyword)
|
|
}
|
|
if propertyIsReverseMapped {
|
|
b.ctx.reverseMappedStack = b.ctx.reverseMappedStack[:len(b.ctx.reverseMappedStack)-1]
|
|
}
|
|
}
|
|
|
|
var modifiers *ast.ModifierList
|
|
if b.ch.isReadonlySymbol(propertySymbol) {
|
|
modifiers = b.f.NewModifierList([]*ast.Node{b.f.NewModifier(ast.KindReadonlyKeyword)})
|
|
b.ctx.approximateLength += 9
|
|
}
|
|
propertySignature := b.f.NewPropertySignatureDeclaration(modifiers, propertyName, optionalToken, propertyTypeNode, nil)
|
|
|
|
b.setCommentRange(propertySignature, propertySymbol.ValueDeclaration)
|
|
typeElements = append(typeElements, propertySignature)
|
|
|
|
return typeElements
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) createTypeNodesFromResolvedType(resolvedType *StructuredType) *ast.NodeList {
|
|
if b.checkTruncationLength() {
|
|
if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 {
|
|
elem := b.f.NewNotEmittedTypeElement()
|
|
return b.f.NewNodeList([]*ast.TypeElement{b.e.AddSyntheticLeadingComment(elem, ast.KindMultiLineCommentTrivia, "elided", false /*hasTrailingNewLine*/)})
|
|
}
|
|
return b.f.NewNodeList([]*ast.Node{b.f.NewPropertySignatureDeclaration(nil, b.f.NewIdentifier("..."), nil, nil, nil)})
|
|
}
|
|
var typeElements []*ast.TypeElement
|
|
for _, signature := range resolvedType.CallSignatures() {
|
|
typeElements = append(typeElements, b.signatureToSignatureDeclarationHelper(signature, ast.KindCallSignature, nil))
|
|
}
|
|
for _, signature := range resolvedType.ConstructSignatures() {
|
|
if signature.flags&SignatureFlagsAbstract != 0 {
|
|
continue
|
|
}
|
|
typeElements = append(typeElements, b.signatureToSignatureDeclarationHelper(signature, ast.KindConstructSignature, nil))
|
|
}
|
|
for _, info := range resolvedType.indexInfos {
|
|
typeElements = append(typeElements, b.indexInfoToIndexSignatureDeclarationHelper(info, core.IfElse(resolvedType.objectFlags&ObjectFlagsReverseMapped != 0, b.createElidedInformationPlaceholder(), nil)))
|
|
}
|
|
|
|
properties := resolvedType.properties
|
|
if len(properties) == 0 {
|
|
return b.f.NewNodeList(typeElements)
|
|
}
|
|
|
|
i := 0
|
|
for _, propertySymbol := range properties {
|
|
i++
|
|
if b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0 {
|
|
if propertySymbol.Flags&ast.SymbolFlagsPrototype != 0 {
|
|
continue
|
|
}
|
|
if getDeclarationModifierFlagsFromSymbol(propertySymbol)&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 {
|
|
b.ctx.tracker.ReportPrivateInBaseOfClassExpression(propertySymbol.Name)
|
|
}
|
|
}
|
|
if b.checkTruncationLength() && (i+2 < len(properties)-1) {
|
|
if b.ctx.flags&nodebuilder.FlagsNoTruncation != 0 {
|
|
typeElements[len(typeElements)-1] = b.e.AddSyntheticLeadingComment(typeElements[len(typeElements)-1], ast.KindMultiLineCommentTrivia, fmt.Sprintf("... %d more elided ...", len(properties)-i), false /*hasTrailingNewLine*/)
|
|
} else {
|
|
text := fmt.Sprintf("... %d more ...", len(properties)-i)
|
|
typeElements = append(typeElements, b.f.NewPropertySignatureDeclaration(nil, b.f.NewIdentifier(text), nil, nil, nil))
|
|
}
|
|
typeElements = b.addPropertyToElementList(properties[len(properties)-1], typeElements)
|
|
break
|
|
}
|
|
typeElements = b.addPropertyToElementList(propertySymbol, typeElements)
|
|
}
|
|
if len(typeElements) != 0 {
|
|
return b.f.NewNodeList(typeElements)
|
|
} else {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) createTypeNodeFromObjectType(t *Type) *ast.TypeNode {
|
|
if b.ch.isGenericMappedType(t) || (t.objectFlags&ObjectFlagsMapped != 0 && t.AsMappedType().containsError) {
|
|
return b.createMappedTypeNodeFromType(t)
|
|
}
|
|
|
|
resolved := b.ch.resolveStructuredTypeMembers(t)
|
|
callSigs := resolved.CallSignatures()
|
|
ctorSigs := resolved.ConstructSignatures()
|
|
if len(resolved.properties) == 0 && len(resolved.indexInfos) == 0 {
|
|
if len(callSigs) == 0 && len(ctorSigs) == 0 {
|
|
b.ctx.approximateLength += 2
|
|
result := b.f.NewTypeLiteralNode(b.f.NewNodeList([]*ast.Node{}))
|
|
b.e.SetEmitFlags(result, printer.EFSingleLine)
|
|
return result
|
|
}
|
|
|
|
if len(callSigs) == 1 && len(ctorSigs) == 0 {
|
|
signature := callSigs[0]
|
|
signatureNode := b.signatureToSignatureDeclarationHelper(signature, ast.KindFunctionType, nil)
|
|
return signatureNode
|
|
}
|
|
|
|
if len(ctorSigs) == 1 && len(callSigs) == 0 {
|
|
signature := ctorSigs[0]
|
|
signatureNode := b.signatureToSignatureDeclarationHelper(signature, ast.KindConstructorType, nil)
|
|
return signatureNode
|
|
}
|
|
}
|
|
|
|
abstractSignatures := core.Filter(ctorSigs, func(signature *Signature) bool {
|
|
return signature.flags&SignatureFlagsAbstract != 0
|
|
})
|
|
if len(abstractSignatures) > 0 {
|
|
types := core.Map(abstractSignatures, func(s *Signature) *Type {
|
|
return b.ch.getOrCreateTypeFromSignature(s)
|
|
})
|
|
// count the number of type elements excluding abstract constructors
|
|
typeElementCount := len(callSigs) + (len(ctorSigs) - len(abstractSignatures)) + len(resolved.indexInfos) + (core.IfElse(b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0, core.CountWhere(resolved.properties, func(p *ast.Symbol) bool {
|
|
return p.Flags&ast.SymbolFlagsPrototype == 0
|
|
}), len(resolved.properties)))
|
|
// don't include an empty object literal if there were no other static-side
|
|
// properties to write, i.e. `abstract class C { }` becomes `abstract new () => {}`
|
|
// and not `(abstract new () => {}) & {}`
|
|
if typeElementCount != 0 {
|
|
// create a copy of the object type without any abstract construct signatures.
|
|
types = append(types, b.getResolvedTypeWithoutAbstractConstructSignatures(resolved))
|
|
}
|
|
return b.typeToTypeNode(b.ch.getIntersectionType(types))
|
|
}
|
|
|
|
restoreFlags := b.saveRestoreFlags()
|
|
b.ctx.flags |= nodebuilder.FlagsInObjectTypeLiteral
|
|
members := b.createTypeNodesFromResolvedType(resolved)
|
|
restoreFlags()
|
|
typeLiteralNode := b.f.NewTypeLiteralNode(members)
|
|
b.ctx.approximateLength += 2
|
|
b.e.SetEmitFlags(typeLiteralNode, core.IfElse((b.ctx.flags&nodebuilder.FlagsMultilineObjectLiterals != 0), 0, printer.EFSingleLine))
|
|
return typeLiteralNode
|
|
}
|
|
|
|
func getTypeAliasForTypeLiteral(c *Checker, t *Type) *ast.Symbol {
|
|
if t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsTypeLiteral != 0 && t.symbol.Declarations != nil {
|
|
node := ast.WalkUpParenthesizedTypes(t.symbol.Declarations[0].Parent)
|
|
if ast.IsTypeAliasDeclaration(node) {
|
|
return c.getSymbolOfDeclaration(node)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) shouldWriteTypeOfFunctionSymbol(symbol *ast.Symbol, typeId TypeId) bool {
|
|
isStaticMethodSymbol := symbol.Flags&ast.SymbolFlagsMethod != 0 && core.Some(symbol.Declarations, func(declaration *ast.Node) bool {
|
|
return ast.IsStatic(declaration) && !b.ch.isLateBindableIndexSignature(ast.GetNameOfDeclaration(declaration))
|
|
})
|
|
isNonLocalFunctionSymbol := false
|
|
if symbol.Flags&ast.SymbolFlagsFunction != 0 {
|
|
if symbol.Parent != nil {
|
|
isNonLocalFunctionSymbol = true
|
|
} else {
|
|
for _, declaration := range symbol.Declarations {
|
|
if declaration.Parent.Kind == ast.KindSourceFile || declaration.Parent.Kind == ast.KindModuleBlock {
|
|
isNonLocalFunctionSymbol = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if isStaticMethodSymbol || isNonLocalFunctionSymbol {
|
|
// typeof is allowed only for static/non local functions
|
|
return (b.ctx.flags&nodebuilder.FlagsUseTypeOfFunction != 0 || b.ctx.visitedTypes.Has(typeId)) && // it is type of the symbol uses itself recursively
|
|
(b.ctx.flags&nodebuilder.FlagsUseStructuralFallback == 0 || b.ch.IsValueSymbolAccessible(symbol, b.ctx.enclosingDeclaration)) // And the build is going to succeed without visibility error or there is no structural fallback allowed
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) createAnonymousTypeNode(t *Type) *ast.TypeNode {
|
|
typeId := t.id
|
|
symbol := t.symbol
|
|
if symbol != nil {
|
|
isInstantiationExpressionType := t.objectFlags&ObjectFlagsInstantiationExpressionType != 0
|
|
if isInstantiationExpressionType {
|
|
instantiationExpressionType := t.AsInstantiationExpressionType()
|
|
existing := instantiationExpressionType.node
|
|
if ast.IsTypeQueryNode(existing) {
|
|
typeNode := b.tryReuseExistingNonParameterTypeNode(existing, t, nil, nil)
|
|
if typeNode != nil {
|
|
return typeNode
|
|
}
|
|
}
|
|
if b.ctx.visitedTypes.Has(typeId) {
|
|
return b.createElidedInformationPlaceholder()
|
|
}
|
|
return b.visitAndTransformType(t, (*nodeBuilderImpl).createTypeNodeFromObjectType)
|
|
}
|
|
var isInstanceType ast.SymbolFlags
|
|
if isClassInstanceSide(b.ch, t) {
|
|
isInstanceType = ast.SymbolFlagsType
|
|
} else {
|
|
isInstanceType = ast.SymbolFlagsValue
|
|
}
|
|
|
|
// !!! JS support
|
|
// if c.isJSConstructor(symbol.ValueDeclaration) {
|
|
// // Instance and static types share the same symbol; only add 'typeof' for the static side.
|
|
// return b.symbolToTypeNode(symbol, isInstanceType, nil)
|
|
// } else
|
|
if symbol.Flags&ast.SymbolFlagsClass != 0 && b.ch.getBaseTypeVariableOfClass(symbol) == nil && !(symbol.ValueDeclaration != nil && ast.IsClassLike(symbol.ValueDeclaration) && b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0 && (!ast.IsClassDeclaration(symbol.ValueDeclaration) || b.ch.IsSymbolAccessible(symbol, b.ctx.enclosingDeclaration, isInstanceType, false /*shouldComputeAliasesToMakeVisible*/).Accessibility != printer.SymbolAccessibilityAccessible)) || symbol.Flags&(ast.SymbolFlagsEnum|ast.SymbolFlagsValueModule) != 0 || b.shouldWriteTypeOfFunctionSymbol(symbol, typeId) {
|
|
return b.symbolToTypeNode(symbol, isInstanceType, nil)
|
|
} else if b.ctx.visitedTypes.Has(typeId) {
|
|
// If type is an anonymous type literal in a type alias declaration, use type alias name
|
|
typeAlias := getTypeAliasForTypeLiteral(b.ch, t)
|
|
if typeAlias != nil {
|
|
// The specified symbol flags need to be reinterpreted as type flags
|
|
return b.symbolToTypeNode(typeAlias, ast.SymbolFlagsType, nil)
|
|
} else {
|
|
return b.createElidedInformationPlaceholder()
|
|
}
|
|
} else {
|
|
return b.visitAndTransformType(t, (*nodeBuilderImpl).createTypeNodeFromObjectType)
|
|
}
|
|
} else {
|
|
// Anonymous types without a symbol are never circular.
|
|
return b.createTypeNodeFromObjectType(t)
|
|
}
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) getTypeFromTypeNode(node *ast.TypeNode, noMappedTypes bool) *Type {
|
|
// !!! noMappedTypes optional param support
|
|
t := b.ch.getTypeFromTypeNode(node)
|
|
if b.ctx.mapper == nil {
|
|
return t
|
|
}
|
|
|
|
instantiated := b.ch.instantiateType(t, b.ctx.mapper)
|
|
if noMappedTypes && instantiated != t {
|
|
return nil
|
|
}
|
|
return instantiated
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) typeToTypeNodeOrCircularityElision(t *Type) *ast.TypeNode {
|
|
if t.flags&TypeFlagsUnion != 0 {
|
|
if b.ctx.visitedTypes.Has(t.id) {
|
|
if b.ctx.flags&nodebuilder.FlagsAllowAnonymousIdentifier == 0 {
|
|
b.ctx.encounteredError = true
|
|
b.ctx.tracker.ReportCyclicStructureError()
|
|
}
|
|
return b.createElidedInformationPlaceholder()
|
|
}
|
|
return b.visitAndTransformType(t, (*nodeBuilderImpl).typeToTypeNode)
|
|
}
|
|
return b.typeToTypeNode(t)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) conditionalTypeToTypeNode(_t *Type) *ast.TypeNode {
|
|
t := _t.AsConditionalType()
|
|
checkTypeNode := b.typeToTypeNode(t.checkType)
|
|
b.ctx.approximateLength += 15
|
|
if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && t.root.isDistributive && t.checkType.flags&TypeFlagsTypeParameter == 0 {
|
|
newParam := b.ch.newTypeParameter(b.ch.newSymbol(ast.SymbolFlagsTypeParameter, "T" /* as __String */))
|
|
name := b.typeParameterToName(newParam)
|
|
newTypeVariable := b.f.NewTypeReferenceNode(name.AsNode(), nil)
|
|
b.ctx.approximateLength += 37
|
|
// 15 each for two added conditionals, 7 for an added infer type
|
|
newMapper := prependTypeMapping(t.root.checkType, newParam, t.mapper)
|
|
saveInferTypeParameters := b.ctx.inferTypeParameters
|
|
b.ctx.inferTypeParameters = t.root.inferTypeParameters
|
|
extendsTypeNode := b.typeToTypeNode(b.ch.instantiateType(t.root.extendsType, newMapper))
|
|
b.ctx.inferTypeParameters = saveInferTypeParameters
|
|
trueTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.instantiateType(b.getTypeFromTypeNode(t.root.node.TrueType, false), newMapper))
|
|
falseTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.instantiateType(b.getTypeFromTypeNode(t.root.node.FalseType, false), newMapper))
|
|
|
|
// outermost conditional makes `T` a type parameter, allowing the inner conditionals to be distributive
|
|
// second conditional makes `T` have `T & checkType` substitution, so it is correctly usable as the checkType
|
|
// inner conditional runs the check the user provided on the check type (distributively) and returns the result
|
|
// checkType extends infer T ? T extends checkType ? T extends extendsType<T> ? trueType<T> : falseType<T> : never : never;
|
|
// this is potentially simplifiable to
|
|
// checkType extends infer T ? T extends checkType & extendsType<T> ? trueType<T> : falseType<T> : never;
|
|
// but that may confuse users who read the output more.
|
|
// On the other hand,
|
|
// checkType extends infer T extends checkType ? T extends extendsType<T> ? trueType<T> : falseType<T> : never;
|
|
// may also work with `infer ... extends ...` in, but would produce declarations only compatible with the latest TS.
|
|
newId := newTypeVariable.AsTypeReferenceNode().TypeName.AsIdentifier().Clone(b.f)
|
|
syntheticExtendsNode := b.f.NewInferTypeNode(b.f.NewTypeParameterDeclaration(nil, newId, nil, nil))
|
|
innerCheckConditionalNode := b.f.NewConditionalTypeNode(newTypeVariable, extendsTypeNode, trueTypeNode, falseTypeNode)
|
|
syntheticTrueNode := b.f.NewConditionalTypeNode(b.f.NewTypeReferenceNode(name.Clone(b.f), nil), b.f.DeepCloneNode(checkTypeNode), innerCheckConditionalNode, b.f.NewKeywordTypeNode(ast.KindNeverKeyword))
|
|
return b.f.NewConditionalTypeNode(checkTypeNode, syntheticExtendsNode, syntheticTrueNode, b.f.NewKeywordTypeNode(ast.KindNeverKeyword))
|
|
}
|
|
saveInferTypeParameters := b.ctx.inferTypeParameters
|
|
b.ctx.inferTypeParameters = t.root.inferTypeParameters
|
|
extendsTypeNode := b.typeToTypeNode(t.extendsType)
|
|
b.ctx.inferTypeParameters = saveInferTypeParameters
|
|
trueTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.getTrueTypeFromConditionalType(_t))
|
|
falseTypeNode := b.typeToTypeNodeOrCircularityElision(b.ch.getFalseTypeFromConditionalType(_t))
|
|
return b.f.NewConditionalTypeNode(checkTypeNode, extendsTypeNode, trueTypeNode, falseTypeNode)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) getParentSymbolOfTypeParameter(typeParameter *TypeParameter) *ast.Symbol {
|
|
tp := ast.GetDeclarationOfKind(typeParameter.symbol, ast.KindTypeParameter)
|
|
var host *ast.Node
|
|
// !!! JSDoc support
|
|
// if ast.IsJSDocTemplateTag(tp.Parent) {
|
|
// host = getEffectiveContainerForJSDocTemplateTag(tp.Parent)
|
|
// } else {
|
|
host = tp.Parent
|
|
// }
|
|
if host == nil {
|
|
return nil
|
|
}
|
|
return b.ch.getSymbolOfNode(host)
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) typeReferenceToTypeNode(t *Type) *ast.TypeNode {
|
|
var typeArguments []*Type = b.ch.getTypeArguments(t)
|
|
if t.Target() == b.ch.globalArrayType || t.Target() == b.ch.globalReadonlyArrayType {
|
|
if b.ctx.flags&nodebuilder.FlagsWriteArrayAsGenericType != 0 {
|
|
typeArgumentNode := b.typeToTypeNode(typeArguments[0])
|
|
return b.f.NewTypeReferenceNode(b.f.NewIdentifier(core.IfElse(t.Target() == b.ch.globalArrayType, "Array", "ReadonlyArray")), b.f.NewNodeList([]*ast.TypeNode{typeArgumentNode}))
|
|
}
|
|
elementType := b.typeToTypeNode(typeArguments[0])
|
|
arrayType := b.f.NewArrayTypeNode(elementType)
|
|
if t.Target() == b.ch.globalArrayType {
|
|
return arrayType
|
|
} else {
|
|
return b.f.NewTypeOperatorNode(ast.KindReadonlyKeyword, arrayType)
|
|
}
|
|
} else if t.Target().objectFlags&ObjectFlagsTuple != 0 {
|
|
typeArguments = core.SameMapIndex(typeArguments, func(arg *Type, i int) *Type {
|
|
isOptional := false
|
|
if i < len(t.Target().AsTupleType().elementInfos) {
|
|
isOptional = t.Target().AsTupleType().elementInfos[i].flags&ElementFlagsOptional != 0
|
|
}
|
|
return b.ch.removeMissingType(arg, isOptional)
|
|
})
|
|
if len(typeArguments) > 0 {
|
|
arity := b.ch.getTypeReferenceArity(t)
|
|
tupleConstituentNodes := b.mapToTypeNodes(typeArguments[0:arity], false /*isBareList*/)
|
|
if tupleConstituentNodes != nil {
|
|
for i := 0; i < len(tupleConstituentNodes.Nodes); i++ {
|
|
flags := t.Target().AsTupleType().elementInfos[i].flags
|
|
labeledElementDeclaration := t.Target().AsTupleType().elementInfos[i].labeledDeclaration
|
|
|
|
if labeledElementDeclaration != nil {
|
|
tupleConstituentNodes.Nodes[i] = b.f.NewNamedTupleMember(core.IfElse(flags&ElementFlagsVariable != 0, b.f.NewToken(ast.KindDotDotDotToken), nil), b.f.NewIdentifier(b.ch.getTupleElementLabel(t.Target().AsTupleType().elementInfos[i], nil, i)), core.IfElse(flags&ElementFlagsOptional != 0, b.f.NewToken(ast.KindQuestionToken), nil), core.IfElse(flags&ElementFlagsRest != 0, b.f.NewArrayTypeNode(tupleConstituentNodes.Nodes[i]), tupleConstituentNodes.Nodes[i]))
|
|
} else {
|
|
switch {
|
|
case flags&ElementFlagsVariable != 0:
|
|
tupleConstituentNodes.Nodes[i] = b.f.NewRestTypeNode(core.IfElse(flags&ElementFlagsRest != 0, b.f.NewArrayTypeNode(tupleConstituentNodes.Nodes[i]), tupleConstituentNodes.Nodes[i]))
|
|
case flags&ElementFlagsOptional != 0:
|
|
tupleConstituentNodes.Nodes[i] = b.f.NewOptionalTypeNode(tupleConstituentNodes.Nodes[i])
|
|
}
|
|
}
|
|
}
|
|
tupleTypeNode := b.f.NewTupleTypeNode(tupleConstituentNodes)
|
|
b.e.SetEmitFlags(tupleTypeNode, printer.EFSingleLine)
|
|
if t.Target().AsTupleType().readonly {
|
|
return b.f.NewTypeOperatorNode(ast.KindReadonlyKeyword, tupleTypeNode)
|
|
} else {
|
|
return tupleTypeNode
|
|
}
|
|
}
|
|
}
|
|
if b.ctx.encounteredError || (b.ctx.flags&nodebuilder.FlagsAllowEmptyTuple != 0) {
|
|
tupleTypeNode := b.f.NewTupleTypeNode(b.f.NewNodeList([]*ast.TypeNode{}))
|
|
b.e.SetEmitFlags(tupleTypeNode, printer.EFSingleLine)
|
|
if t.Target().AsTupleType().readonly {
|
|
return b.f.NewTypeOperatorNode(ast.KindReadonlyKeyword, tupleTypeNode)
|
|
} else {
|
|
return tupleTypeNode
|
|
}
|
|
}
|
|
b.ctx.encounteredError = true
|
|
return nil
|
|
// TODO: GH#18217
|
|
} else if b.ctx.flags&nodebuilder.FlagsWriteClassExpressionAsTypeLiteral != 0 && t.symbol.ValueDeclaration != nil && ast.IsClassLike(t.symbol.ValueDeclaration) && !b.ch.IsValueSymbolAccessible(t.symbol, b.ctx.enclosingDeclaration) {
|
|
return b.createAnonymousTypeNode(t)
|
|
} else {
|
|
outerTypeParameters := t.Target().AsInterfaceType().OuterTypeParameters()
|
|
i := 0
|
|
var resultType *ast.TypeNode
|
|
if outerTypeParameters != nil {
|
|
length := len(outerTypeParameters)
|
|
for i < length {
|
|
// Find group of type arguments for type parameters with the same declaring container.
|
|
start := i
|
|
parent := b.getParentSymbolOfTypeParameter(outerTypeParameters[i].AsTypeParameter())
|
|
for ok := true; ok; ok = i < length && b.getParentSymbolOfTypeParameter(outerTypeParameters[i].AsTypeParameter()) == parent { // do-while loop
|
|
i++
|
|
}
|
|
// When type parameters are their own type arguments for the whole group (i.e. we have
|
|
// the default outer type arguments), we don't show the group.
|
|
|
|
if !slices.Equal(outerTypeParameters[start:i], typeArguments[start:i]) {
|
|
typeArgumentSlice := b.mapToTypeNodes(typeArguments[start:i], false /*isBareList*/)
|
|
restoreFlags := b.saveRestoreFlags()
|
|
b.ctx.flags |= nodebuilder.FlagsForbidIndexedAccessSymbolReferences
|
|
ref := b.symbolToTypeNode(parent, ast.SymbolFlagsType, typeArgumentSlice)
|
|
restoreFlags()
|
|
if resultType == nil {
|
|
resultType = ref
|
|
} else {
|
|
resultType = b.appendReferenceToType(resultType, ref)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var typeArgumentNodes *ast.NodeList
|
|
if len(typeArguments) > 0 {
|
|
typeParameterCount := 0
|
|
typeParams := t.Target().AsInterfaceType().TypeParameters()
|
|
if typeParams != nil {
|
|
typeParameterCount = min(len(typeParams), len(typeArguments))
|
|
|
|
// Maybe we should do this for more types, but for now we only elide type arguments that are
|
|
// identical to their associated type parameters' defaults for `Iterable`, `IterableIterator`,
|
|
// `AsyncIterable`, and `AsyncIterableIterator` to provide backwards-compatible .d.ts emit due
|
|
// to each now having three type parameters instead of only one.
|
|
if b.ch.isReferenceToType(t, b.ch.getGlobalIterableType()) || b.ch.isReferenceToType(t, b.ch.getGlobalIterableIteratorType()) || b.ch.isReferenceToType(t, b.ch.getGlobalAsyncIterableType()) || b.ch.isReferenceToType(t, b.ch.getGlobalAsyncIterableIteratorType()) {
|
|
if t.AsTypeReference().node == nil || !ast.IsTypeReferenceNode(t.AsTypeReference().node) || t.AsTypeReference().node.TypeArguments() == nil || len(t.AsTypeReference().node.TypeArguments()) < typeParameterCount {
|
|
for typeParameterCount > 0 {
|
|
typeArgument := typeArguments[typeParameterCount-1]
|
|
typeParameter := t.Target().AsInterfaceType().TypeParameters()[typeParameterCount-1]
|
|
defaultType := b.ch.getDefaultFromTypeParameter(typeParameter)
|
|
if defaultType == nil || !b.ch.isTypeIdenticalTo(typeArgument, defaultType) {
|
|
break
|
|
}
|
|
typeParameterCount--
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
typeArgumentNodes = b.mapToTypeNodes(typeArguments[i:typeParameterCount], false /*isBareList*/)
|
|
}
|
|
restoreFlags := b.saveRestoreFlags()
|
|
b.ctx.flags |= nodebuilder.FlagsForbidIndexedAccessSymbolReferences
|
|
finalRef := b.symbolToTypeNode(t.symbol, ast.SymbolFlagsType, typeArgumentNodes)
|
|
restoreFlags()
|
|
if resultType == nil {
|
|
return finalRef
|
|
} else {
|
|
return b.appendReferenceToType(resultType, finalRef)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) visitAndTransformType(t *Type, transform func(b *nodeBuilderImpl, t *Type) *ast.TypeNode) *ast.TypeNode {
|
|
typeId := t.id
|
|
isConstructorObject := t.objectFlags&ObjectFlagsAnonymous != 0 && t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsClass != 0
|
|
var id *CompositeSymbolIdentity
|
|
switch {
|
|
case t.objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node != nil:
|
|
id = &CompositeSymbolIdentity{false, 0, ast.GetNodeId(t.AsTypeReference().node)}
|
|
case t.flags&TypeFlagsConditional != 0:
|
|
id = &CompositeSymbolIdentity{false, 0, ast.GetNodeId(t.AsConditionalType().root.node.AsNode())}
|
|
case t.symbol != nil:
|
|
id = &CompositeSymbolIdentity{isConstructorObject, ast.GetSymbolId(t.symbol), 0}
|
|
default:
|
|
id = nil
|
|
}
|
|
// Since instantiations of the same anonymous type have the same symbol, tracking symbols instead
|
|
// of types allows us to catch circular references to instantiations of the same anonymous type
|
|
|
|
key := CompositeTypeCacheIdentity{typeId, b.ctx.flags, b.ctx.internalFlags}
|
|
if b.ctx.enclosingDeclaration != nil && b.links.Has(b.ctx.enclosingDeclaration) {
|
|
links := b.links.Get(b.ctx.enclosingDeclaration)
|
|
cachedResult, ok := links.serializedTypes[key]
|
|
if ok {
|
|
// TODO:: check if we instead store late painted statements associated with this?
|
|
for _, arg := range cachedResult.trackedSymbols {
|
|
b.ctx.tracker.TrackSymbol(arg.symbol, arg.enclosingDeclaration, arg.meaning)
|
|
}
|
|
if cachedResult.truncating {
|
|
b.ctx.truncating = true
|
|
}
|
|
b.ctx.approximateLength += cachedResult.addedLength
|
|
return b.f.DeepCloneNode(cachedResult.node)
|
|
}
|
|
}
|
|
|
|
var depth int
|
|
if id != nil {
|
|
depth = b.ctx.symbolDepth[*id]
|
|
if depth > 10 {
|
|
return b.createElidedInformationPlaceholder()
|
|
}
|
|
b.ctx.symbolDepth[*id] = depth + 1
|
|
}
|
|
b.ctx.visitedTypes.Add(typeId)
|
|
prevTrackedSymbols := b.ctx.trackedSymbols
|
|
b.ctx.trackedSymbols = nil
|
|
startLength := b.ctx.approximateLength
|
|
result := transform(b, t)
|
|
addedLength := b.ctx.approximateLength - startLength
|
|
if !b.ctx.reportedDiagnostic && !b.ctx.encounteredError {
|
|
links := b.links.Get(b.ctx.enclosingDeclaration)
|
|
if links.serializedTypes == nil {
|
|
links.serializedTypes = make(map[CompositeTypeCacheIdentity]*SerializedTypeEntry)
|
|
}
|
|
links.serializedTypes[key] = &SerializedTypeEntry{
|
|
node: result,
|
|
truncating: b.ctx.truncating,
|
|
addedLength: addedLength,
|
|
trackedSymbols: b.ctx.trackedSymbols,
|
|
}
|
|
}
|
|
b.ctx.visitedTypes.Delete(typeId)
|
|
if id != nil {
|
|
b.ctx.symbolDepth[*id] = depth
|
|
}
|
|
b.ctx.trackedSymbols = prevTrackedSymbols
|
|
return result
|
|
|
|
// !!! TODO: Attempt node reuse or parse nodes to minimize copying once text range setting is set up
|
|
// deepCloneOrReuseNode := func(node T) T {
|
|
// if !nodeIsSynthesized(node) && getParseTreeNode(node) == node {
|
|
// return node
|
|
// }
|
|
// return setTextRange(b.ctx, b.f.cloneNode(visitEachChildWorker(node, deepCloneOrReuseNode, nil /*b.ctx*/, deepCloneOrReuseNodes, deepCloneOrReuseNode)), node)
|
|
// }
|
|
|
|
// deepCloneOrReuseNodes := func(nodes *NodeArray[*ast.Node], visitor Visitor, test func(node *ast.Node) bool, start number, count number) *NodeArray[*ast.Node] {
|
|
// if nodes != nil && nodes.length == 0 {
|
|
// // Ensure we explicitly make a copy of an empty array; visitNodes will not do this unless the array has elements,
|
|
// // which can lead to us reusing the same empty NodeArray more than once within the same AST during type noding.
|
|
// return setTextRangeWorker(b.f.NewNodeArray(nil, nodes.hasTrailingComma), nodes)
|
|
// }
|
|
// return visitNodes(nodes, visitor, test, start, count)
|
|
// }
|
|
}
|
|
|
|
func (b *nodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode {
|
|
inTypeAlias := b.ctx.flags & nodebuilder.FlagsInTypeAlias
|
|
b.ctx.flags &^= nodebuilder.FlagsInTypeAlias
|
|
|
|
if t == nil {
|
|
if b.ctx.flags&nodebuilder.FlagsAllowEmptyUnionOrIntersection == 0 {
|
|
b.ctx.encounteredError = true
|
|
return nil
|
|
// TODO: GH#18217
|
|
}
|
|
b.ctx.approximateLength += 3
|
|
return b.f.NewKeywordTypeNode(ast.KindAnyKeyword)
|
|
}
|
|
|
|
if b.ctx.flags&nodebuilder.FlagsNoTypeReduction == 0 {
|
|
t = b.ch.getReducedType(t)
|
|
}
|
|
|
|
if t.flags&TypeFlagsAny != 0 {
|
|
if t.alias != nil {
|
|
return t.alias.ToTypeReferenceNode(b)
|
|
}
|
|
if t == b.ch.unresolvedType {
|
|
return b.e.AddSyntheticLeadingComment(b.f.NewKeywordTypeNode(ast.KindAnyKeyword), ast.KindMultiLineCommentTrivia, "unresolved", false /*hasTrailingNewLine*/)
|
|
}
|
|
b.ctx.approximateLength += 3
|
|
return b.f.NewKeywordTypeNode(core.IfElse(t == b.ch.intrinsicMarkerType, ast.KindIntrinsicKeyword, ast.KindAnyKeyword))
|
|
}
|
|
if t.flags&TypeFlagsUnknown != 0 {
|
|
return b.f.NewKeywordTypeNode(ast.KindUnknownKeyword)
|
|
}
|
|
if t.flags&TypeFlagsString != 0 {
|
|
b.ctx.approximateLength += 6
|
|
return b.f.NewKeywordTypeNode(ast.KindStringKeyword)
|
|
}
|
|
if t.flags&TypeFlagsNumber != 0 {
|
|
b.ctx.approximateLength += 6
|
|
return b.f.NewKeywordTypeNode(ast.KindNumberKeyword)
|
|
}
|
|
if t.flags&TypeFlagsBigInt != 0 {
|
|
b.ctx.approximateLength += 6
|
|
return b.f.NewKeywordTypeNode(ast.KindBigIntKeyword)
|
|
}
|
|
if t.flags&TypeFlagsBoolean != 0 && t.alias == nil {
|
|
b.ctx.approximateLength += 7
|
|
return b.f.NewKeywordTypeNode(ast.KindBooleanKeyword)
|
|
}
|
|
if t.flags&TypeFlagsEnumLike != 0 {
|
|
if t.symbol.Flags&ast.SymbolFlagsEnumMember != 0 {
|
|
parentSymbol := b.ch.getParentOfSymbol(t.symbol)
|
|
parentName := b.symbolToTypeNode(parentSymbol, ast.SymbolFlagsType, nil)
|
|
if b.ch.getDeclaredTypeOfSymbol(parentSymbol) == t {
|
|
return parentName
|
|
}
|
|
memberName := ast.SymbolName(t.symbol)
|
|
if scanner.IsIdentifierText(memberName, core.LanguageVariantStandard) {
|
|
return b.appendReferenceToType(parentName /* as TypeReferenceNode | ImportTypeNode */, b.f.NewTypeReferenceNode(b.f.NewIdentifier(memberName), nil /*typeArguments*/))
|
|
}
|
|
if ast.IsImportTypeNode(parentName) {
|
|
parentName.AsImportTypeNode().IsTypeOf = true
|
|
// mutably update, node is freshly manufactured anyhow
|
|
return b.f.NewIndexedAccessTypeNode(parentName, b.f.NewLiteralTypeNode(b.f.NewStringLiteral(memberName)))
|
|
} else if ast.IsTypeReferenceNode(parentName) {
|
|
return b.f.NewIndexedAccessTypeNode(b.f.NewTypeQueryNode(parentName.AsTypeReferenceNode().TypeName, nil), b.f.NewLiteralTypeNode(b.f.NewStringLiteral(memberName)))
|
|
} else {
|
|
panic("Unhandled type node kind returned from `symbolToTypeNode`.")
|
|
}
|
|
}
|
|
return b.symbolToTypeNode(t.symbol, ast.SymbolFlagsType, nil)
|
|
}
|
|
if t.flags&TypeFlagsStringLiteral != 0 {
|
|
b.ctx.approximateLength += len(t.AsLiteralType().value.(string)) + 2
|
|
lit := b.f.NewStringLiteral(t.AsLiteralType().value.(string) /*, b.flags&nodebuilder.FlagsUseSingleQuotesForStringLiteralType != 0*/)
|
|
b.e.AddEmitFlags(lit, printer.EFNoAsciiEscaping)
|
|
return b.f.NewLiteralTypeNode(lit)
|
|
}
|
|
if t.flags&TypeFlagsNumberLiteral != 0 {
|
|
value := t.AsLiteralType().value.(jsnum.Number)
|
|
b.ctx.approximateLength += len(value.String())
|
|
if value < 0 {
|
|
return b.f.NewLiteralTypeNode(b.f.NewPrefixUnaryExpression(ast.KindMinusToken, b.f.NewNumericLiteral(value.String()[1:])))
|
|
} else {
|
|
return b.f.NewLiteralTypeNode(b.f.NewNumericLiteral(value.String()))
|
|
}
|
|
}
|
|
if t.flags&TypeFlagsBigIntLiteral != 0 {
|
|
b.ctx.approximateLength += len(pseudoBigIntToString(getBigIntLiteralValue(t))) + 1
|
|
return b.f.NewLiteralTypeNode(b.f.NewBigIntLiteral(pseudoBigIntToString(getBigIntLiteralValue(t)) + "n"))
|
|
}
|
|
if t.flags&TypeFlagsBooleanLiteral != 0 {
|
|
if t.AsLiteralType().value.(bool) {
|
|
b.ctx.approximateLength += 4
|
|
return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(ast.KindTrueKeyword))
|
|
} else {
|
|
b.ctx.approximateLength += 5
|
|
return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(ast.KindFalseKeyword))
|
|
}
|
|
}
|
|
if t.flags&TypeFlagsUniqueESSymbol != 0 {
|
|
if b.ctx.flags&nodebuilder.FlagsAllowUniqueESSymbolType == 0 {
|
|
if b.ch.IsValueSymbolAccessible(t.symbol, b.ctx.enclosingDeclaration) {
|
|
b.ctx.approximateLength += 6
|
|
return b.symbolToTypeNode(t.symbol, ast.SymbolFlagsValue, nil)
|
|
}
|
|
b.ctx.tracker.ReportInaccessibleUniqueSymbolError()
|
|
}
|
|
b.ctx.approximateLength += 13
|
|
return b.f.NewTypeOperatorNode(ast.KindUniqueKeyword, b.f.NewKeywordTypeNode(ast.KindSymbolKeyword))
|
|
}
|
|
if t.flags&TypeFlagsVoid != 0 {
|
|
b.ctx.approximateLength += 4
|
|
return b.f.NewKeywordTypeNode(ast.KindVoidKeyword)
|
|
}
|
|
if t.flags&TypeFlagsUndefined != 0 {
|
|
b.ctx.approximateLength += 9
|
|
return b.f.NewKeywordTypeNode(ast.KindUndefinedKeyword)
|
|
}
|
|
if t.flags&TypeFlagsNull != 0 {
|
|
b.ctx.approximateLength += 4
|
|
return b.f.NewLiteralTypeNode(b.f.NewKeywordExpression(ast.KindNullKeyword))
|
|
}
|
|
if t.flags&TypeFlagsNever != 0 {
|
|
b.ctx.approximateLength += 5
|
|
return b.f.NewKeywordTypeNode(ast.KindNeverKeyword)
|
|
}
|
|
if t.flags&TypeFlagsESSymbol != 0 {
|
|
b.ctx.approximateLength += 6
|
|
return b.f.NewKeywordTypeNode(ast.KindSymbolKeyword)
|
|
}
|
|
if t.flags&TypeFlagsNonPrimitive != 0 {
|
|
b.ctx.approximateLength += 6
|
|
return b.f.NewKeywordTypeNode(ast.KindObjectKeyword)
|
|
}
|
|
if isThisTypeParameter(t) {
|
|
if b.ctx.flags&nodebuilder.FlagsInObjectTypeLiteral != 0 {
|
|
if !b.ctx.encounteredError && b.ctx.flags&nodebuilder.FlagsAllowThisInObjectLiteral == 0 {
|
|
b.ctx.encounteredError = true
|
|
}
|
|
b.ctx.tracker.ReportInaccessibleThisError()
|
|
}
|
|
b.ctx.approximateLength += 4
|
|
return b.f.NewThisTypeNode()
|
|
}
|
|
|
|
if inTypeAlias == 0 && t.alias != nil && (b.ctx.flags&nodebuilder.FlagsUseAliasDefinedOutsideCurrentScope != 0 || b.ch.IsTypeSymbolAccessible(t.alias.Symbol(), b.ctx.enclosingDeclaration)) {
|
|
sym := t.alias.Symbol()
|
|
typeArgumentNodes := b.mapToTypeNodes(t.alias.TypeArguments(), false /*isBareList*/)
|
|
if isReservedMemberName(sym.Name) && sym.Flags&ast.SymbolFlagsClass == 0 {
|
|
return b.f.NewTypeReferenceNode(b.f.NewIdentifier(""), typeArgumentNodes)
|
|
}
|
|
if typeArgumentNodes != nil && len(typeArgumentNodes.Nodes) == 1 && sym == b.ch.globalArrayType.symbol {
|
|
return b.f.NewArrayTypeNode(typeArgumentNodes.Nodes[0])
|
|
}
|
|
return b.symbolToTypeNode(sym, ast.SymbolFlagsType, typeArgumentNodes)
|
|
}
|
|
|
|
objectFlags := t.objectFlags
|
|
|
|
if objectFlags&ObjectFlagsReference != 0 {
|
|
debug.Assert(t.Flags()&TypeFlagsObject != 0)
|
|
if t.AsTypeReference().node != nil {
|
|
return b.visitAndTransformType(t, (*nodeBuilderImpl).typeReferenceToTypeNode)
|
|
} else {
|
|
return b.typeReferenceToTypeNode(t)
|
|
}
|
|
}
|
|
if t.flags&TypeFlagsTypeParameter != 0 || objectFlags&ObjectFlagsClassOrInterface != 0 {
|
|
if t.flags&TypeFlagsTypeParameter != 0 && slices.Contains(b.ctx.inferTypeParameters, t) {
|
|
b.ctx.approximateLength += len(ast.SymbolName(t.symbol)) + 6
|
|
var constraintNode *ast.TypeNode
|
|
constraint := b.ch.getConstraintOfTypeParameter(t)
|
|
if constraint != nil {
|
|
// If the infer type has a constraint that is not the same as the constraint
|
|
// we would have normally inferred based on b, we emit the constraint
|
|
// using `infer T extends ?`. We omit inferred constraints from type references
|
|
// as they may be elided.
|
|
inferredConstraint := b.ch.getInferredTypeParameterConstraint(t, true /*omitTypeReferences*/)
|
|
if !(inferredConstraint != nil && b.ch.isTypeIdenticalTo(constraint, inferredConstraint)) {
|
|
b.ctx.approximateLength += 9
|
|
constraintNode = b.typeToTypeNode(constraint)
|
|
}
|
|
}
|
|
return b.f.NewInferTypeNode(b.typeParameterToDeclarationWithConstraint(t, constraintNode))
|
|
}
|
|
if b.ctx.flags&nodebuilder.FlagsGenerateNamesForShadowedTypeParams != 0 && t.flags&TypeFlagsTypeParameter != 0 {
|
|
name := b.typeParameterToName(t)
|
|
b.ctx.approximateLength += len(name.Text)
|
|
return b.f.NewTypeReferenceNode(b.f.NewIdentifier(name.Text), nil /*typeArguments*/)
|
|
}
|
|
// Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter.
|
|
if t.symbol != nil {
|
|
return b.symbolToTypeNode(t.symbol, ast.SymbolFlagsType, nil)
|
|
}
|
|
var name string
|
|
if (t == b.ch.markerSuperTypeForCheck || t == b.ch.markerSubTypeForCheck) && b.ch.varianceTypeParameter != nil && b.ch.varianceTypeParameter.symbol != nil {
|
|
name = (core.IfElse(t == b.ch.markerSubTypeForCheck, "sub-", "super-")) + ast.SymbolName(b.ch.varianceTypeParameter.symbol)
|
|
} else {
|
|
name = "?"
|
|
}
|
|
return b.f.NewTypeReferenceNode(b.f.NewIdentifier(name), nil /*typeArguments*/)
|
|
}
|
|
if t.flags&TypeFlagsUnion != 0 && t.AsUnionType().origin != nil {
|
|
t = t.AsUnionType().origin
|
|
}
|
|
if t.flags&(TypeFlagsUnion|TypeFlagsIntersection) != 0 {
|
|
var types []*Type
|
|
if t.flags&TypeFlagsUnion != 0 {
|
|
types = b.ch.formatUnionTypes(t.AsUnionType().types)
|
|
} else {
|
|
types = t.AsIntersectionType().types
|
|
}
|
|
if len(types) == 1 {
|
|
return b.typeToTypeNode(types[0])
|
|
}
|
|
typeNodes := b.mapToTypeNodes(types, true /*isBareList*/)
|
|
if typeNodes != nil && len(typeNodes.Nodes) > 0 {
|
|
if t.flags&TypeFlagsUnion != 0 {
|
|
return b.f.NewUnionTypeNode(typeNodes)
|
|
} else {
|
|
return b.f.NewIntersectionTypeNode(typeNodes)
|
|
}
|
|
} else {
|
|
if !b.ctx.encounteredError && b.ctx.flags&nodebuilder.FlagsAllowEmptyUnionOrIntersection == 0 {
|
|
b.ctx.encounteredError = true
|
|
}
|
|
return nil
|
|
// TODO: GH#18217
|
|
}
|
|
}
|
|
if objectFlags&(ObjectFlagsAnonymous|ObjectFlagsMapped) != 0 {
|
|
debug.Assert(t.Flags()&TypeFlagsObject != 0)
|
|
// The type is an object literal type.
|
|
return b.createAnonymousTypeNode(t)
|
|
}
|
|
if t.flags&TypeFlagsIndex != 0 {
|
|
indexedType := t.Target()
|
|
b.ctx.approximateLength += 6
|
|
indexTypeNode := b.typeToTypeNode(indexedType)
|
|
return b.f.NewTypeOperatorNode(ast.KindKeyOfKeyword, indexTypeNode)
|
|
}
|
|
if t.flags&TypeFlagsTemplateLiteral != 0 {
|
|
texts := t.AsTemplateLiteralType().texts
|
|
types := t.AsTemplateLiteralType().types
|
|
templateHead := b.f.NewTemplateHead(texts[0], texts[0], ast.TokenFlagsNone)
|
|
templateSpans := b.f.NewNodeList(core.MapIndex(types, func(t *Type, i int) *ast.Node {
|
|
var res *ast.TemplateMiddleOrTail
|
|
if i < len(types)-1 {
|
|
res = b.f.NewTemplateMiddle(texts[i+1], texts[i+1], ast.TokenFlagsNone)
|
|
} else {
|
|
res = b.f.NewTemplateTail(texts[i+1], texts[i+1], ast.TokenFlagsNone)
|
|
}
|
|
return b.f.NewTemplateLiteralTypeSpan(b.typeToTypeNode(t), res)
|
|
}))
|
|
b.ctx.approximateLength += 2
|
|
return b.f.NewTemplateLiteralTypeNode(templateHead, templateSpans)
|
|
}
|
|
if t.flags&TypeFlagsStringMapping != 0 {
|
|
typeNode := b.typeToTypeNode(t.Target())
|
|
return b.symbolToTypeNode(t.AsStringMappingType().symbol, ast.SymbolFlagsType, b.f.NewNodeList([]*ast.Node{typeNode}))
|
|
}
|
|
if t.flags&TypeFlagsIndexedAccess != 0 {
|
|
objectTypeNode := b.typeToTypeNode(t.AsIndexedAccessType().objectType)
|
|
indexTypeNode := b.typeToTypeNode(t.AsIndexedAccessType().indexType)
|
|
b.ctx.approximateLength += 2
|
|
return b.f.NewIndexedAccessTypeNode(objectTypeNode, indexTypeNode)
|
|
}
|
|
if t.flags&TypeFlagsConditional != 0 {
|
|
return b.visitAndTransformType(t, (*nodeBuilderImpl).conditionalTypeToTypeNode)
|
|
}
|
|
if t.flags&TypeFlagsSubstitution != 0 {
|
|
typeNode := b.typeToTypeNode(t.AsSubstitutionType().baseType)
|
|
if !b.ch.isNoInferType(t) {
|
|
return typeNode
|
|
}
|
|
noInferSymbol := b.ch.getGlobalTypeAliasSymbol("NoInfer", 1, false)
|
|
if noInferSymbol != nil {
|
|
return b.symbolToTypeNode(noInferSymbol, ast.SymbolFlagsType, b.f.NewNodeList([]*ast.Node{typeNode}))
|
|
} else {
|
|
return typeNode
|
|
}
|
|
}
|
|
|
|
panic("Should be unreachable.")
|
|
}
|
|
|
|
// Direct serialization core functions for types, type aliases, and symbols
|
|
|
|
func (t *TypeAlias) ToTypeReferenceNode(b *nodeBuilderImpl) *ast.Node {
|
|
return b.f.NewTypeReferenceNode(b.symbolToEntityNameNode(t.Symbol()), b.mapToTypeNodes(t.TypeArguments(), false /*isBareList*/))
|
|
}
|