package checker

import (
	"context"
	"fmt"
	"iter"
	"maps"
	"math"
	"slices"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"

	"github.com/microsoft/typescript-go/internal/ast"
	"github.com/microsoft/typescript-go/internal/binder"
	"github.com/microsoft/typescript-go/internal/collections"
	"github.com/microsoft/typescript-go/internal/core"
	"github.com/microsoft/typescript-go/internal/debug"
	"github.com/microsoft/typescript-go/internal/diagnostics"
	"github.com/microsoft/typescript-go/internal/evaluator"
	"github.com/microsoft/typescript-go/internal/jsnum"
	"github.com/microsoft/typescript-go/internal/module"
	"github.com/microsoft/typescript-go/internal/modulespecifiers"
	"github.com/microsoft/typescript-go/internal/scanner"
	"github.com/microsoft/typescript-go/internal/stringutil"
	"github.com/microsoft/typescript-go/internal/tracing"
	"github.com/microsoft/typescript-go/internal/tsoptions"
	"github.com/microsoft/typescript-go/internal/tspath"
	"github.com/zeebo/xxh3"
)

// CheckMode

type CheckMode uint32

const (
	CheckModeNormal               CheckMode = 0      // Normal type checking
	CheckModeContextual           CheckMode = 1 << 0 // Explicitly assigned contextual type, therefore not cacheable
	CheckModeInferential          CheckMode = 1 << 1 // Inferential typing
	CheckModeSkipContextSensitive CheckMode = 1 << 2 // Skip context sensitive function expressions
	CheckModeSkipGenericFunctions CheckMode = 1 << 3 // Skip single signature generic functions
	CheckModeIsForSignatureHelp   CheckMode = 1 << 4 // Call resolution for purposes of signature help
	CheckModeRestBindingElement   CheckMode = 1 << 5 // Checking a type that is going to be used to determine the type of a rest binding element
	//   e.g. in `const { a, ...rest } = foo`, when checking the type of `foo` to determine the type of `rest`,
	//   we need to preserve generic types instead of substituting them for constraints
	CheckModeTypeOnly   CheckMode = 1 << 6 // Called from getTypeOfExpression, diagnostics may be omitted
	CheckModeForceTuple CheckMode = 1 << 7
)

type TypeSystemEntity any

type TypeSystemPropertyName int32

const (
	TypeSystemPropertyNameType TypeSystemPropertyName = iota
	TypeSystemPropertyNameResolvedBaseConstructorType
	TypeSystemPropertyNameDeclaredType
	TypeSystemPropertyNameResolvedReturnType
	TypeSystemPropertyNameResolvedBaseConstraint
	TypeSystemPropertyNameResolvedTypeArguments
	TypeSystemPropertyNameResolvedBaseTypes
	TypeSystemPropertyNameWriteType
	TypeSystemPropertyNameInitializerIsUndefined
	TypeSystemPropertyNameAliasTarget
)

type TypeResolution struct {
	target       TypeSystemEntity
	propertyName TypeSystemPropertyName
	result       bool
}

// ContextualInfo

type ContextualInfo struct {
	node    *ast.Node
	t       *Type
	isCache bool
}

// InferenceContextInfo

type InferenceContextInfo struct {
	node    *ast.Node
	context *InferenceContext
}

// WideningKind

type WideningKind int32

const (
	WideningKindNormal WideningKind = iota
	WideningKindFunctionReturn
	WideningKindGeneratorNext
	WideningKindGeneratorYield
)

// EnumLiteralKey

type EnumLiteralKey struct {
	enumSymbol *ast.Symbol
	value      any
}

// EnumRelationKey

type EnumRelationKey struct {
	sourceId ast.SymbolId
	targetId ast.SymbolId
}

// TypeCacheKind

type CachedTypeKind int32

const (
	CachedTypeKindLiteralUnionBaseType CachedTypeKind = iota
	CachedTypeKindIndexType
	CachedTypeKindStringIndexType
	CachedTypeKindEquivalentBaseType
	CachedTypeKindApparentType
	CachedTypeKindAwaitedType
	CachedTypeKindEvolvingArrayType
	CachedTypeKindArrayLiteralType
	CachedTypeKindPermissiveInstantiation
	CachedTypeKindRestrictiveInstantiation
	CachedTypeKindRestrictiveTypeParameter
	CachedTypeKindIndexedAccessForReading
	CachedTypeKindIndexedAccessForWriting
	CachedTypeKindWidened
	CachedTypeKindRegularObjectLiteral
	CachedTypeKindPromisedTypeOfPromise
	CachedTypeKindDefaultOnlyType
	CachedTypeKindSyntheticType
	CachedTypeKindDecoratorContext
	CachedTypeKindDecoratorContextStatic
	CachedTypeKindDecoratorContextPrivate
	CachedTypeKindDecoratorContextPrivateStatic
)

// CachedTypeKey

type CachedTypeKey struct {
	kind   CachedTypeKind
	typeId TypeId
}

// NarrowedTypeKey

type NarrowedTypeKey struct {
	t            *Type
	candidate    *Type
	assumeTrue   bool
	checkDerived bool
}

// UnionOfUnionKey

type UnionOfUnionKey struct {
	id1 TypeId
	id2 TypeId
	r   UnionReduction
	a   CacheHashKey
}

// CachedSignatureKey

type CachedSignatureKey struct {
	sig *Signature
	key CacheHashKey // Type list key or one of the special keys below
}

var (
	SignatureKeyErased    = CacheHashKey(xxh3.HashString128("-"))
	SignatureKeyCanonical = CacheHashKey(xxh3.HashString128("*"))
	SignatureKeyBase      = CacheHashKey(xxh3.HashString128("#"))
	SignatureKeyInner     = CacheHashKey(xxh3.HashString128("<"))
	SignatureKeyOuter     = CacheHashKey(xxh3.HashString128(">"))
)

// StringMappingKey

type StringMappingKey struct {
	s *ast.Symbol
	t *Type
}

// AssignmentReducedKey

type AssignmentReducedKey struct {
	id1 TypeId
	id2 TypeId
}

// DiscriminatedContextualTypeKey

type DiscriminatedContextualTypeKey struct {
	nodeId ast.NodeId
	typeId TypeId
}

// InstantiationExpressionKey

type InstantiationExpressionKey struct {
	nodeId ast.NodeId
	typeId TypeId
}

// SubstitutionTypeKey

type SubstitutionTypeKey struct {
	baseId       TypeId
	constraintId TypeId
}

// ReverseMappedTypeKey

type ReverseMappedTypeKey struct {
	sourceId     TypeId
	targetId     TypeId
	constraintId TypeId
}

// IterationTypesKey

type IterationTypesKey struct {
	typeId TypeId
	use    IterationUse
}

// PropertiesTypesKey

type PropertiesTypesKey struct {
	typeId            TypeId
	include           TypeFlags
	includeOrigin     bool
	unresolvedMembers bool
}

// NonExistentPropertyKey

type NonExistentPropertyKey struct {
	propNode       *ast.Node
	containingType *Type
	isUncheckedJS  bool
}

// FlowLoopKey

type FlowLoopKey struct {
	flowNode *ast.FlowNode
	refKey   CacheHashKey
}

type FlowLoopInfo struct {
	key   FlowLoopKey
	types []*Type
}

// InferenceFlags

type InferenceFlags uint32

const (
	InferenceFlagsNone                   InferenceFlags = 0      // No special inference behaviors
	InferenceFlagsNoDefault              InferenceFlags = 1 << 0 // Infer silentNeverType for no inferences (otherwise anyType or unknownType)
	InferenceFlagsAnyDefault             InferenceFlags = 1 << 1 // Infer anyType (in JS files) for no inferences (otherwise unknownType)
	InferenceFlagsSkippedGenericFunction InferenceFlags = 1 << 2 // A generic function was skipped during inference
)

// InferenceContext

type InferenceContext struct {
	inferences                    []*InferenceInfo // Inferences made for each type parameter
	signature                     *Signature       // Generic signature for which inferences are made (if any)
	flags                         InferenceFlags   // Inference flags
	compareTypes                  TypeComparer     // Type comparer function
	mapper                        *TypeMapper      // Mapper that fixes inferences
	nonFixingMapper               *TypeMapper      // Mapper that doesn't fix inferences
	returnMapper                  *TypeMapper      // Type mapper for inferences from return types (if any)
	outerReturnMapper             *TypeMapper      // Type mapper for inferences from return types of outer function (if any)
	inferredTypeParameters        []*Type          // Inferred type parameters for function result
	intraExpressionInferenceSites []IntraExpressionInferenceSite
}

type InferenceInfo struct {
	typeParameter    *Type             // Type parameter for which inferences are being made
	candidates       []*Type           // Candidates in covariant positions in decreasing depth order
	candidateDepths  []int             // Type argument depths of covariant inferences
	contraCandidates []*Type           // Candidates in contravariant positions
	inferredType     *Type             // Cache for resolved inferred type
	priority         InferencePriority // Priority of current inference set
	topLevel         bool              // True if all inferences are to top level occurrences
	isFixed          bool              // True if inferences are fixed
	impliedArity     int               // Implied arity (or -1)
}

type InferencePriority int32

const (
	InferencePriorityNone                         InferencePriority = 0
	InferencePriorityNakedTypeVariable            InferencePriority = 1 << 0  // Naked type variable in union or intersection type
	InferencePrioritySpeculativeTuple             InferencePriority = 1 << 1  // Speculative tuple inference
	InferencePrioritySubstituteSource             InferencePriority = 1 << 2  // Source of inference originated within a substitution type's substitute
	InferencePriorityHomomorphicMappedType        InferencePriority = 1 << 3  // Reverse inference for homomorphic mapped type
	InferencePriorityPartialHomomorphicMappedType InferencePriority = 1 << 4  // Partial reverse inference for homomorphic mapped type
	InferencePriorityMappedTypeConstraint         InferencePriority = 1 << 5  // Reverse inference for mapped type
	InferencePriorityContravariantConditional     InferencePriority = 1 << 6  // Conditional type in contravariant position
	InferencePriorityReturnType                   InferencePriority = 1 << 7  // Inference made from return type of generic function
	InferencePriorityLiteralKeyof                 InferencePriority = 1 << 8  // Inference made from a string literal to a keyof T
	InferencePriorityNoConstraints                InferencePriority = 1 << 9  // Don't infer from constraints of instantiable types
	InferencePriorityAlwaysStrict                 InferencePriority = 1 << 10 // Always use strict rules for contravariant inferences
	InferencePriorityMaxValue                     InferencePriority = 1 << 11 // Seed for inference priority tracking
	InferencePriorityCircularity                  InferencePriority = -1      // Inference circularity (value less than all other priorities)

	InferencePriorityPriorityImpliesCombination = InferencePriorityReturnType | InferencePriorityMappedTypeConstraint | InferencePriorityLiteralKeyof // These priorities imply that the resulting type should be a combination of all candidates
)

type IntraExpressionInferenceSite struct {
	node *ast.Node
	t    *Type
}

type DeclarationMeaning uint32

const (
	DeclarationMeaningGetAccessor DeclarationMeaning = 1 << iota
	DeclarationMeaningSetAccessor
	DeclarationMeaningPropertyAssignment
	DeclarationMeaningMethod
	DeclarationMeaningPrivateStatic
	DeclarationMeaningGetOrSetAccessor           = DeclarationMeaningGetAccessor | DeclarationMeaningSetAccessor
	DeclarationMeaningPropertyAssignmentOrMethod = DeclarationMeaningPropertyAssignment | DeclarationMeaningMethod
)

type DeclarationSpaces int32

const (
	DeclarationSpacesNone            DeclarationSpaces = 0
	DeclarationSpacesExportValue     DeclarationSpaces = 1 << 0
	DeclarationSpacesExportType      DeclarationSpaces = 1 << 1
	DeclarationSpacesExportNamespace DeclarationSpaces = 1 << 2
)

// IntrinsicTypeKind

type IntrinsicTypeKind int32

const (
	IntrinsicTypeKindUnknown IntrinsicTypeKind = iota
	IntrinsicTypeKindUppercase
	IntrinsicTypeKindLowercase
	IntrinsicTypeKindCapitalize
	IntrinsicTypeKindUncapitalize
	IntrinsicTypeKindNoInfer
)

var intrinsicTypeKinds = map[string]IntrinsicTypeKind{
	"Uppercase":    IntrinsicTypeKindUppercase,
	"Lowercase":    IntrinsicTypeKindLowercase,
	"Capitalize":   IntrinsicTypeKindCapitalize,
	"Uncapitalize": IntrinsicTypeKindUncapitalize,
	"NoInfer":      IntrinsicTypeKindNoInfer,
}

type MappedTypeModifiers uint32

const (
	MappedTypeModifiersIncludeReadonly MappedTypeModifiers = 1 << 0
	MappedTypeModifiersExcludeReadonly MappedTypeModifiers = 1 << 1
	MappedTypeModifiersIncludeOptional MappedTypeModifiers = 1 << 2
	MappedTypeModifiersExcludeOptional MappedTypeModifiers = 1 << 3
)

type MappedTypeNameTypeKind int32

const (
	MappedTypeNameTypeKindNone MappedTypeNameTypeKind = iota
	MappedTypeNameTypeKindFiltering
	MappedTypeNameTypeKindRemapping
)

type ReferenceHint int32

const (
	ReferenceHintUnspecified ReferenceHint = iota
	ReferenceHintIdentifier
	ReferenceHintProperty
	ReferenceHintExportAssignment
	ReferenceHintJsx
	ReferenceHintExportImportEquals
	ReferenceHintExportSpecifier
	ReferenceHintDecorator
)

type TypeFacts uint32

const (
	TypeFactsNone               TypeFacts = 0
	TypeFactsTypeofEQString     TypeFacts = 1 << 0
	TypeFactsTypeofEQNumber     TypeFacts = 1 << 1
	TypeFactsTypeofEQBigInt     TypeFacts = 1 << 2
	TypeFactsTypeofEQBoolean    TypeFacts = 1 << 3
	TypeFactsTypeofEQSymbol     TypeFacts = 1 << 4
	TypeFactsTypeofEQObject     TypeFacts = 1 << 5
	TypeFactsTypeofEQFunction   TypeFacts = 1 << 6
	TypeFactsTypeofEQHostObject TypeFacts = 1 << 7
	TypeFactsTypeofNEString     TypeFacts = 1 << 8
	TypeFactsTypeofNENumber     TypeFacts = 1 << 9
	TypeFactsTypeofNEBigInt     TypeFacts = 1 << 10
	TypeFactsTypeofNEBoolean    TypeFacts = 1 << 11
	TypeFactsTypeofNESymbol     TypeFacts = 1 << 12
	TypeFactsTypeofNEObject     TypeFacts = 1 << 13
	TypeFactsTypeofNEFunction   TypeFacts = 1 << 14
	TypeFactsTypeofNEHostObject TypeFacts = 1 << 15
	TypeFactsEQUndefined        TypeFacts = 1 << 16
	TypeFactsEQNull             TypeFacts = 1 << 17
	TypeFactsEQUndefinedOrNull  TypeFacts = 1 << 18
	TypeFactsNEUndefined        TypeFacts = 1 << 19
	TypeFactsNENull             TypeFacts = 1 << 20
	TypeFactsNEUndefinedOrNull  TypeFacts = 1 << 21
	TypeFactsTruthy             TypeFacts = 1 << 22
	TypeFactsFalsy              TypeFacts = 1 << 23
	TypeFactsIsUndefined        TypeFacts = 1 << 24
	TypeFactsIsNull             TypeFacts = 1 << 25
	TypeFactsIsUndefinedOrNull  TypeFacts = TypeFactsIsUndefined | TypeFactsIsNull
	TypeFactsAll                TypeFacts = (1 << 27) - 1
	// The following members encode facts about particular kinds of types for use in the getTypeFacts function.
	// The presence of a particular fact means that the given test is true for some (and possibly all) values
	// of that kind of type.
	TypeFactsBaseStringStrictFacts     TypeFacts = TypeFactsTypeofEQString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull
	TypeFactsBaseStringFacts           TypeFacts = TypeFactsBaseStringStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy
	TypeFactsStringStrictFacts         TypeFacts = TypeFactsBaseStringStrictFacts | TypeFactsTruthy | TypeFactsFalsy
	TypeFactsStringFacts               TypeFacts = TypeFactsBaseStringFacts | TypeFactsTruthy
	TypeFactsEmptyStringStrictFacts    TypeFacts = TypeFactsBaseStringStrictFacts | TypeFactsFalsy
	TypeFactsEmptyStringFacts          TypeFacts = TypeFactsBaseStringFacts
	TypeFactsNonEmptyStringStrictFacts TypeFacts = TypeFactsBaseStringStrictFacts | TypeFactsTruthy
	TypeFactsNonEmptyStringFacts       TypeFacts = TypeFactsBaseStringFacts | TypeFactsTruthy
	TypeFactsBaseNumberStrictFacts     TypeFacts = TypeFactsTypeofEQNumber | TypeFactsTypeofNEString | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull
	TypeFactsBaseNumberFacts           TypeFacts = TypeFactsBaseNumberStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy
	TypeFactsNumberStrictFacts         TypeFacts = TypeFactsBaseNumberStrictFacts | TypeFactsTruthy | TypeFactsFalsy
	TypeFactsNumberFacts               TypeFacts = TypeFactsBaseNumberFacts | TypeFactsTruthy
	TypeFactsZeroNumberStrictFacts     TypeFacts = TypeFactsBaseNumberStrictFacts | TypeFactsFalsy
	TypeFactsZeroNumberFacts           TypeFacts = TypeFactsBaseNumberFacts
	TypeFactsNonZeroNumberStrictFacts  TypeFacts = TypeFactsBaseNumberStrictFacts | TypeFactsTruthy
	TypeFactsNonZeroNumberFacts        TypeFacts = TypeFactsBaseNumberFacts | TypeFactsTruthy
	TypeFactsBaseBigIntStrictFacts     TypeFacts = TypeFactsTypeofEQBigInt | TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull
	TypeFactsBaseBigIntFacts           TypeFacts = TypeFactsBaseBigIntStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy
	TypeFactsBigIntStrictFacts         TypeFacts = TypeFactsBaseBigIntStrictFacts | TypeFactsTruthy | TypeFactsFalsy
	TypeFactsBigIntFacts               TypeFacts = TypeFactsBaseBigIntFacts | TypeFactsTruthy
	TypeFactsZeroBigIntStrictFacts     TypeFacts = TypeFactsBaseBigIntStrictFacts | TypeFactsFalsy
	TypeFactsZeroBigIntFacts           TypeFacts = TypeFactsBaseBigIntFacts
	TypeFactsNonZeroBigIntStrictFacts  TypeFacts = TypeFactsBaseBigIntStrictFacts | TypeFactsTruthy
	TypeFactsNonZeroBigIntFacts        TypeFacts = TypeFactsBaseBigIntFacts | TypeFactsTruthy
	TypeFactsBaseBooleanStrictFacts    TypeFacts = TypeFactsTypeofEQBoolean | TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull
	TypeFactsBaseBooleanFacts          TypeFacts = TypeFactsBaseBooleanStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy
	TypeFactsBooleanStrictFacts        TypeFacts = TypeFactsBaseBooleanStrictFacts | TypeFactsTruthy | TypeFactsFalsy
	TypeFactsBooleanFacts              TypeFacts = TypeFactsBaseBooleanFacts | TypeFactsTruthy
	TypeFactsFalseStrictFacts          TypeFacts = TypeFactsBaseBooleanStrictFacts | TypeFactsFalsy
	TypeFactsFalseFacts                TypeFacts = TypeFactsBaseBooleanFacts
	TypeFactsTrueStrictFacts           TypeFacts = TypeFactsBaseBooleanStrictFacts | TypeFactsTruthy
	TypeFactsTrueFacts                 TypeFacts = TypeFactsBaseBooleanFacts | TypeFactsTruthy
	TypeFactsSymbolStrictFacts         TypeFacts = TypeFactsTypeofEQSymbol | TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull | TypeFactsTruthy
	TypeFactsSymbolFacts               TypeFacts = TypeFactsSymbolStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy
	TypeFactsObjectStrictFacts         TypeFacts = TypeFactsTypeofEQObject | TypeFactsTypeofEQHostObject | TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEFunction | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull | TypeFactsTruthy
	TypeFactsObjectFacts               TypeFacts = TypeFactsObjectStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy
	TypeFactsFunctionStrictFacts       TypeFacts = TypeFactsTypeofEQFunction | TypeFactsTypeofEQHostObject | TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsNEUndefined | TypeFactsNENull | TypeFactsNEUndefinedOrNull | TypeFactsTruthy
	TypeFactsFunctionFacts             TypeFacts = TypeFactsFunctionStrictFacts | TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsFalsy
	TypeFactsVoidFacts                 TypeFacts = TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsEQUndefined | TypeFactsEQUndefinedOrNull | TypeFactsNENull | TypeFactsFalsy
	TypeFactsUndefinedFacts            TypeFacts = TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsEQUndefined | TypeFactsEQUndefinedOrNull | TypeFactsNENull | TypeFactsFalsy | TypeFactsIsUndefined
	TypeFactsNullFacts                 TypeFacts = TypeFactsTypeofEQObject | TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEFunction | TypeFactsTypeofNEHostObject | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsNEUndefined | TypeFactsFalsy | TypeFactsIsNull
	TypeFactsEmptyObjectStrictFacts    TypeFacts = TypeFactsAll & ^(TypeFactsEQUndefined | TypeFactsEQNull | TypeFactsEQUndefinedOrNull | TypeFactsIsUndefinedOrNull)
	TypeFactsEmptyObjectFacts          TypeFacts = TypeFactsAll & ^TypeFactsIsUndefinedOrNull
	TypeFactsUnknownFacts              TypeFacts = TypeFactsAll & ^TypeFactsIsUndefinedOrNull
	TypeFactsAllTypeofNE               TypeFacts = TypeFactsTypeofNEString | TypeFactsTypeofNENumber | TypeFactsTypeofNEBigInt | TypeFactsTypeofNEBoolean | TypeFactsTypeofNESymbol | TypeFactsTypeofNEObject | TypeFactsTypeofNEFunction | TypeFactsNEUndefined
	// Masks
	TypeFactsOrFactsMask  TypeFacts = TypeFactsTypeofEQFunction | TypeFactsTypeofNEObject
	TypeFactsAndFactsMask TypeFacts = TypeFactsAll & ^TypeFactsOrFactsMask
)

type IterationUse uint32

const (
	IterationUseAllowsSyncIterablesFlag  IterationUse = 1 << 0
	IterationUseAllowsAsyncIterablesFlag IterationUse = 1 << 1
	IterationUseAllowsStringInputFlag    IterationUse = 1 << 2
	IterationUseForOfFlag                IterationUse = 1 << 3
	IterationUseYieldStarFlag            IterationUse = 1 << 4
	IterationUseSpreadFlag               IterationUse = 1 << 5
	IterationUseDestructuringFlag        IterationUse = 1 << 6
	IterationUsePossiblyOutOfBounds      IterationUse = 1 << 7
	// Spread, Destructuring, Array element assignment
	IterationUseElement                  = IterationUseAllowsSyncIterablesFlag
	IterationUseSpread                   = IterationUseAllowsSyncIterablesFlag | IterationUseSpreadFlag
	IterationUseDestructuring            = IterationUseAllowsSyncIterablesFlag | IterationUseDestructuringFlag
	IterationUseForOf                    = IterationUseAllowsSyncIterablesFlag | IterationUseAllowsStringInputFlag | IterationUseForOfFlag
	IterationUseForAwaitOf               = IterationUseAllowsSyncIterablesFlag | IterationUseAllowsAsyncIterablesFlag | IterationUseAllowsStringInputFlag | IterationUseForOfFlag
	IterationUseYieldStar                = IterationUseAllowsSyncIterablesFlag | IterationUseYieldStarFlag
	IterationUseAsyncYieldStar           = IterationUseAllowsSyncIterablesFlag | IterationUseAllowsAsyncIterablesFlag | IterationUseYieldStarFlag
	IterationUseGeneratorReturnType      = IterationUseAllowsSyncIterablesFlag
	IterationUseAsyncGeneratorReturnType = IterationUseAllowsAsyncIterablesFlag
	IterationUseCacheFlags               = IterationUseAllowsSyncIterablesFlag | IterationUseAllowsAsyncIterablesFlag | IterationUseForOfFlag
)

type IterationTypes struct {
	yieldType  *Type
	returnType *Type
	nextType   *Type
}

type IterationTypeKind int32

const (
	IterationTypeKindYield IterationTypeKind = iota
	IterationTypeKindReturn
	IterationTypeKindNext
)

type IterationTypesResolver struct {
	iteratorSymbolName                   string
	getGlobalIteratorType                func() *Type
	getGlobalIterableType                func() *Type
	getGlobalIterableTypeChecked         func() *Type
	getGlobalIterableIteratorType        func() *Type
	getGlobalIterableIteratorTypeChecked func() *Type
	getGlobalIteratorObjectType          func() *Type
	getGlobalGeneratorType               func() *Type
	getGlobalBuiltinIteratorTypes        func() []*Type
	resolveIterationType                 func(t *Type, errorNode *ast.Node) *Type
	mustHaveANextMethodDiagnostic        *diagnostics.Message
	mustBeAMethodDiagnostic              *diagnostics.Message
	mustHaveAValueDiagnostic             *diagnostics.Message
}

type WideningContext struct {
	parent             *WideningContext // Parent context
	propertyName       string           // Name of property in parent
	siblings           []*Type          // Types of siblings
	resolvedProperties []*ast.Symbol    // Properties occurring in sibling object literals
	childContexts      map[string]*WideningContext
	widenedTypes       map[*Type]*Type
}

const maxSerializationLevel = 2

type Program interface {
	Host
	Options() *core.CompilerOptions
	SourceFiles() []*ast.SourceFile
	BindSourceFiles()
	FileExists(fileName string) bool
	GetSourceFile(fileName string) *ast.SourceFile
	GetSourceFileForResolvedModule(fileName string) *ast.SourceFile
	GetEmitModuleFormatOfFile(sourceFile ast.HasFileName) core.ModuleKind
	GetEmitSyntaxForUsageLocation(sourceFile ast.HasFileName, usageLocation *ast.StringLiteralLike) core.ResolutionMode
	GetImpliedNodeFormatForEmit(sourceFile ast.HasFileName) core.ModuleKind
	GetResolvedModule(currentSourceFile ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule
	GetResolvedModules() map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule]
	GetPackagesMap() map[string]bool
	GetSourceFileMetaData(path tspath.Path) ast.SourceFileMetaData
	GetJSXRuntimeImportSpecifier(path tspath.Path) (moduleReference string, specifier *ast.Node)
	GetImportHelpersImportSpecifier(path tspath.Path) *ast.Node
	SourceFileMayBeEmitted(sourceFile *ast.SourceFile, forceDtsEmit bool) bool
	IsSourceFileDefaultLibrary(path tspath.Path) bool
	GetProjectReferenceFromOutputDts(path tspath.Path) *tsoptions.SourceOutputAndProjectReference
	GetRedirectForResolution(file ast.HasFileName) *tsoptions.ParsedCommandLine
	CommonSourceDirectory() string
}

type Host interface {
	modulespecifiers.ModuleSpecifierGenerationHost
}

// Checker

var nextCheckerID atomic.Uint32

type Checker struct {
	id                                          uint32
	program                                     Program
	compilerOptions                             *core.CompilerOptions
	files                                       []*ast.SourceFile
	fileIndexMap                                map[*ast.SourceFile]int
	compareSymbols                              func(*ast.Symbol, *ast.Symbol) int
	compareSymbolChains                         func([]*ast.Symbol, []*ast.Symbol) int
	TypeCount                                   uint32
	SymbolCount                                 uint32
	SignatureCount                              uint32
	TotalInstantiationCount                     uint32
	instantiationCount                          uint32
	instantiationDepth                          uint32
	conditionalConstraintDepth                  uint32
	inlineLevel                                 int
	serializationLevel                          int
	currentNode                                 *ast.Node
	varianceTypeParameter                       *Type
	languageVersion                             core.ScriptTarget
	moduleKind                                  core.ModuleKind
	moduleResolutionKind                        core.ModuleResolutionKind
	isInferencePartiallyBlocked                 bool
	legacyDecorators                            bool
	emitStandardClassFields                     bool
	strictNullChecks                            bool
	strictFunctionTypes                         bool
	strictBindCallApply                         bool
	strictPropertyInitialization                bool
	strictBuiltinIteratorReturn                 bool
	noImplicitAny                               bool
	noImplicitThis                              bool
	useUnknownInCatchVariables                  bool
	exactOptionalPropertyTypes                  bool
	canCollectSymbolAliasAccessibilityData      bool
	wasCanceled                                 bool
	saveDeferredDiagnostics                     bool
	arrayVariances                              []VarianceFlags
	globals                                     ast.SymbolTable
	evaluate                                    evaluator.Evaluator
	stringLiteralTypes                          map[string]*Type
	numberLiteralTypes                          map[jsnum.Number]*Type
	nanType                                     *Type
	bigintLiteralTypes                          map[jsnum.PseudoBigInt]*Type
	enumLiteralTypes                            map[EnumLiteralKey]*Type
	enumNaNLiteralTypes                         map[*ast.Symbol]*Type
	indexedAccessTypes                          map[CacheHashKey]*Type
	templateLiteralTypes                        map[CacheHashKey]*Type
	stringMappingTypes                          map[StringMappingKey]*Type
	uniqueESSymbolTypes                         map[*ast.Symbol]*Type
	thisExpandoKinds                            map[*ast.Symbol]thisAssignmentDeclarationKind
	thisExpandoLocations                        map[*ast.Symbol]*ast.Node
	subtypeReductionCache                       map[CacheHashKey][]*Type
	cachedTypes                                 map[CachedTypeKey]*Type
	cachedSignatures                            map[CachedSignatureKey]*Signature
	undefinedProperties                         map[string]*ast.Symbol
	narrowedTypes                               map[NarrowedTypeKey]*Type
	assignmentReducedTypes                      map[AssignmentReducedKey]*Type
	discriminatedContextualTypes                map[DiscriminatedContextualTypeKey]*Type
	instantiationExpressionTypes                map[InstantiationExpressionKey]*Type
	substitutionTypes                           map[SubstitutionTypeKey]*Type
	reverseMappedCache                          map[ReverseMappedTypeKey]*Type
	reverseHomomorphicMappedCache               map[ReverseMappedTypeKey]*Type
	iterationTypesCache                         map[IterationTypesKey]IterationTypes
	markerTypes                                 collections.Set[*Type]
	undefinedSymbol                             *ast.Symbol
	argumentsSymbol                             *ast.Symbol
	requireSymbol                               *ast.Symbol
	unknownSymbol                               *ast.Symbol
	unresolvedSymbols                           map[string]*ast.Symbol
	errorTypes                                  map[CacheHashKey]*Type
	moduleSymbols                               map[*ast.Node]*ast.Symbol
	globalThisSymbol                            *ast.Symbol
	symbolTableAliasCache                       map[symbolTableID][]*ast.Symbol
	classExpressionNameTables                   map[ast.NodeId]ast.SymbolTable
	resolveName                                 func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol
	resolveNameForSymbolSuggestion              func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol
	tupleTypes                                  map[CacheHashKey]*Type
	unionTypes                                  map[CacheHashKey]*Type
	unionOfUnionTypes                           map[UnionOfUnionKey]*Type
	intersectionTypes                           map[CacheHashKey]*Type
	propertiesTypes                             map[PropertiesTypesKey]*Type
	diagnostics                                 ast.DiagnosticsCollection
	suggestionDiagnostics                       ast.DiagnosticsCollection
	symbolArena                                 core.Arena[ast.Symbol]
	signatureArena                              core.Arena[Signature]
	indexInfoArena                              core.Arena[IndexInfo]
	mergedSymbols                               map[*ast.Symbol]*ast.Symbol
	factory                                     ast.NodeFactory
	nodeLinks                                   core.LinkStore[*ast.Node, NodeLinks]
	signatureLinks                              core.LinkStore[*ast.Node, SignatureLinks]
	symbolNodeLinks                             core.LinkStore[*ast.Node, SymbolNodeLinks]
	typeNodeLinks                               core.LinkStore[*ast.Node, TypeNodeLinks]
	enumMemberLinks                             core.LinkStore[*ast.Node, EnumMemberLinks]
	assertionLinks                              core.LinkStore[*ast.Node, AssertionLinks]
	arrayLiteralLinks                           core.LinkStore[*ast.Node, ArrayLiteralLinks]
	switchStatementLinks                        core.LinkStore[*ast.Node, SwitchStatementLinks]
	jsxElementLinks                             core.LinkStore[*ast.Node, JsxElementLinks]
	symbolReferenceLinks                        core.LinkStore[*ast.Symbol, SymbolReferenceLinks]
	valueSymbolLinks                            core.LinkStore[*ast.Symbol, ValueSymbolLinks]
	mappedSymbolLinks                           core.LinkStore[*ast.Symbol, MappedSymbolLinks]
	deferredSymbolLinks                         core.LinkStore[*ast.Symbol, DeferredSymbolLinks]
	aliasSymbolLinks                            core.LinkStore[*ast.Symbol, AliasSymbolLinks]
	moduleSymbolLinks                           core.LinkStore[*ast.Symbol, ModuleSymbolLinks]
	lateBoundLinks                              core.LinkStore[*ast.Symbol, LateBoundLinks]
	exportTypeLinks                             core.LinkStore[*ast.Symbol, ExportTypeLinks]
	membersAndExportsLinks                      core.LinkStore[*ast.Symbol, MembersAndExportsLinks]
	typeAliasLinks                              core.LinkStore[*ast.Symbol, TypeAliasLinks]
	declaredTypeLinks                           core.LinkStore[*ast.Symbol, DeclaredTypeLinks]
	spreadLinks                                 core.LinkStore[*ast.Symbol, SpreadLinks]
	varianceLinks                               core.LinkStore[*ast.Symbol, VarianceLinks]
	ReverseMappedSymbolLinks                    core.LinkStore[*ast.Symbol, ReverseMappedSymbolLinks]
	markedAssignmentSymbolLinks                 core.LinkStore[*ast.Symbol, MarkedAssignmentSymbolLinks]
	symbolContainerLinks                        core.LinkStore[*ast.Symbol, ContainingSymbolLinks]
	sourceFileLinks                             core.LinkStore[*ast.SourceFile, SourceFileLinks]
	regExpScanner                               *scanner.Scanner
	patternForType                              map[*Type]*ast.Node
	contextFreeTypes                            map[*ast.Node]*Type
	anyType                                     *Type
	autoType                                    *Type
	wildcardType                                *Type
	blockedStringType                           *Type
	errorType                                   *Type
	unresolvedType                              *Type
	nonInferrableAnyType                        *Type
	intrinsicMarkerType                         *Type
	unknownType                                 *Type
	undefinedType                               *Type
	undefinedWideningType                       *Type
	missingType                                 *Type
	undefinedOrMissingType                      *Type
	optionalType                                *Type
	nullType                                    *Type
	nullWideningType                            *Type
	stringType                                  *Type
	numberType                                  *Type
	bigintType                                  *Type
	regularFalseType                            *Type
	falseType                                   *Type
	regularTrueType                             *Type
	trueType                                    *Type
	booleanType                                 *Type
	esSymbolType                                *Type
	voidType                                    *Type
	neverType                                   *Type
	silentNeverType                             *Type
	implicitNeverType                           *Type
	unreachableNeverType                        *Type
	nonPrimitiveType                            *Type
	stringOrNumberType                          *Type
	stringNumberSymbolType                      *Type
	numberOrBigIntType                          *Type
	templateConstraintType                      *Type
	numericStringType                           *Type
	uniqueLiteralType                           *Type
	uniqueLiteralMapper                         *TypeMapper
	reliabilityFlags                            RelationComparisonResult
	reportUnreliableMapper                      *TypeMapper
	reportUnmeasurableMapper                    *TypeMapper
	restrictiveMapper                           *TypeMapper
	permissiveMapper                            *TypeMapper
	emptyObjectType                             *Type
	emptyJsxObjectType                          *Type
	emptyFreshJsxObjectType                     *Type
	emptyTypeLiteralType                        *Type
	unknownEmptyObjectType                      *Type
	unknownUnionType                            *Type
	emptyGenericType                            *Type
	anyFunctionType                             *Type
	noConstraintType                            *Type
	circularConstraintType                      *Type
	resolvingDefaultType                        *Type
	markerSuperType                             *Type
	markerSubType                               *Type
	markerOtherType                             *Type
	markerSuperTypeForCheck                     *Type
	markerSubTypeForCheck                       *Type
	noTypePredicate                             *TypePredicate
	anySignature                                *Signature
	unknownSignature                            *Signature
	resolvingSignature                          *Signature
	silentNeverSignature                        *Signature
	cachedArgumentsReferenced                   map[*ast.Node]bool
	enumNumberIndexInfo                         *IndexInfo
	anyBaseTypeIndexInfo                        *IndexInfo
	patternAmbientModules                       []*ast.PatternAmbientModule
	patternAmbientModuleAugmentations           ast.SymbolTable
	globalObjectType                            *Type
	globalFunctionType                          *Type
	globalCallableFunctionType                  *Type
	globalNewableFunctionType                   *Type
	globalArrayType                             *Type
	globalReadonlyArrayType                     *Type
	globalStringType                            *Type
	globalNumberType                            *Type
	globalBooleanType                           *Type
	globalRegExpType                            *Type
	globalThisType                              *Type
	anyArrayType                                *Type
	autoArrayType                               *Type
	anyReadonlyArrayType                        *Type
	deferredGlobalImportMetaExpressionType      *Type
	contextualBindingPatterns                   []*ast.Node
	emptyStringType                             *Type
	zeroType                                    *Type
	zeroBigIntType                              *Type
	typeofType                                  *Type
	typeResolutions                             []TypeResolution
	resolutionStart                             int
	inVarianceComputation                       bool
	apparentArgumentCount                       *int
	lastGetCombinedNodeFlagsNode                *ast.Node
	lastGetCombinedNodeFlagsResult              ast.NodeFlags
	lastGetCombinedModifierFlagsNode            *ast.Node
	lastGetCombinedModifierFlagsResult          ast.ModifierFlags
	freeinferenceState                          *InferenceState
	freeFlowState                               *FlowState
	flowLoopCache                               map[FlowLoopKey]*Type
	flowLoopStack                               []FlowLoopInfo
	sharedFlows                                 []SharedFlow
	antecedentTypes                             []*Type
	flowAnalysisDisabled                        bool
	flowInvocationCount                         int
	flowTypeCache                               map[*ast.Node]*Type
	lastFlowNode                                *ast.FlowNode
	lastFlowNodeReachable                       bool
	flowNodeReachable                           map[*ast.FlowNode]bool
	flowNodePostSuper                           map[*ast.FlowNode]bool
	renamedBindingElementsInTypes               []*ast.Node
	contextualInfos                             []ContextualInfo
	inferenceContextInfos                       []InferenceContextInfo
	awaitedTypeStack                            []*Type
	reverseMappedSourceStack                    []*Type
	reverseMappedTargetStack                    []*Type
	reverseExpandingFlags                       ExpandingFlags
	freeRelater                                 *Relater
	subtypeRelation                             *Relation
	strictSubtypeRelation                       *Relation
	assignableRelation                          *Relation
	comparableRelation                          *Relation
	identityRelation                            *Relation
	enumRelation                                map[EnumRelationKey]RelationComparisonResult
	getGlobalESSymbolType                       func() *Type
	getGlobalBigIntType                         func() *Type
	getGlobalImportMetaType                     func() *Type
	getGlobalImportAttributesType               func() *Type
	getGlobalImportAttributesTypeChecked        func() *Type
	getGlobalNonNullableTypeAliasOrNil          func() *ast.Symbol
	getGlobalExtractSymbol                      func() *ast.Symbol
	getGlobalDisposableType                     func() *Type
	getGlobalAsyncDisposableType                func() *Type
	getGlobalAwaitedSymbol                      func() *ast.Symbol
	getGlobalAwaitedSymbolOrNil                 func() *ast.Symbol
	getGlobalNaNSymbolOrNil                     func() *ast.Symbol
	getGlobalRecordSymbol                       func() *ast.Symbol
	getGlobalTemplateStringsArrayType           func() *Type
	getGlobalESSymbolConstructorSymbolOrNil     func() *ast.Symbol
	getGlobalESSymbolConstructorTypeSymbolOrNil func() *ast.Symbol
	getGlobalImportCallOptionsType              func() *Type
	getGlobalImportCallOptionsTypeChecked       func() *Type
	getGlobalPromiseType                        func() *Type
	getGlobalPromiseTypeChecked                 func() *Type
	getGlobalPromiseLikeType                    func() *Type
	getGlobalPromiseConstructorSymbol           func() *ast.Symbol
	getGlobalPromiseConstructorSymbolOrNil      func() *ast.Symbol
	getGlobalOmitSymbol                         func() *ast.Symbol
	getGlobalNoInferSymbolOrNil                 func() *ast.Symbol
	getGlobalIteratorType                       func() *Type
	getGlobalIterableType                       func() *Type
	getGlobalIterableTypeChecked                func() *Type
	getGlobalIterableIteratorType               func() *Type
	getGlobalIterableIteratorTypeChecked        func() *Type
	getGlobalIteratorObjectType                 func() *Type
	getGlobalGeneratorType                      func() *Type
	getGlobalAsyncIteratorType                  func() *Type
	getGlobalAsyncIterableType                  func() *Type
	getGlobalAsyncIterableTypeChecked           func() *Type
	getGlobalAsyncIterableIteratorType          func() *Type
	getGlobalAsyncIterableIteratorTypeChecked   func() *Type
	getGlobalAsyncIteratorObjectType            func() *Type
	getGlobalAsyncGeneratorType                 func() *Type
	getGlobalIteratorYieldResultType            func() *Type
	getGlobalIteratorReturnResultType           func() *Type
	getGlobalTypedPropertyDescriptorType        func() *Type
	getGlobalClassDecoratorContextType          func() *Type
	getGlobalClassMethodDecoratorContextType    func() *Type
	getGlobalClassGetterDecoratorContextType    func() *Type
	getGlobalClassSetterDecoratorContextType    func() *Type
	getGlobalClassAccessorDecoratorContxtType   func() *Type
	getGlobalClassAccessorDecoratorContextType  func() *Type
	getGlobalClassAccessorDecoratorTargetType   func() *Type
	getGlobalClassAccessorDecoratorResultType   func() *Type
	getGlobalClassFieldDecoratorContextType     func() *Type
	syncIterationTypesResolver                  *IterationTypesResolver
	asyncIterationTypesResolver                 *IterationTypesResolver
	isPrimitiveOrObjectOrEmptyType              func(*Type) bool
	containsMissingType                         func(*Type) bool
	couldContainTypeVariables                   func(*Type) bool
	isStringIndexSignatureOnlyType              func(*Type) bool
	markNodeAssignments                         func(*ast.Node) bool
	compareTypesAssignable                      TypeComparer
	emitResolver                                *EmitResolver
	emitResolverOnce                            sync.Once
	_jsxNamespace                               string
	_jsxFactoryEntity                           *ast.Node
	skipDirectInferenceNodes                    collections.Set[*ast.Node]
	ctx                                         context.Context
	packagesMap                                 map[string]bool
	activeMappers                               []*TypeMapper
	activeTypeMappersCaches                     []map[CacheHashKey]*Type
	ambientModulesOnce                          sync.Once
	ambientModules                              []*ast.Symbol
	withinUnreachableCode                       bool
	reportedUnreachableNodes                    collections.Set[*ast.Node]
	nonExistentProperties                       collections.Set[NonExistentPropertyKey]
	deferredDiagnosticCallbacks                 []func()
	typeToStringNodebuilder                     *NodeBuilder

	mu     sync.Mutex
	tracer *Tracer // Optional tracer for trace events and type recording (for --generateTrace)
}

func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) {
	program.BindSourceFiles()

	c := &Checker{}
	c.id = nextCheckerID.Add(1)
	c.tracer = tracer
	c.program = program
	c.compilerOptions = program.Options()
	c.files = program.SourceFiles()
	c.fileIndexMap = createFileIndexMap(c.files)
	c.compareSymbols = c.compareSymbolsWorker           // Closure optimization
	c.compareSymbolChains = c.compareSymbolChainsWorker // Closure optimization
	c.languageVersion = c.compilerOptions.GetEmitScriptTarget()
	c.moduleKind = c.compilerOptions.GetEmitModuleKind()
	c.moduleResolutionKind = c.compilerOptions.GetModuleResolutionKind()
	c.legacyDecorators = c.compilerOptions.ExperimentalDecorators == core.TSTrue
	c.emitStandardClassFields = c.compilerOptions.GetEmitStandardClassFields()
	c.strictNullChecks = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.StrictNullChecks)
	c.strictFunctionTypes = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.StrictFunctionTypes)
	c.strictBindCallApply = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.StrictBindCallApply)
	c.strictPropertyInitialization = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.StrictPropertyInitialization)
	c.strictBuiltinIteratorReturn = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.StrictBuiltinIteratorReturn)
	c.noImplicitAny = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.NoImplicitAny)
	c.noImplicitThis = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.NoImplicitThis)
	c.useUnknownInCatchVariables = c.compilerOptions.GetStrictOptionValue(c.compilerOptions.UseUnknownInCatchVariables)
	c.exactOptionalPropertyTypes = c.compilerOptions.ExactOptionalPropertyTypes == core.TSTrue
	c.canCollectSymbolAliasAccessibilityData = c.compilerOptions.VerbatimModuleSyntax.IsFalseOrUnknown()
	c.arrayVariances = []VarianceFlags{VarianceFlagsCovariant}
	c.globals = make(ast.SymbolTable, countGlobalSymbols(c.files))
	c.evaluate = evaluator.NewEvaluator(c.evaluateEntity, ast.OEKParentheses)
	c.stringLiteralTypes = make(map[string]*Type)
	c.numberLiteralTypes = make(map[jsnum.Number]*Type)
	c.bigintLiteralTypes = make(map[jsnum.PseudoBigInt]*Type)
	c.enumLiteralTypes = make(map[EnumLiteralKey]*Type)
	c.enumNaNLiteralTypes = make(map[*ast.Symbol]*Type)
	c.indexedAccessTypes = make(map[CacheHashKey]*Type)
	c.templateLiteralTypes = make(map[CacheHashKey]*Type)
	c.stringMappingTypes = make(map[StringMappingKey]*Type)
	c.uniqueESSymbolTypes = make(map[*ast.Symbol]*Type)
	c.thisExpandoKinds = make(map[*ast.Symbol]thisAssignmentDeclarationKind)
	c.thisExpandoLocations = make(map[*ast.Symbol]*ast.Node)
	c.subtypeReductionCache = make(map[CacheHashKey][]*Type)
	c.cachedTypes = make(map[CachedTypeKey]*Type)
	c.cachedSignatures = make(map[CachedSignatureKey]*Signature)
	c.undefinedProperties = make(map[string]*ast.Symbol)
	c.narrowedTypes = make(map[NarrowedTypeKey]*Type)
	c.assignmentReducedTypes = make(map[AssignmentReducedKey]*Type)
	c.discriminatedContextualTypes = make(map[DiscriminatedContextualTypeKey]*Type)
	c.instantiationExpressionTypes = make(map[InstantiationExpressionKey]*Type)
	c.substitutionTypes = make(map[SubstitutionTypeKey]*Type)
	c.reverseMappedCache = make(map[ReverseMappedTypeKey]*Type)
	c.reverseHomomorphicMappedCache = make(map[ReverseMappedTypeKey]*Type)
	c.iterationTypesCache = make(map[IterationTypesKey]IterationTypes)
	c.undefinedSymbol = c.newSymbol(ast.SymbolFlagsProperty, "undefined")
	c.argumentsSymbol = c.newSymbol(ast.SymbolFlagsProperty, "arguments")
	c.requireSymbol = c.newSymbol(ast.SymbolFlagsProperty, "require")
	c.unknownSymbol = c.newSymbol(ast.SymbolFlagsProperty, "unknown")
	c.unresolvedSymbols = make(map[string]*ast.Symbol)
	c.errorTypes = make(map[CacheHashKey]*Type)
	c.moduleSymbols = make(map[*ast.Node]*ast.Symbol)
	c.globalThisSymbol = c.newSymbolEx(ast.SymbolFlagsModule, "globalThis", ast.CheckFlagsReadonly)
	c.globalThisSymbol.Exports = c.globals
	c.globals[c.globalThisSymbol.Name] = c.globalThisSymbol
	c.resolveName = c.createNameResolver().Resolve
	c.resolveNameForSymbolSuggestion = c.createNameResolverForSuggestion().Resolve
	c.tupleTypes = make(map[CacheHashKey]*Type)
	c.unionTypes = make(map[CacheHashKey]*Type)
	c.unionOfUnionTypes = make(map[UnionOfUnionKey]*Type)
	c.intersectionTypes = make(map[CacheHashKey]*Type)
	c.propertiesTypes = make(map[PropertiesTypesKey]*Type)
	c.mergedSymbols = make(map[*ast.Symbol]*ast.Symbol)
	c.patternForType = make(map[*Type]*ast.Node)
	c.contextFreeTypes = make(map[*ast.Node]*Type)
	c.anyType = c.newIntrinsicType(TypeFlagsAny, "any")
	c.autoType = c.newIntrinsicTypeEx(TypeFlagsAny, "any", ObjectFlagsNonInferrableType)
	c.wildcardType = c.newIntrinsicType(TypeFlagsAny, "any")
	c.blockedStringType = c.newIntrinsicType(TypeFlagsAny, "any")
	c.errorType = c.newIntrinsicType(TypeFlagsAny, "error")
	c.unresolvedType = c.newIntrinsicType(TypeFlagsAny, "unresolved")
	c.nonInferrableAnyType = c.newIntrinsicTypeEx(TypeFlagsAny, "any", ObjectFlagsContainsWideningType)
	c.intrinsicMarkerType = c.newIntrinsicType(TypeFlagsAny, "intrinsic")
	c.unknownType = c.newIntrinsicType(TypeFlagsUnknown, "unknown")
	c.undefinedType = c.newIntrinsicType(TypeFlagsUndefined, "undefined")
	c.undefinedWideningType = c.createWideningType(c.undefinedType)
	c.missingType = c.newIntrinsicType(TypeFlagsUndefined, "undefined")
	c.undefinedOrMissingType = core.IfElse(c.exactOptionalPropertyTypes, c.missingType, c.undefinedType)
	c.optionalType = c.newIntrinsicType(TypeFlagsUndefined, "undefined")
	c.nullType = c.newIntrinsicType(TypeFlagsNull, "null")
	c.nullWideningType = c.createWideningType(c.nullType)
	c.stringType = c.newIntrinsicType(TypeFlagsString, "string")
	c.numberType = c.newIntrinsicType(TypeFlagsNumber, "number")
	c.bigintType = c.newIntrinsicType(TypeFlagsBigInt, "bigint")
	c.regularFalseType = c.newLiteralType(TypeFlagsBooleanLiteral, false, nil)
	c.falseType = c.newLiteralType(TypeFlagsBooleanLiteral, false, c.regularFalseType)
	c.regularFalseType.AsLiteralType().freshType = c.falseType
	c.falseType.AsLiteralType().freshType = c.falseType
	c.regularTrueType = c.newLiteralType(TypeFlagsBooleanLiteral, true, nil)
	c.trueType = c.newLiteralType(TypeFlagsBooleanLiteral, true, c.regularTrueType)
	c.regularTrueType.AsLiteralType().freshType = c.trueType
	c.trueType.AsLiteralType().freshType = c.trueType
	c.booleanType = c.getUnionType([]*Type{c.regularFalseType, c.regularTrueType})
	c.esSymbolType = c.newIntrinsicType(TypeFlagsESSymbol, "symbol")
	c.voidType = c.newIntrinsicType(TypeFlagsVoid, "void")
	c.neverType = c.newIntrinsicType(TypeFlagsNever, "never")
	c.silentNeverType = c.newIntrinsicTypeEx(TypeFlagsNever, "never", ObjectFlagsNonInferrableType)
	c.implicitNeverType = c.newIntrinsicType(TypeFlagsNever, "never")
	c.unreachableNeverType = c.newIntrinsicType(TypeFlagsNever, "never")
	c.nonPrimitiveType = c.newIntrinsicType(TypeFlagsNonPrimitive, "object")
	c.stringOrNumberType = c.getUnionType([]*Type{c.stringType, c.numberType})
	c.stringNumberSymbolType = c.getUnionType([]*Type{c.stringType, c.numberType, c.esSymbolType})
	c.numberOrBigIntType = c.getUnionType([]*Type{c.numberType, c.bigintType})
	c.numericStringType = c.getTemplateLiteralType([]string{"", ""}, []*Type{c.numberType}) // The `${number}` type
	c.templateConstraintType = c.getUnionType([]*Type{c.stringType, c.numberType, c.booleanType, c.bigintType, c.nullType, c.undefinedType})
	c.uniqueLiteralType = c.newIntrinsicType(TypeFlagsNever, "never") // Special `never` flagged by union reduction to behave as a literal
	c.uniqueLiteralMapper = newFunctionTypeMapper(c.getUniqueLiteralTypeForTypeParameter)
	c.reportUnreliableMapper = newFunctionTypeMapper(c.reportUnreliableWorker)
	c.reportUnmeasurableMapper = newFunctionTypeMapper(c.reportUnmeasurableWorker)
	c.restrictiveMapper = newFunctionTypeMapper(c.restrictiveMapperWorker)
	c.permissiveMapper = newFunctionTypeMapper(c.permissiveMapperWorker)
	c.emptyObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil)
	c.emptyJsxObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil)
	c.emptyFreshJsxObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil)
	c.emptyTypeLiteralType = c.newAnonymousType(c.newSymbol(ast.SymbolFlagsTypeLiteral, ast.InternalSymbolNameType), nil, nil, nil, nil)
	c.unknownEmptyObjectType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil)
	c.unknownUnionType = c.createUnknownUnionType()
	c.emptyGenericType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil)
	c.emptyGenericType.AsObjectType().instantiations = make(map[CacheHashKey]*Type)
	c.anyFunctionType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil)
	c.anyFunctionType.objectFlags |= ObjectFlagsNonInferrableType
	c.noConstraintType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil)
	c.circularConstraintType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil)
	c.resolvingDefaultType = c.newAnonymousType(nil /*symbol*/, nil, nil, nil, nil)
	c.markerSuperType = c.newTypeParameter(nil)
	c.markerSubType = c.newTypeParameter(nil)
	c.markerSubType.AsTypeParameter().constraint = c.markerSuperType
	c.markerOtherType = c.newTypeParameter(nil)
	c.markerSuperTypeForCheck = c.newTypeParameter(nil)
	c.markerSubTypeForCheck = c.newTypeParameter(nil)
	c.markerSubTypeForCheck.AsTypeParameter().constraint = c.markerSuperTypeForCheck
	c.noTypePredicate = &TypePredicate{kind: TypePredicateKindIdentifier, parameterIndex: 0, parameterName: "<<unresolved>>", t: c.anyType}
	c.anySignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.anyType, nil, 0)
	c.unknownSignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.errorType, nil, 0)
	c.resolvingSignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.anyType, nil, 0)
	c.silentNeverSignature = c.newSignature(SignatureFlagsNone, nil, nil, nil, nil, c.silentNeverType, nil, 0)
	c.cachedArgumentsReferenced = make(map[*ast.Node]bool)
	c.enumNumberIndexInfo = &IndexInfo{keyType: c.numberType, valueType: c.stringType, isReadonly: true}
	c.anyBaseTypeIndexInfo = &IndexInfo{keyType: c.stringType, valueType: c.anyType, isReadonly: false}
	c.emptyStringType = c.getStringLiteralType("")
	c.zeroType = c.getNumberLiteralType(0)
	c.zeroBigIntType = c.getBigIntLiteralType(jsnum.PseudoBigInt{})
	c.typeofType = c.getUnionType(core.Map(slices.Sorted(maps.Keys(typeofNEFacts)), c.getStringLiteralType))
	c.flowLoopCache = make(map[FlowLoopKey]*Type)
	c.flowNodeReachable = make(map[*ast.FlowNode]bool)
	c.flowNodePostSuper = make(map[*ast.FlowNode]bool)
	c.subtypeRelation = &Relation{}
	c.strictSubtypeRelation = &Relation{}
	c.assignableRelation = &Relation{}
	c.comparableRelation = &Relation{}
	c.identityRelation = &Relation{}
	c.enumRelation = make(map[EnumRelationKey]RelationComparisonResult)
	c.getGlobalESSymbolType = c.getGlobalTypeResolver("Symbol", 0 /*arity*/, false /*reportErrors*/)
	c.getGlobalBigIntType = c.getGlobalTypeResolver("BigInt", 0 /*arity*/, false /*reportErrors*/)
	c.getGlobalImportMetaType = c.getGlobalTypeResolver("ImportMeta", 0 /*arity*/, true /*reportErrors*/)
	c.getGlobalImportAttributesType = c.getGlobalTypeResolver("ImportAttributes", 0 /*arity*/, false /*reportErrors*/)
	c.getGlobalImportAttributesTypeChecked = c.getGlobalTypeResolver("ImportAttributes", 0 /*arity*/, true /*reportErrors*/)
	c.getGlobalNonNullableTypeAliasOrNil = c.getGlobalTypeAliasResolver("NonNullable", 1 /*arity*/, false /*reportErrors*/)
	c.getGlobalExtractSymbol = c.getGlobalTypeAliasResolver("Extract", 2 /*arity*/, true /*reportErrors*/)
	c.getGlobalDisposableType = c.getGlobalTypeResolver("Disposable", 0 /*arity*/, true /*reportErrors*/)
	c.getGlobalAsyncDisposableType = c.getGlobalTypeResolver("AsyncDisposable", 0 /*arity*/, true /*reportErrors*/)
	c.getGlobalAwaitedSymbol = c.getGlobalTypeAliasResolver("Awaited", 1 /*arity*/, true /*reportErrors*/)
	c.getGlobalAwaitedSymbolOrNil = c.getGlobalTypeAliasResolver("Awaited", 1 /*arity*/, false /*reportErrors*/)
	c.getGlobalNaNSymbolOrNil = c.getGlobalValueSymbolResolver("NaN", false /*reportErrors*/)
	c.getGlobalRecordSymbol = c.getGlobalTypeAliasResolver("Record", 2 /*arity*/, true /*reportErrors*/)
	c.getGlobalTemplateStringsArrayType = c.getGlobalTypeResolver("TemplateStringsArray", 0 /*arity*/, true /*reportErrors*/)
	c.getGlobalESSymbolConstructorSymbolOrNil = c.getGlobalValueSymbolResolver("Symbol", false /*reportErrors*/)
	c.getGlobalESSymbolConstructorTypeSymbolOrNil = c.getGlobalTypeSymbolResolver("SymbolConstructor", false /*reportErrors*/)
	c.getGlobalImportCallOptionsType = c.getGlobalTypeResolver("ImportCallOptions", 0 /*arity*/, false /*reportErrors*/)
	c.getGlobalImportCallOptionsTypeChecked = c.getGlobalTypeResolver("ImportCallOptions", 0 /*arity*/, true /*reportErrors*/)
	c.getGlobalPromiseType = c.getGlobalTypeResolver("Promise", 1 /*arity*/, false /*reportErrors*/)
	c.getGlobalPromiseTypeChecked = c.getGlobalTypeResolver("Promise", 1 /*arity*/, true /*reportErrors*/)
	c.getGlobalPromiseLikeType = c.getGlobalTypeResolver("PromiseLike", 1 /*arity*/, true /*reportErrors*/)
	c.getGlobalPromiseConstructorSymbol = c.getGlobalValueSymbolResolver("Promise", true /*reportErrors*/)
	c.getGlobalPromiseConstructorSymbolOrNil = c.getGlobalValueSymbolResolver("Promise", false /*reportErrors*/)
	c.getGlobalOmitSymbol = c.getGlobalTypeAliasResolver("Omit", 2 /*arity*/, true /*reportErrors*/)
	c.getGlobalNoInferSymbolOrNil = c.getGlobalTypeAliasResolver("NoInfer", 1 /*arity*/, false /*reportErrors*/)
	c.getGlobalIteratorType = c.getGlobalTypeResolver("Iterator", 3 /*arity*/, false /*reportErrors*/)
	c.getGlobalIterableType = c.getGlobalTypeResolver("Iterable", 3 /*arity*/, false /*reportErrors*/)
	c.getGlobalIterableTypeChecked = c.getGlobalTypeResolver("Iterable", 3 /*arity*/, true /*reportErrors*/)
	c.getGlobalIterableIteratorType = c.getGlobalTypeResolver("IterableIterator", 3 /*arity*/, false /*reportErrors*/)
	c.getGlobalIterableIteratorTypeChecked = c.getGlobalTypeResolver("IterableIterator", 3 /*arity*/, true /*reportErrors*/)
	c.getGlobalIteratorObjectType = c.getGlobalTypeResolver("IteratorObject", 3 /*arity*/, false /*reportErrors*/)
	c.getGlobalGeneratorType = c.getGlobalTypeResolver("Generator", 3 /*arity*/, false /*reportErrors*/)
	c.getGlobalAsyncIteratorType = c.getGlobalTypeResolver("AsyncIterator", 3 /*arity*/, false /*reportErrors*/)
	c.getGlobalAsyncIterableType = c.getGlobalTypeResolver("AsyncIterable", 3 /*arity*/, false /*reportErrors*/)
	c.getGlobalAsyncIterableTypeChecked = c.getGlobalTypeResolver("AsyncIterable", 3 /*arity*/, true /*reportErrors*/)
	c.getGlobalAsyncIterableIteratorType = c.getGlobalTypeResolver("AsyncIterableIterator", 3 /*arity*/, false /*reportErrors*/)
	c.getGlobalAsyncIterableIteratorTypeChecked = c.getGlobalTypeResolver("AsyncIterableIterator", 3 /*arity*/, true /*reportErrors*/)
	c.getGlobalAsyncIteratorObjectType = c.getGlobalTypeResolver("AsyncIteratorObject", 3 /*arity*/, false /*reportErrors*/)
	c.getGlobalAsyncGeneratorType = c.getGlobalTypeResolver("AsyncGenerator", 3 /*arity*/, false /*reportErrors*/)
	c.getGlobalIteratorYieldResultType = c.getGlobalTypeResolver("IteratorYieldResult", 1 /*arity*/, false /*reportErrors*/)
	c.getGlobalIteratorReturnResultType = c.getGlobalTypeResolver("IteratorReturnResult", 1 /*arity*/, false /*reportErrors*/)
	c.getGlobalTypedPropertyDescriptorType = c.getGlobalTypeResolver("TypedPropertyDescriptor", 1 /*arity*/, true /*reportErrors*/)
	c.getGlobalClassDecoratorContextType = c.getGlobalTypeResolver("ClassDecoratorContext", 1 /*arity*/, true /*reportErrors*/)
	c.getGlobalClassMethodDecoratorContextType = c.getGlobalTypeResolver("ClassMethodDecoratorContext", 2 /*arity*/, true /*reportErrors*/)
	c.getGlobalClassGetterDecoratorContextType = c.getGlobalTypeResolver("ClassGetterDecoratorContext", 2 /*arity*/, true /*reportErrors*/)
	c.getGlobalClassSetterDecoratorContextType = c.getGlobalTypeResolver("ClassSetterDecoratorContext", 2 /*arity*/, true /*reportErrors*/)
	c.getGlobalClassAccessorDecoratorContextType = c.getGlobalTypeResolver("ClassAccessorDecoratorContext", 2 /*arity*/, true /*reportErrors*/)
	c.getGlobalClassAccessorDecoratorTargetType = c.getGlobalTypeResolver("ClassAccessorDecoratorTarget", 2 /*arity*/, true /*reportErrors*/)
	c.getGlobalClassAccessorDecoratorResultType = c.getGlobalTypeResolver("ClassAccessorDecoratorResult", 2 /*arity*/, true /*reportErrors*/)
	c.getGlobalClassFieldDecoratorContextType = c.getGlobalTypeResolver("ClassFieldDecoratorContext", 2 /*arity*/, true /*reportErrors*/)
	c.initializeClosures()
	c.initializeIterationResolvers()
	c.initializeChecker()
	return c, &c.mu
}

func createFileIndexMap(files []*ast.SourceFile) map[*ast.SourceFile]int {
	result := make(map[*ast.SourceFile]int, len(files))
	for i, file := range files {
		result[file] = i
	}
	return result
}

func countGlobalSymbols(files []*ast.SourceFile) int {
	count := 0
	for _, file := range files {
		if !ast.IsExternalOrCommonJSModule(file) {
			count += len(file.Locals)
		}
	}
	return count
}

func (c *Checker) reportUnreliableWorker(t *Type) *Type {
	if t == c.markerSuperType || t == c.markerSubType || t == c.markerOtherType {
		c.reliabilityFlags |= RelationComparisonResultReportsUnreliable
	}
	return t
}

func (c *Checker) reportUnmeasurableWorker(t *Type) *Type {
	if t == c.markerSuperType || t == c.markerSubType || t == c.markerOtherType {
		c.reliabilityFlags |= RelationComparisonResultReportsUnmeasurable
	}
	return t
}

// Resolve to the global class or interface by the given name and arity, or emptyObjectType/emptyGenericType otherwise
func (c *Checker) getGlobalTypeResolver(name string, arity int, reportErrors bool) func() *Type {
	return core.Memoize(func() *Type {
		return c.getGlobalType(name, arity, reportErrors)
	})
}

// Resolve to the global type alias symbol by the given name and arity, or nil otherwise
func (c *Checker) getGlobalTypeAliasResolver(name string, arity int, reportErrors bool) func() *ast.Symbol {
	return core.Memoize(func() *ast.Symbol {
		return c.getGlobalTypeAliasSymbol(name, arity, reportErrors)
	})
}

// Resolve to the global value symbol by the given name, or nil otherwise
func (c *Checker) getGlobalValueSymbolResolver(name string, reportErrors bool) func() *ast.Symbol {
	return core.Memoize(func() *ast.Symbol {
		return c.getGlobalSymbol(name, ast.SymbolFlagsValue, core.IfElse(reportErrors, diagnostics.Cannot_find_global_value_0, nil))
	})
}

func (c *Checker) getGlobalTypeSymbolResolver(name string, reportErrors bool) func() *ast.Symbol {
	return core.Memoize(func() *ast.Symbol {
		return c.getGlobalSymbol(name, ast.SymbolFlagsType, core.IfElse(reportErrors, diagnostics.Cannot_find_global_type_0, nil))
	})
}

func (c *Checker) getGlobalTypesResolver(names []string, arity int, reportErrors bool) func() []*Type {
	return core.Memoize(func() []*Type {
		return core.Map(names, func(name string) *Type {
			return c.getGlobalType(name, arity, reportErrors)
		})
	})
}

func (c *Checker) getGlobalTypeAliasSymbol(name string, arity int, reportErrors bool) *ast.Symbol {
	symbol := c.getGlobalSymbol(name, ast.SymbolFlagsTypeAlias, core.IfElse(reportErrors, diagnostics.Cannot_find_global_type_0, nil))
	if symbol == nil {
		return nil
	}
	// Resolve the declared type of the symbol. This resolves type parameters for the type alias so that we can check arity.
	c.getDeclaredTypeOfSymbol(symbol)
	if len(c.typeAliasLinks.Get(symbol).typeParameters) != arity {
		if reportErrors {
			decl := core.Find(symbol.Declarations, ast.IsTypeAliasDeclaration)
			c.error(decl, diagnostics.Global_type_0_must_have_1_type_parameter_s, ast.SymbolName(symbol), arity)
		}
		return nil
	}
	return symbol
}

func (c *Checker) GetTypeAliasTypeParameters(symbol *ast.Symbol) []*Type {
	if symbol.Flags&ast.SymbolFlagsTypeAlias == 0 {
		panic("Attempted to fetch type alias parameters for non-type-alias symbol")
	}
	c.getDeclaredTypeOfSymbol(symbol)
	return c.typeAliasLinks.Get(symbol).typeParameters
}

func (c *Checker) getGlobalType(name string, arity int, reportErrors bool) *Type {
	symbol := c.getGlobalSymbol(name, ast.SymbolFlagsType, core.IfElse(reportErrors, diagnostics.Cannot_find_global_type_0, nil))
	if symbol != nil {
		if symbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0 {
			t := c.getDeclaredTypeOfSymbol(symbol)
			if len(t.AsInterfaceType().TypeParameters()) == arity {
				return t
			}
			if reportErrors {
				c.error(getGlobalTypeDeclaration(symbol), diagnostics.Global_type_0_must_have_1_type_parameter_s, ast.SymbolName(symbol), arity)
			}
		} else if reportErrors {
			c.error(getGlobalTypeDeclaration(symbol), diagnostics.Global_type_0_must_be_a_class_or_interface_type, ast.SymbolName(symbol))
		}
	}
	if arity != 0 {
		return c.emptyGenericType
	}
	return c.emptyObjectType
}

func getGlobalTypeDeclaration(symbol *ast.Symbol) *ast.Declaration {
	for _, declaration := range symbol.Declarations {
		switch declaration.Kind {
		case ast.KindClassDeclaration, ast.KindInterfaceDeclaration, ast.KindEnumDeclaration, ast.KindTypeAliasDeclaration:
			return declaration
		}
	}
	return nil
}

func (c *Checker) getGlobalSymbol(name string, meaning ast.SymbolFlags, diagnostic *diagnostics.Message) *ast.Symbol {
	// Don't track references for global symbols anyway, so value if `isReference` is arbitrary
	return c.resolveName(nil, name, meaning, diagnostic, false /*isUse*/, false /*excludeGlobals*/)
}

func (c *Checker) initializeClosures() {
	c.isPrimitiveOrObjectOrEmptyType = func(t *Type) bool {
		return t.flags&(TypeFlagsPrimitive|TypeFlagsNonPrimitive) != 0 || c.IsEmptyAnonymousObjectType(t)
	}
	c.containsMissingType = func(t *Type) bool {
		return t == c.missingType || t.flags&TypeFlagsUnion != 0 && t.Types()[0] == c.missingType
	}
	c.couldContainTypeVariables = c.couldContainTypeVariablesWorker
	c.isStringIndexSignatureOnlyType = c.isStringIndexSignatureOnlyTypeWorker
	c.markNodeAssignments = c.markNodeAssignmentsWorker
	c.compareTypesAssignable = c.compareTypesAssignableWorker
}

func (c *Checker) initializeIterationResolvers() {
	c.syncIterationTypesResolver = &IterationTypesResolver{
		iteratorSymbolName:                   "iterator",
		getGlobalIteratorType:                c.getGlobalIteratorType,
		getGlobalIterableType:                c.getGlobalIterableType,
		getGlobalIterableTypeChecked:         c.getGlobalIterableTypeChecked,
		getGlobalIterableIteratorType:        c.getGlobalIterableIteratorType,
		getGlobalIterableIteratorTypeChecked: c.getGlobalIterableIteratorTypeChecked,
		getGlobalIteratorObjectType:          c.getGlobalIteratorObjectType,
		getGlobalGeneratorType:               c.getGlobalGeneratorType,
		getGlobalBuiltinIteratorTypes:        c.getGlobalTypesResolver([]string{"ArrayIterator", "MapIterator", "SetIterator", "StringIterator"}, 1, false /*reportErrors*/),
		resolveIterationType: func(t *Type, errorNode *ast.Node) *Type {
			return t
		},
		mustHaveANextMethodDiagnostic: diagnostics.An_iterator_must_have_a_next_method,
		mustBeAMethodDiagnostic:       diagnostics.The_0_property_of_an_iterator_must_be_a_method,
		mustHaveAValueDiagnostic:      diagnostics.The_type_returned_by_the_0_method_of_an_iterator_must_have_a_value_property,
	}
	c.asyncIterationTypesResolver = &IterationTypesResolver{
		iteratorSymbolName:                   "asyncIterator",
		getGlobalIteratorType:                c.getGlobalAsyncIteratorType,
		getGlobalIterableType:                c.getGlobalAsyncIterableType,
		getGlobalIterableTypeChecked:         c.getGlobalAsyncIterableTypeChecked,
		getGlobalIterableIteratorType:        c.getGlobalAsyncIterableIteratorType,
		getGlobalIterableIteratorTypeChecked: c.getGlobalAsyncIterableIteratorTypeChecked,
		getGlobalIteratorObjectType:          c.getGlobalAsyncIteratorObjectType,
		getGlobalGeneratorType:               c.getGlobalAsyncGeneratorType,
		getGlobalBuiltinIteratorTypes:        c.getGlobalTypesResolver([]string{"ReadableStreamAsyncIterator"}, 1, false /*reportErrors*/),
		resolveIterationType: func(t *Type, errorNode *ast.Node) *Type {
			return c.getAwaitedTypeEx(t, errorNode, diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
		},
		mustHaveANextMethodDiagnostic: diagnostics.An_async_iterator_must_have_a_next_method,
		mustBeAMethodDiagnostic:       diagnostics.The_0_property_of_an_async_iterator_must_be_a_method,
		mustHaveAValueDiagnostic:      diagnostics.The_type_returned_by_the_0_method_of_an_async_iterator_must_be_a_promise_for_a_type_with_a_value_property,
	}
}

func (c *Checker) initializeChecker() {
	// Initialize global symbol table
	var ambientModuleSymbols []*ast.Symbol
	augmentations := make([][]*ast.Node, 0, len(c.files))
	for _, file := range c.files {
		if !ast.IsExternalOrCommonJSModule(file) {
			// It is an error for a non-external-module (i.e. script) to declare its own `globalThis`.
			if fileGlobalThisSymbol := file.Locals["globalThis"]; fileGlobalThisSymbol != nil {
				for _, d := range fileGlobalThisSymbol.Declarations {
					c.addDiagnostic(NewDiagnosticForNode(d, diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0, "globalThis"))
				}
			}
			for _, symbol := range file.Locals {
				// We defer merging of global ambient module declarations since they may require other global symbols
				// and types to be resolved. See https://github.com/microsoft/typescript-go/issues/2953.
				if symbol.Flags&ast.SymbolFlagsModule != 0 && ast.IsAmbientModuleSymbolName(symbol.Name) {
					ambientModuleSymbols = append(ambientModuleSymbols, symbol)
				} else {
					c.mergeGlobalSymbol(symbol)
				}
			}
		}
		c.patternAmbientModules = append(c.patternAmbientModules, file.PatternAmbientModules...)
		augmentations = append(augmentations, file.ModuleAugmentations)
		if file.Symbol != nil {
			// Merge in UMD exports with first-in-wins semantics (see #9771)
			for name, symbol := range file.GlobalExports {
				if _, ok := c.globals[name]; !ok {
					c.globals[name] = symbol
				}
			}
		}
	}
	// We do global augmentations separately from module augmentations (and before creating global types) because they
	//  1. Affect global types. We won't have the correct global types until global augmentations are merged. Also,
	//  2. Module augmentation instantiation requires creating the type of a module, which, in turn, can require
	//       checking for an export or property on the module (if export=) which, in turn, can fall back to the
	//       apparent type of the module - either globalObjectType or globalFunctionType - which wouldn't exist if we
	//       did module augmentations prior to finalizing the global types.
	for _, list := range augmentations {
		for _, augmentation := range list {
			// Merge 'global' module augmentations. This needs to be done after global symbol table is initialized to
			// make sure that all ambient modules are indexed
			if ast.IsGlobalScopeAugmentation(augmentation.Parent) {
				c.mergeModuleAugmentation(augmentation)
			}
		}
	}
	c.addUndefinedToGlobalsOrErrorOnRedeclaration()
	c.valueSymbolLinks.Get(c.undefinedSymbol).resolvedType = c.undefinedWideningType
	c.valueSymbolLinks.Get(c.argumentsSymbol).resolvedType = c.getGlobalType("IArguments", 0 /*arity*/, true /*reportErrors*/)
	c.valueSymbolLinks.Get(c.unknownSymbol).resolvedType = c.errorType
	c.valueSymbolLinks.Get(c.globalThisSymbol).resolvedType = c.newObjectType(ObjectFlagsAnonymous, c.globalThisSymbol)
	// Initialize special types
	c.globalArrayType = c.getGlobalType("Array", 1 /*arity*/, true /*reportErrors*/)
	c.globalObjectType = c.getGlobalType("Object", 0 /*arity*/, true /*reportErrors*/)
	c.globalFunctionType = c.getGlobalType("Function", 0 /*arity*/, true /*reportErrors*/)
	c.globalCallableFunctionType = c.getGlobalStrictFunctionType("CallableFunction")
	c.globalNewableFunctionType = c.getGlobalStrictFunctionType("NewableFunction")
	c.globalStringType = c.getGlobalType("String", 0 /*arity*/, true /*reportErrors*/)
	c.globalNumberType = c.getGlobalType("Number", 0 /*arity*/, true /*reportErrors*/)
	c.globalBooleanType = c.getGlobalType("Boolean", 0 /*arity*/, true /*reportErrors*/)
	c.globalRegExpType = c.getGlobalType("RegExp", 0 /*arity*/, true /*reportErrors*/)
	c.anyArrayType = c.createArrayType(c.anyType)
	c.autoArrayType = c.createArrayType(c.autoType)
	if c.autoArrayType == c.emptyObjectType {
		// autoArrayType is used as a marker, so even if global Array type is not defined, it needs to be a unique type
		c.autoArrayType = c.newAnonymousType(nil, nil, nil, nil, nil)
	}
	c.globalReadonlyArrayType = c.getGlobalType("ReadonlyArray", 1 /*arity*/, false /*reportErrors*/)
	if c.globalReadonlyArrayType == c.emptyGenericType {
		c.globalReadonlyArrayType = c.globalArrayType
	}
	c.anyReadonlyArrayType = c.createTypeFromGenericGlobalType(c.globalReadonlyArrayType, []*Type{c.anyType})
	c.globalThisType = c.getGlobalType("ThisType", 1 /*arity*/, false /*reportErrors*/)
	// Now merge global ambient module declarations
	for _, symbol := range ambientModuleSymbols {
		c.mergeGlobalSymbol(symbol)
	}
	// merge _nonglobal_ module augmentations.
	// this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed
	for _, list := range augmentations {
		for _, augmentation := range list {
			if !ast.IsGlobalScopeAugmentation(augmentation.Parent) {
				c.mergeModuleAugmentation(augmentation)
			}
		}
	}
}

func (c *Checker) mergeGlobalSymbol(symbol *ast.Symbol) {
	globalSymbol := c.globals[symbol.Name]
	var merged *ast.Symbol
	if globalSymbol != nil {
		merged = c.mergeSymbol(globalSymbol, symbol, false /*unidirectional*/)
	} else {
		merged = c.getMergedSymbol(symbol)
	}
	c.globals[symbol.Name] = merged
}

func (c *Checker) mergeModuleAugmentation(moduleName *ast.Node) {
	moduleNode := moduleName.Parent
	moduleAugmentation := moduleNode.AsModuleDeclaration()
	if moduleAugmentation.Symbol.Declarations[0] != moduleNode {
		// this is a combined symbol for multiple augmentations within the same file.
		// its symbol already has accumulated information for all declarations
		// so we need to add it just once - do the work only for first declaration
		return
	}
	if ast.IsGlobalScopeAugmentation(moduleNode) {
		c.mergeSymbolTable(c.globals, moduleAugmentation.Symbol.Exports, false /*unidirectional*/, nil /*parent*/)
	} else {
		// find a module that about to be augmented
		// do not validate names of augmentations that are defined in ambient context
		var moduleNotFoundError *diagnostics.Message
		if moduleName.Parent.Parent.Flags&ast.NodeFlagsAmbient == 0 {
			moduleNotFoundError = diagnostics.Invalid_module_name_in_augmentation_module_0_cannot_be_found
		}
		mainModule := c.resolveExternalModuleNameWorker(moduleName, moduleName, moduleNotFoundError /*ignoreErrors*/, false /*isForAugmentation*/, true)
		if mainModule == nil {
			return
		}
		// obtain item referenced by 'export='
		mainModule = c.resolveExternalModuleSymbol(mainModule, false /*dontResolveAlias*/)
		if mainModule.Flags&ast.SymbolFlagsNamespace != 0 {
			// If we're merging an augmentation to a pattern ambient module, we want to
			// perform the merge unidirectionally from the augmentation ('a.foo') to
			// the pattern ('*.foo'), so that 'getMergedSymbol()' on a.foo gives you
			// all the exports both from the pattern and from the augmentation, but
			// 'getMergedSymbol()' on *.foo only gives you exports from *.foo.
			if core.Some(c.patternAmbientModules, func(module *ast.PatternAmbientModule) bool {
				return mainModule == module.Symbol
			}) {
				merged := c.mergeSymbol(moduleAugmentation.Symbol, mainModule, true /*unidirectional*/)
				// moduleName will be a StringLiteral since this is not `declare global`.
				ast.GetSymbolTable(&c.patternAmbientModuleAugmentations)[moduleName.Text()] = merged
			} else {
				if mainModule.Exports[ast.InternalSymbolNameExportStar] != nil && len(moduleAugmentation.Symbol.Exports) != 0 {
					// We may need to merge the module augmentation's exports into the target symbols of the resolved exports
					resolvedExports := c.getResolvedMembersOrExportsOfSymbol(mainModule, MembersOrExportsResolutionKindResolvedExports)
					for key, value := range moduleAugmentation.Symbol.Exports {
						if resolvedExports[key] != nil && mainModule.Exports[key] == nil {
							c.mergeSymbol(resolvedExports[key], value, false /*unidirectional*/)
						}
					}
				}
				c.mergeSymbol(mainModule, moduleAugmentation.Symbol, false /*unidirectional*/)
			}
		} else {
			// moduleName will be a StringLiteral since this is not `declare global`.
			c.error(moduleName, diagnostics.Cannot_augment_module_0_because_it_resolves_to_a_non_module_entity, moduleName.Text())
		}
	}
}

func (c *Checker) addUndefinedToGlobalsOrErrorOnRedeclaration() {
	name := c.undefinedSymbol.Name
	targetSymbol := c.globals[name]
	if targetSymbol != nil {
		for _, declaration := range targetSymbol.Declarations {
			if !ast.IsTypeDeclaration(declaration) {
				c.addDiagnostic(createDiagnosticForNode(declaration, diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0, name))
			}
		}
	} else {
		c.globals[name] = c.undefinedSymbol
	}
}

func (c *Checker) createNameResolver() *binder.NameResolver {
	return &binder.NameResolver{
		CompilerOptions:                  c.compilerOptions,
		GetSymbolOfDeclaration:           c.getSymbolOfDeclaration,
		Error:                            c.error,
		Globals:                          c.globals,
		ArgumentsSymbol:                  c.argumentsSymbol,
		RequireSymbol:                    c.requireSymbol,
		Lookup:                           c.getSymbol,
		SymbolReferenced:                 c.symbolReferenced,
		SetRequiresScopeChangeCache:      c.setRequiresScopeChangeCache,
		GetRequiresScopeChangeCache:      c.getRequiresScopeChangeCache,
		OnPropertyWithInvalidInitializer: c.checkAndReportErrorForInvalidInitializer,
		OnFailedToResolveSymbol:          c.onFailedToResolveSymbol,
		OnSuccessfullyResolvedSymbol:     c.onSuccessfullyResolvedSymbol,
	}
}

func (c *Checker) createNameResolverForSuggestion() *binder.NameResolver {
	return &binder.NameResolver{
		CompilerOptions:             c.compilerOptions,
		GetSymbolOfDeclaration:      c.getSymbolOfDeclaration,
		Error:                       c.error,
		Globals:                     c.globals,
		ArgumentsSymbol:             c.argumentsSymbol,
		RequireSymbol:               c.requireSymbol,
		Lookup:                      c.getSuggestionForSymbolNameLookup,
		SymbolReferenced:            c.symbolReferenced,
		SetRequiresScopeChangeCache: c.setRequiresScopeChangeCache,
		GetRequiresScopeChangeCache: c.getRequiresScopeChangeCache,
	}
}

func (c *Checker) symbolReferenced(symbol *ast.Symbol, meaning ast.SymbolFlags) {
	c.symbolReferenceLinks.Get(symbol).referenceKinds |= meaning
}

func (c *Checker) getRequiresScopeChangeCache(node *ast.Node) core.Tristate {
	return c.nodeLinks.Get(node).declarationRequiresScopeChange
}

func (c *Checker) setRequiresScopeChangeCache(node *ast.Node, value core.Tristate) {
	c.nodeLinks.Get(node).declarationRequiresScopeChange = value
}

// The invalid initializer error is needed in two situation:
// 1. When result is undefined, after checking for a missing "this."
// 2. When result is defined
func (c *Checker) checkAndReportErrorForInvalidInitializer(errorLocation *ast.Node, name string, propertyWithInvalidInitializer *ast.Node, result *ast.Symbol) bool {
	if !c.compilerOptions.GetEmitStandardClassFields() {
		if errorLocation != nil && result == nil && c.checkAndReportErrorForMissingPrefix(errorLocation, name) {
			return true
		}
		// We have a match, but the reference occurred within a property initializer and the identifier also binds
		// to a local variable in the constructor where the code will be emitted. Note that this is actually allowed
		// with emitStandardClassFields because the scope semantics are different.
		prop := propertyWithInvalidInitializer.AsPropertyDeclaration()
		message := core.IfElse(errorLocation != nil && prop.Type != nil && prop.Type.Loc.ContainsInclusive(errorLocation.Pos()),
			diagnostics.Type_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor,
			diagnostics.Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor)
		c.error(errorLocation, message, scanner.DeclarationNameToString(prop.Name()), name)
		return true
	}
	return false
}

func (c *Checker) checkAndReportErrorForMissingPrefix(errorLocation *ast.Node, name string) bool {
	if !ast.IsIdentifier(errorLocation) || errorLocation.Text() != name || isTypeReferenceIdentifier(errorLocation) || IsInTypeQuery(errorLocation) {
		return false
	}
	container := c.getThisContainer(errorLocation, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/)
	for location := container; location.Parent != nil; location = location.Parent {
		if ast.IsClassLike(location.Parent) {
			classSymbol := c.getSymbolOfDeclaration(location.Parent)
			if classSymbol == nil {
				break
			}
			// Check to see if a static member exists.
			constructorType := c.getTypeOfSymbol(classSymbol)
			if c.getPropertyOfType(constructorType, name) != nil {
				c.error(errorLocation, diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0, name, c.symbolToString(classSymbol))
				return true
			}
			// No static member is present.
			// Check if we're in an instance method and look for a relevant instance member.
			if location == container && !ast.IsStatic(location) {
				instanceType := c.getDeclaredTypeOfSymbol(classSymbol).AsInterfaceType().thisType
				// TODO: GH#18217
				if c.getPropertyOfType(instanceType, name) != nil {
					c.error(errorLocation, diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0, name)
					return true
				}
			}
		}
	}
	return false
}

func (c *Checker) onFailedToResolveSymbol(errorLocation *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message) {
	if errorLocation != nil && (errorLocation.Parent.Kind == ast.KindJSDocLink ||
		c.checkAndReportErrorForMissingPrefix(errorLocation, name) ||
		c.checkAndReportErrorForExtendingInterface(errorLocation) ||
		c.checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) ||
		c.checkAndReportErrorForExportingPrimitiveType(errorLocation, name) ||
		c.checkAndReportErrorForUsingNamespaceAsTypeOrValue(errorLocation, name, meaning) ||
		c.checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) ||
		c.checkAndReportErrorForUsingValueAsType(errorLocation, name, meaning)) {
		return
	}
	declarationName := name
	if errorLocation != nil && ast.IsIdentifier(errorLocation) && errorLocation.Text() == name {
		declarationName = scanner.DeclarationNameToString(errorLocation) // use escape sequences from original file
	}
	// Report missing lib first
	suggestedLib := c.getSuggestedLibForNonExistentName(name)
	if suggestedLib != "" {
		c.error(errorLocation, nameNotFoundMessage, declarationName, suggestedLib)
		return
	}
	// Then spelling suggestions
	suggestion := c.getSuggestedSymbolForNonexistentSymbol(errorLocation, name, meaning)
	if suggestion != nil && !(suggestion.ValueDeclaration != nil && ast.IsAmbientModule(suggestion.ValueDeclaration) && ast.IsGlobalScopeAugmentation(suggestion.ValueDeclaration)) {
		suggestionName := c.symbolToString(suggestion)
		isUncheckedJS := c.isUncheckedJSSuggestion(errorLocation, suggestion, false /*excludeClasses*/)
		message := core.IfElse(meaning == ast.SymbolFlagsNamespace, diagnostics.Cannot_find_namespace_0_Did_you_mean_1,
			core.IfElse(isUncheckedJS, diagnostics.Could_not_find_name_0_Did_you_mean_1, diagnostics.Cannot_find_name_0_Did_you_mean_1))
		diagnostic := NewDiagnosticForNode(errorLocation, message, declarationName, suggestionName)
		if suggestion.ValueDeclaration != nil {
			diagnostic.AddRelatedInfo(NewDiagnosticForNode(suggestion.ValueDeclaration, diagnostics.X_0_is_declared_here, suggestionName))
		}
		c.addErrorOrSuggestion(!isUncheckedJS, diagnostic)
		return
	}
	// And then fall back to unspecified "not found"
	c.error(errorLocation, nameNotFoundMessage, declarationName)
}

func (c *Checker) checkAndReportErrorForUsingTypeAsNamespace(errorLocation *ast.Node, name string, meaning ast.SymbolFlags) bool {
	if meaning == ast.SymbolFlagsNamespace {
		symbol := c.resolveSymbol(c.resolveName(errorLocation, name, ast.SymbolFlagsType&^ast.SymbolFlagsNamespace, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/))
		if symbol != nil {
			parent := errorLocation.Parent
			if ast.IsQualifiedName(parent) {
				debug.Assert(parent.AsQualifiedName().Left == errorLocation, "Should only be resolving left side of qualified name as a namespace")
				propName := parent.AsQualifiedName().Right.Text()
				propType := c.getPropertyOfType(c.getDeclaredTypeOfSymbol(symbol), propName)
				if propType != nil {
					c.error(parent, diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1, name, propName)
					return true
				}
			}
			c.error(errorLocation, diagnostics.X_0_only_refers_to_a_type_but_is_being_used_as_a_namespace_here, name)
			return true
		}
	}
	return false
}

func (c *Checker) checkAndReportErrorForExportingPrimitiveType(errorLocation *ast.Node, name string) bool {
	if isPrimitiveTypeName(name) && errorLocation.Parent.Kind == ast.KindExportSpecifier {
		c.error(errorLocation, diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, name)
		return true
	}
	return false
}

func isPrimitiveTypeName(s string) bool {
	return s == "any" || s == "string" || s == "number" || s == "boolean" || s == "never" || s == "unknown"
}

func (c *Checker) checkAndReportErrorForUsingNamespaceAsTypeOrValue(errorLocation *ast.Node, name string, meaning ast.SymbolFlags) bool {
	if meaning&(ast.SymbolFlagsValue&^ast.SymbolFlagsType) != 0 {
		symbol := c.resolveSymbol(c.resolveName(errorLocation, name, ast.SymbolFlagsNamespaceModule, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/))
		if symbol != nil {
			c.error(errorLocation, diagnostics.Cannot_use_namespace_0_as_a_value, name)
			return true
		}
	} else if meaning&(ast.SymbolFlagsType&^ast.SymbolFlagsValue) != 0 {
		symbol := c.resolveSymbol(c.resolveName(errorLocation, name, ast.SymbolFlagsModule, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/))
		if symbol != nil {
			c.error(errorLocation, diagnostics.Cannot_use_namespace_0_as_a_type, name)
			return true
		}
	}
	return false
}

func (c *Checker) checkAndReportErrorForUsingTypeAsValue(errorLocation *ast.Node, name string, meaning ast.SymbolFlags) bool {
	if meaning&ast.SymbolFlagsValue != 0 {
		if isPrimitiveTypeName(name) {
			grandparent := errorLocation.Parent.Parent
			if grandparent != nil && grandparent.Parent != nil && ast.IsHeritageClause(grandparent) {
				heritageKind := grandparent.AsHeritageClause().Token
				containerKind := grandparent.Parent.Kind
				if containerKind == ast.KindInterfaceDeclaration && heritageKind == ast.KindExtendsKeyword {
					c.error(errorLocation, diagnostics.An_interface_cannot_extend_a_primitive_type_like_0_It_can_only_extend_other_named_object_types, name)
				} else if ast.IsClassLike(grandparent.Parent) && heritageKind == ast.KindExtendsKeyword {
					c.error(errorLocation, diagnostics.A_class_cannot_extend_a_primitive_type_like_0_Classes_can_only_extend_constructable_values, name)
				} else if ast.IsClassLike(grandparent.Parent) && heritageKind == ast.KindImplementsKeyword {
					c.error(errorLocation, diagnostics.A_class_cannot_implement_a_primitive_type_like_0_It_can_only_implement_other_named_object_types, name)
				}
			} else {
				c.error(errorLocation, diagnostics.X_0_only_refers_to_a_type_but_is_being_used_as_a_value_here, name)
			}
			return true
		}
		symbol := c.resolveSymbol(c.resolveName(errorLocation, name, ast.SymbolFlagsType & ^ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/))
		if symbol != nil {
			allFlags := c.getSymbolFlags(symbol)
			if allFlags&ast.SymbolFlagsValue == 0 {
				if isES2015OrLaterConstructorName(name) {
					c.error(errorLocation, diagnostics.X_0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later, name)
				} else if c.maybeMappedType(errorLocation, symbol) {
					c.error(errorLocation, diagnostics.X_0_only_refers_to_a_type_but_is_being_used_as_a_value_here_Did_you_mean_to_use_1_in_0, name, core.IfElse(name == "K", "P", "K"))
				} else {
					c.error(errorLocation, diagnostics.X_0_only_refers_to_a_type_but_is_being_used_as_a_value_here, name)
				}
				return true
			}
		}
	}
	return false
}

func isES2015OrLaterConstructorName(s string) bool {
	return s == "Promise" || s == "Symbol" || s == "Map" || s == "WeakMap" || s == "Set" || s == "WeakSet"
}

func (c *Checker) maybeMappedType(node *ast.Node, symbol *ast.Symbol) bool {
	for {
		node = node.Parent
		if !(ast.IsComputedPropertyName(node) || ast.IsPropertySignatureDeclaration(node)) {
			break
		}
	}
	if ast.IsTypeLiteralNode(node) && len(node.Members()) == 1 {
		t := c.getDeclaredTypeOfSymbol(symbol)
		return t.flags&TypeFlagsUnion != 0 && c.allTypesAssignableToKindEx(t, TypeFlagsStringOrNumberLiteral, true /*strict*/)
	}
	return false
}

func (c *Checker) checkAndReportErrorForUsingValueAsType(errorLocation *ast.Node, name string, meaning ast.SymbolFlags) bool {
	if meaning&(ast.SymbolFlagsType & ^ast.SymbolFlagsNamespace) != 0 {
		symbol := c.resolveSymbol(c.resolveName(errorLocation, name, ^ast.SymbolFlagsType&ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/))
		if symbol != nil && symbol.Flags&ast.SymbolFlagsNamespace == 0 {
			c.error(errorLocation, diagnostics.X_0_refers_to_a_value_but_is_being_used_as_a_type_here_Did_you_mean_typeof_0, name)
			return true
		}
	}
	return false
}

func (c *Checker) getSuggestedLibForNonExistentName(name string) string {
	featureMap := getFeatureMap()
	if typeFeatures, ok := featureMap[name]; ok {
		return typeFeatures[0].lib
	}
	return ""
}

func (c *Checker) getSuggestedSymbolForNonexistentSymbol(location *ast.Node, outerName string, meaning ast.SymbolFlags) *ast.Symbol {
	return c.resolveNameForSymbolSuggestion(location, outerName, meaning, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/)
}

var primitiveTypeAliasSuggestions = sync.OnceValue(func() map[string]*ast.Symbol {
	result := make(map[string]*ast.Symbol, 6)
	for _, e := range []struct{ primitive, builtin string }{
		{"string", "String"},
		{"number", "Number"},
		{"boolean", "Boolean"},
		{"object", "Object"},
		{"bigint", "BigInt"},
		{"symbol", "Symbol"},
	} {
		sym := &ast.Symbol{}
		sym.Flags = ast.SymbolFlagsTypeAlias | ast.SymbolFlagsTransient
		sym.Name = e.primitive
		result[e.builtin] = sym
	}
	return result
})

func getPrimitiveTypeAliasSuggestions(symbols ast.SymbolTable) iter.Seq[*ast.Symbol] {
	return func(yield func(*ast.Symbol) bool) {
		for builtinName, suggestion := range primitiveTypeAliasSuggestions() {
			if _, ok := symbols[builtinName]; ok {
				if !yield(suggestion) {
					return
				}
			}
		}
	}
}

func (c *Checker) getSuggestionForSymbolNameLookup(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol {
	symbol := c.getSymbol(symbols, name, meaning)
	if symbol != nil {
		return symbol
	}
	var extras iter.Seq[*ast.Symbol]
	if meaning&ast.SymbolFlagsGlobalLookup != 0 {
		extras = getPrimitiveTypeAliasSuggestions(symbols)
	}
	return c.getSpellingSuggestionForName(name, core.ConcatenateSeq(maps.Values(symbols), extras), meaning)
}

// Given a name and a list of symbols whose names are *not* equal to the name, return a spelling suggestion if there is
// one that is close enough. Names less than length 3 only check for case-insensitive equality, not levenshtein distance.
//
// If there is a candidate that's the same except for case, return that.
// If there is a candidate that's within one edit of the name, return that.
// Otherwise, return the candidate with the smallest Levenshtein distance,
//
// Except for candidates:
//   - With no name
//   - Whose meaning doesn't match the `meaning` parameter.
//   - Whose length differs from the target name by more than 0.34 of the length of the name.
//   - Whose levenshtein distance is more than 0.4 of the length of the name (0.4 allows 1 substitution/transposition
//     for every 5 characters, and 1 insertion/deletion at 3 characters)
func (c *Checker) getSpellingSuggestionForName(name string, symbols iter.Seq[*ast.Symbol], meaning ast.SymbolFlags) *ast.Symbol {
	getCandidateName := func(candidate *ast.Symbol) string {
		candidateName := ast.SymbolName(candidate)
		if len(candidateName) == 0 || candidateName[0] == '"' || candidateName[0] == '\xFE' {
			return ""
		}
		if candidate.Flags&meaning != 0 {
			return candidateName
		}
		if candidate.Flags&ast.SymbolFlagsAlias != 0 {
			alias := c.tryResolveAlias(candidate)
			if alias != nil && alias.Flags&meaning != 0 {
				return candidateName
			}
		}
		return ""
	}
	return core.GetSpellingSuggestion(name, symbols, getCandidateName, c.compareSymbols)
}

func (c *Checker) onSuccessfullyResolvedSymbol(errorLocation *ast.Node, result *ast.Symbol, meaning ast.SymbolFlags, lastLocation *ast.Node, associatedDeclarationForContainingInitializerOrBindingName *ast.Node, withinDeferredContext bool) {
	name := result.Name
	isInExternalModule := lastLocation != nil && ast.IsSourceFile(lastLocation) && ast.IsExternalOrCommonJSModule(lastLocation.AsSourceFile())
	// Only check for block-scoped variable if we have an error location and are looking for the
	// name with variable meaning
	//      For example,
	//          declare module foo {
	//              interface bar {}
	//          }
	//      const foo/*1*/: foo/*2*/.bar;
	// The foo at /*1*/ and /*2*/ will share same symbol with two meanings:
	// block-scoped variable and namespace module. However, only when we
	// try to resolve name in /*1*/ which is used in variable position,
	// we want to check for block-scoped
	if errorLocation != nil && (meaning&ast.SymbolFlagsBlockScopedVariable != 0 || meaning&(ast.SymbolFlagsClass|ast.SymbolFlagsEnum) != 0 && meaning&ast.SymbolFlagsValue == ast.SymbolFlagsValue) {
		exportOrLocalSymbol := c.getExportSymbolOfValueSymbolIfExported(result)
		if exportOrLocalSymbol.Flags&(ast.SymbolFlagsBlockScopedVariable|ast.SymbolFlagsClass|ast.SymbolFlagsEnum) != 0 {
			c.checkResolvedBlockScopedVariable(exportOrLocalSymbol, errorLocation)
		}
	}
	// If we're in an external module, we can't reference value symbols created from UMD export declarations
	if isInExternalModule && (meaning&ast.SymbolFlagsValue) == ast.SymbolFlagsValue && errorLocation.Flags&ast.NodeFlagsJSDoc == 0 {
		merged := c.getMergedSymbol(result)
		if len(merged.Declarations) != 0 && core.Every(merged.Declarations, func(d *ast.Node) bool {
			return ast.IsNamespaceExportDeclaration(d) || ast.IsSourceFile(d) && d.AsSourceFile().GlobalExports != nil
		}) {
			c.errorOrSuggestion(c.compilerOptions.AllowUmdGlobalAccess != core.TSTrue, errorLocation, diagnostics.X_0_refers_to_a_UMD_global_but_the_current_file_is_a_module_Consider_adding_an_import_instead, name)
		}
	}
	// If we're in a parameter initializer or binding name, we can't reference the values of the parameter whose initializer we're within or parameters to the right
	if associatedDeclarationForContainingInitializerOrBindingName != nil && !withinDeferredContext && (meaning&ast.SymbolFlagsValue) == ast.SymbolFlagsValue {
		candidate := c.getMergedSymbol(c.getLateBoundSymbol(result))
		root := ast.GetRootDeclaration(associatedDeclarationForContainingInitializerOrBindingName)
		// A parameter initializer or binding pattern initializer within a parameter cannot refer to itself
		if candidate == c.getSymbolOfDeclaration(associatedDeclarationForContainingInitializerOrBindingName) {
			c.error(errorLocation, diagnostics.Parameter_0_cannot_reference_itself, scanner.DeclarationNameToString(associatedDeclarationForContainingInitializerOrBindingName.Name()))
		} else if candidate.ValueDeclaration != nil && candidate.ValueDeclaration.Pos() > associatedDeclarationForContainingInitializerOrBindingName.Pos() && root.Parent.Locals() != nil && c.getSymbol(root.Parent.Locals(), candidate.Name, meaning) == candidate {
			c.error(errorLocation, diagnostics.Parameter_0_cannot_reference_identifier_1_declared_after_it, scanner.DeclarationNameToString(associatedDeclarationForContainingInitializerOrBindingName.Name()), scanner.DeclarationNameToString(errorLocation))
		}
	}
	if errorLocation != nil && meaning&ast.SymbolFlagsValue != 0 && result.Flags&ast.SymbolFlagsAlias != 0 && result.Flags&ast.SymbolFlagsValue == 0 && !ast.IsValidTypeOnlyAliasUseSite(errorLocation) {
		typeOnlyDeclaration := c.getTypeOnlyAliasDeclarationEx(result, ast.SymbolFlagsValue)
		if typeOnlyDeclaration != nil {
			message := core.IfElse(ast.NodeKindIs(typeOnlyDeclaration, ast.KindExportSpecifier, ast.KindExportDeclaration, ast.KindNamespaceExport),
				diagnostics.X_0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type,
				diagnostics.X_0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type)
			c.addTypeOnlyDeclarationRelatedInfo(c.error(errorLocation, message, name), typeOnlyDeclaration, name)
		}
	}
	// Look at 'compilerOptions.isolatedModules' and not 'getIsolatedModules(...)' (which considers 'verbatimModuleSyntax')
	// here because 'verbatimModuleSyntax' will already have an error for importing a type without 'import type'.
	if c.compilerOptions.IsolatedModules == core.TSTrue && result != nil && isInExternalModule && (meaning&ast.SymbolFlagsValue) == ast.SymbolFlagsValue {
		isGlobal := c.getSymbol(c.globals, name, meaning) == result
		var nonValueSymbol *ast.Symbol
		if isGlobal && ast.IsSourceFile(lastLocation) {
			nonValueSymbol = c.getSymbol(lastLocation.Locals(), name, ^ast.SymbolFlagsValue)
		}
		if nonValueSymbol != nil {
			importDecl := core.Find(nonValueSymbol.Declarations, func(d *ast.Node) bool {
				return ast.NodeKindIs(d, ast.KindImportSpecifier, ast.KindImportClause, ast.KindNamespaceImport, ast.KindImportEqualsDeclaration)
			})
			if importDecl != nil && !ast.IsTypeOnlyImportDeclaration(importDecl) {
				c.error(importDecl, diagnostics.Import_0_conflicts_with_global_value_used_in_this_file_so_must_be_declared_with_a_type_only_import_when_isolatedModules_is_enabled, name)
			}
		}
	}
}

func (c *Checker) checkResolvedBlockScopedVariable(result *ast.Symbol, errorLocation *ast.Node) {
	debug.Assert(result.Flags&ast.SymbolFlagsBlockScopedVariable != 0 || result.Flags&ast.SymbolFlagsClass != 0 || result.Flags&ast.SymbolFlagsEnum != 0)
	if result.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsFunctionScopedVariable|ast.SymbolFlagsAssignment) != 0 && result.Flags&ast.SymbolFlagsClass != 0 {
		// constructor functions aren't block scoped
		return
	}
	// Block-scoped variables cannot be used before their definition
	declaration := core.Find(result.Declarations, func(d *ast.Node) bool {
		return ast.IsBlockOrCatchScoped(d) || ast.IsClassLike(d) || ast.IsEnumDeclaration(d)
	})
	if declaration == nil {
		panic("checkResolvedBlockScopedVariable could not find block-scoped declaration")
	}
	if declaration.Flags&ast.NodeFlagsAmbient == 0 && !c.isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation) {
		var diagnostic *ast.Diagnostic
		declarationName := scanner.DeclarationNameToString(ast.GetNameOfDeclaration(declaration))
		if result.Flags&ast.SymbolFlagsBlockScopedVariable != 0 {
			diagnostic = c.error(errorLocation, diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationName)
		} else if result.Flags&ast.SymbolFlagsClass != 0 {
			diagnostic = c.error(errorLocation, diagnostics.Class_0_used_before_its_declaration, declarationName)
		} else if result.Flags&ast.SymbolFlagsRegularEnum != 0 {
			diagnostic = c.error(errorLocation, diagnostics.Enum_0_used_before_its_declaration, declarationName)
		} else {
			debug.Assert(result.Flags&ast.SymbolFlagsConstEnum != 0)
			if c.compilerOptions.GetIsolatedModules() {
				diagnostic = c.error(errorLocation, diagnostics.Enum_0_used_before_its_declaration, declarationName)
			}
		}
		if diagnostic != nil {
			diagnostic.AddRelatedInfo(createDiagnosticForNode(declaration, diagnostics.X_0_is_declared_here, declarationName))
		}
	}
}

func (c *Checker) isBlockScopedNameDeclaredBeforeUse(declaration *ast.Node, usage *ast.Node) bool {
	declarationFile := ast.GetSourceFileOfNode(declaration)
	useFile := ast.GetSourceFileOfNode(usage)
	declContainer := ast.GetEnclosingBlockScopeContainer(declaration)
	if declarationFile != useFile {
		// nodes are in different files and order cannot be determined
		return true
	}
	// deferred usage in a type context is always OK regardless of the usage position:
	if usage.Flags&ast.NodeFlagsJSDoc != 0 || IsInTypeQuery(usage) || c.isInAmbientOrTypeNode(usage) {
		return true
	}
	if declaration.Pos() <= usage.Pos() && !(ast.IsPropertyDeclaration(declaration) && isThisProperty(usage.Parent) && declaration.Initializer() == nil && !isExclamationToken(declaration.PostfixToken())) {
		// declaration is before usage
		switch {
		case declaration.Kind == ast.KindBindingElement:
			// still might be illegal if declaration and usage are both binding elements (eg var [a = b, b = b] = [1, 2])
			errorBindingElement := ast.FindAncestorKind(usage, ast.KindBindingElement)
			if errorBindingElement != nil {
				return ast.FindAncestor(errorBindingElement, ast.IsBindingElement) != ast.FindAncestor(declaration, ast.IsBindingElement) || declaration.Pos() < errorBindingElement.Pos()
			}
			// or it might be illegal if usage happens before parent variable is declared (eg var [a] = a)
			return c.isBlockScopedNameDeclaredBeforeUse(ast.FindAncestorKind(declaration, ast.KindVariableDeclaration), usage)
		case declaration.Kind == ast.KindVariableDeclaration:
			// still might be illegal if usage is in the initializer of the variable declaration (eg var a = a)
			return !isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration, usage, declContainer)
		case ast.IsClassLike(declaration):
			// still might be illegal if the usage is within a computed property name in the class (eg class A { static p = "a"; [A.p]() {} })
			// or when used within a decorator in the class (e.g. `@dec(A.x) class A { static x = "x" }`),
			// except when used in a function that is not an IIFE (e.g., `@dec(() => A.x) class A { ... }`)
			container := usage
			for container != nil && container != declaration {
				if ast.IsComputedPropertyName(container) && container.Parent.Parent == declaration ||
					!c.legacyDecorators && ast.IsDecorator(container) && (container.Parent == declaration ||
						ast.IsMethodDeclaration(container.Parent) && container.Parent.Parent == declaration ||
						ast.IsAccessor(container.Parent) && container.Parent.Parent == declaration ||
						ast.IsPropertyDeclaration(container.Parent) && container.Parent.Parent == declaration ||
						ast.IsParameterDeclaration(container.Parent) && container.Parent.Parent.Parent == declaration) {
					break
				}
				container = container.Parent
			}
			if container == nil || container == declaration {
				return true
			}
			if !c.legacyDecorators && ast.IsDecorator(container) {
				n := usage
				for n != nil && n != container {
					if ast.IsFunctionLike(n) && ast.GetImmediatelyInvokedFunctionExpression(n) == nil {
						break
					}
					n = n.Parent
				}
				return n != nil && n != container
			}
			return false
		case ast.IsPropertyDeclaration(declaration):
			// still might be illegal if a self-referencing property initializer (eg private x = this.x)
			return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, false /*stopAtAnyPropertyDeclaration*/)
		case ast.IsParameterPropertyDeclaration(declaration, declaration.Parent):
			// foo = this.bar is illegal in emitStandardClassFields when bar is a parameter property
			return !(c.emitStandardClassFields && ast.GetContainingClass(declaration) == ast.GetContainingClass(usage) && c.isUsedInFunctionOrInstanceProperty(usage, declaration, declContainer))
		}
		return true
	}
	// declaration is after usage, but it can still be legal if usage is deferred:
	// 1. inside an export specifier
	// 2. inside a function
	// 3. inside an instance property initializer, a reference to a non-instance property
	//    (except when emitStandardClassFields: true and the reference is to a parameter property)
	// 4. inside a static property initializer, a reference to a static method in the same class
	// 5. inside a TS export= declaration (since we will move the export statement during emit to avoid TDZ)
	if ast.IsExportSpecifier(usage.Parent) || ast.IsExportAssignment(usage.Parent) && usage.Parent.AsExportAssignment().IsExportEquals {
		// export specifiers do not use the variable, they only make it available for use
		return true
	}
	// When resolving symbols for exports, the `usage` location passed in can be the export site directly
	if ast.IsExportAssignment(usage) && usage.AsExportAssignment().IsExportEquals {
		return true
	}
	if c.isUsedInFunctionOrInstanceProperty(usage, declaration, declContainer) {
		if c.emitStandardClassFields && ast.GetContainingClass(declaration) != nil && (ast.IsPropertyDeclaration(declaration) || ast.IsParameterPropertyDeclaration(declaration, declaration.Parent)) {
			return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, true /*stopAtAnyPropertyDeclaration*/)
		}
		return true
	}
	return false
}

func (c *Checker) isUsedInFunctionOrInstanceProperty(usage *ast.Node, declaration *ast.Node, declContainer *ast.Node) bool {
	return ast.FindAncestorOrQuit(usage, func(current *ast.Node) ast.FindAncestorResult {
		if current == declContainer {
			return ast.FindAncestorQuit
		}
		if ast.IsFunctionLike(current) {
			return ast.ToFindAncestorResult(ast.GetImmediatelyInvokedFunctionExpression(current) == nil)
		}
		if ast.IsClassStaticBlockDeclaration(current) {
			return ast.ToFindAncestorResult(declaration.Pos() < usage.Pos())
		}

		if current.Parent != nil && ast.IsPropertyDeclaration(current.Parent) {
			propertyDeclaration := current.Parent
			initializerOfProperty := propertyDeclaration.Initializer() == current
			if initializerOfProperty {
				if ast.IsStatic(current.Parent) {
					if ast.IsMethodDeclaration(declaration) {
						return ast.FindAncestorTrue
					}
					if ast.IsPropertyDeclaration(declaration) && ast.GetContainingClass(usage) == ast.GetContainingClass(declaration) {
						propName := declaration.Name()
						if ast.IsIdentifier(propName) || ast.IsPrivateIdentifier(propName) {
							t := c.getTypeOfSymbol(c.getSymbolOfDeclaration(declaration))
							staticBlocks := core.Filter(declaration.Parent.Members(), ast.IsClassStaticBlockDeclaration)
							if c.isPropertyInitializedInStaticBlocks(propName, t, staticBlocks, declaration.Parent.Pos(), current.Pos()) {
								return ast.FindAncestorTrue
							}
						}
					}
				} else {
					isDeclarationInstanceProperty := ast.IsPropertyDeclaration(declaration) && !ast.IsStatic(declaration)
					if !isDeclarationInstanceProperty || ast.GetContainingClass(usage) != ast.GetContainingClass(declaration) {
						return ast.FindAncestorTrue
					}
				}
			}
		}

		if current.Parent != nil && ast.IsDecorator(current.Parent) {
			decorator := current.Parent.AsDecorator()
			if decorator.Expression == current {
				if ast.IsParameterDeclaration(decorator.Parent) {
					if c.isUsedInFunctionOrInstanceProperty(decorator.Parent.Parent.Parent, declaration, declContainer) {
						return ast.FindAncestorTrue
					}
					return ast.FindAncestorQuit
				}
				if ast.IsMethodDeclaration(decorator.Parent) {
					if c.isUsedInFunctionOrInstanceProperty(decorator.Parent.Parent, declaration, declContainer) {
						return ast.FindAncestorTrue
					}
					return ast.FindAncestorQuit
				}
			}
		}

		return ast.FindAncestorFalse
	}) != nil
}

func isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration *ast.Node, usage *ast.Node, declContainer *ast.Node) bool {
	switch declaration.Parent.Parent.Kind {
	case ast.KindVariableStatement, ast.KindForStatement, ast.KindForOfStatement:
		// variable statement/for/for-of statement case,
		// use site should not be inside variable declaration (initializer of declaration or binding element)
		if isSameScopeDescendentOf(usage, declaration, declContainer) {
			return true
		}
	}
	// ForIn/ForOf case - use site should not be used in expression part
	grandparent := declaration.Parent.Parent
	return ast.IsForInOrOfStatement(grandparent) && isSameScopeDescendentOf(usage, grandparent.Expression(), declContainer)
}

// Starting from 'initial' node walk up the parent chain until 'stopAt' node is reached.
// If at any point current node is equal to 'parent' node - return true.
// If current node is an IIFE, continue walking up.
// Return false if 'stopAt' node is reached or isFunctionLike(current) === true.
func isSameScopeDescendentOf(initial *ast.Node, parent *ast.Node, stopAt *ast.Node) bool {
	if parent == nil {
		return false
	}
	for n := initial; n != nil; n = n.Parent {
		if n == parent {
			return true
		}
		if n == stopAt || ast.IsFunctionLike(n) && (ast.GetImmediatelyInvokedFunctionExpression(n) == nil || (ast.GetFunctionFlags(n)&ast.FunctionFlagsAsyncGenerator != 0)) {
			return false
		}
	}
	return false
}

// isPropertyImmediatelyReferencedWithinDeclaration is used for detecting ES-standard class field use-before-def errors
func isPropertyImmediatelyReferencedWithinDeclaration(declaration *ast.Node, usage *ast.Node, stopAtAnyPropertyDeclaration bool) bool {
	// always legal if usage is after declaration
	if usage.End() > declaration.End() {
		return false
	}
	// still might be legal if usage is deferred (e.g. x: any = () => this.x)
	// otherwise illegal if immediately referenced within the declaration (e.g. x: any = this.x)
	for node := usage; node != nil && node != declaration; node = node.Parent {
		switch node.Kind {
		case ast.KindArrowFunction:
			return false
		case ast.KindPropertyDeclaration:
			// even when stopping at any property declaration, they need to come from the same class
			return stopAtAnyPropertyDeclaration &&
				((ast.IsPropertyDeclaration(declaration) && node.Parent == declaration.Parent) ||
					(ast.IsParameterPropertyDeclaration(declaration, declaration.Parent) && node.Parent == declaration.Parent.Parent))
		case ast.KindBlock:
			switch node.Parent.Kind {
			case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
				return false
			}
		}
	}
	return true
}

// Return the type-only declaration node (if any) for the given alias symbol (non-transitively)
func (c *Checker) getTypeOnlyAliasDeclaration(symbol *ast.Symbol) *ast.Node {
	if symbol.Flags&ast.SymbolFlagsAlias != 0 {
		c.resolveAlias(symbol)
		return c.aliasSymbolLinks.Get(symbol).typeOnlyDeclaration
	}
	return nil
}

// Return the first type-only alias declaration node (if any) in the resolution chain that affects
// the symbol for the given meaning
func (c *Checker) getTypeOnlyAliasDeclarationEx(symbol *ast.Symbol, meaning ast.SymbolFlags) *ast.Node {
	for symbol.Flags&ast.SymbolFlagsAlias != 0 && symbol.Flags&meaning == 0 {
		resolved := c.resolveAlias(symbol)
		links := c.aliasSymbolLinks.Get(symbol)
		if links.typeOnlyDeclaration != nil {
			return links.typeOnlyDeclaration
		}
		symbol = resolved
	}
	return nil
}

func (c *Checker) getImmediateAliasedSymbol(symbol *ast.Symbol) *ast.Symbol {
	debug.Assert(symbol.Flags&ast.SymbolFlagsAlias != 0, "Should only get Alias here.")
	links := c.aliasSymbolLinks.Get(symbol)
	if links.immediateTarget == nil {
		node := c.getDeclarationOfAliasSymbol(symbol)
		if node == nil {
			panic("Unexpected nil in getImmediateAliasedSymbol")
		}
		links.immediateTarget = c.getTargetOfAliasDeclaration(node)
	}
	return links.immediateTarget
}

func (c *Checker) addTypeOnlyDeclarationRelatedInfo(diagnostic *ast.Diagnostic, typeOnlyDeclaration *ast.Node, name string) *ast.Diagnostic {
	if typeOnlyDeclaration == nil {
		return diagnostic
	}
	isExport := ast.IsExportSpecifier(typeOnlyDeclaration) || ast.IsExportDeclaration(typeOnlyDeclaration) || ast.IsNamespaceExport(typeOnlyDeclaration)
	return diagnostic.AddRelatedInfo(NewDiagnosticForNode(typeOnlyDeclaration, core.IfElse(isExport, diagnostics.X_0_was_exported_here, diagnostics.X_0_was_imported_here), name))
}

func (c *Checker) getSymbol(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol {
	if meaning&ast.SymbolFlagsAll != 0 {
		symbol := c.getMergedSymbol(symbols[name])
		if symbol != nil {
			if symbol.Flags&meaning != 0 {
				return symbol
			}
			if symbol.Flags&ast.SymbolFlagsAlias != 0 {
				targetFlags := c.getSymbolFlags(symbol)
				// `targetFlags` will be `SymbolFlags.All` if an error occurred in alias resolution; this avoids cascading errors
				if targetFlags&meaning != 0 {
					return symbol
				}
			}
		}
	}
	// return nil if we can't find a symbol
	return nil
}

func (c *Checker) checkSourceFile(ctx context.Context, sourceFile *ast.SourceFile, checkUnused bool) {
	c.ctx = ctx
	links := c.sourceFileLinks.Get(sourceFile)
	if !links.typeChecked {
		c.saveDeferredDiagnostics = true
		if tr := c.tracer; tr != nil {
			defer tr.Push(tracing.PhaseCheck, "checkSourceFile", map[string]any{"path": sourceFile.FileName()}, true)()
		}
		// Grammar checking
		c.checkGrammarSourceFile(sourceFile)
		c.renamedBindingElementsInTypes = nil
		c.checkSourceElements(sourceFile.Statements.Nodes)
		c.checkDeferredNodes(sourceFile)
		if ast.IsExternalOrCommonJSModule(sourceFile) {
			c.checkExternalModuleExports(sourceFile.AsNode())
			c.registerForUnusedIdentifiersCheck(sourceFile.AsNode())
		}
		if !sourceFile.IsDeclarationFile && !c.isCanceled() {
			c.checkUnusedRenamedBindingElements()
		}
		c.saveDeferredDiagnostics = false
		c.produceDeferredDiagnostics()
		c.reportedUnreachableNodes.Clear()
		links.typeChecked = true
	}
	if checkUnused && !links.unusedChecked {
		// The unused identifiers check relies on a full type check having first been performed
		if !sourceFile.IsDeclarationFile && !c.isCanceled() {
			c.checkUnusedIdentifiers(links.identifierCheckNodes)
		}
		links.unusedChecked = true
	}
	if c.isCanceled() {
		c.wasCanceled = true
	}
	c.ctx = nil
}

func (c *Checker) checkSourceElements(nodes []*ast.Node) {
	for _, node := range nodes {
		if c.isCanceled() {
			break
		}
		c.checkSourceElement(node)
	}
}

func (c *Checker) checkSourceElement(node *ast.Node) bool {
	if node != nil {
		saveCurrentNode := c.currentNode
		saveWithinUnreachableCode := c.withinUnreachableCode
		c.currentNode = node
		c.instantiationCount = 0
		c.checkSourceElementWorker(node)
		c.currentNode = saveCurrentNode
		c.withinUnreachableCode = saveWithinUnreachableCode
	}
	return false
}

func (c *Checker) checkSourceElementWorker(node *ast.Node) {
	for _, jsdoc := range node.EagerJSDoc(nil) {
		c.checkJSDocComments(jsdoc)
		if tags := jsdoc.AsJSDoc().Tags; tags != nil {
			for _, tag := range tags.Nodes {
				c.checkJSDocComments(tag)
			}
		}
	}

	if !c.withinUnreachableCode && c.compilerOptions.AllowUnreachableCode != core.TSTrue {
		if c.checkSourceElementUnreachable(node) {
			c.withinUnreachableCode = true
		}
	}

	switch node.Kind {
	case ast.KindTypeParameter:
		c.checkTypeParameter(node)
	case ast.KindParameter:
		c.checkParameter(node)
	case ast.KindPropertyDeclaration:
		c.checkPropertyDeclaration(node)
	case ast.KindPropertySignature:
		c.checkPropertySignature(node)
	case ast.KindConstructorType, ast.KindFunctionType, ast.KindCallSignature, ast.KindConstructSignature, ast.KindIndexSignature:
		c.checkSignatureDeclaration(node)
	case ast.KindMethodDeclaration, ast.KindMethodSignature:
		c.checkMethodDeclaration(node)
	case ast.KindClassStaticBlockDeclaration:
		c.checkClassStaticBlockDeclaration(node)
	case ast.KindConstructor:
		c.checkConstructorDeclaration(node)
	case ast.KindGetAccessor, ast.KindSetAccessor:
		c.checkAccessorDeclaration(node)
	case ast.KindTypeReference:
		c.checkTypeReferenceNode(node)
	case ast.KindTypePredicate:
		c.checkTypePredicate(node)
	case ast.KindTypeQuery:
		c.checkTypeQuery(node)
	case ast.KindTypeLiteral:
		c.checkTypeLiteral(node)
	case ast.KindArrayType:
		c.checkArrayType(node)
	case ast.KindTupleType:
		c.checkTupleType(node)
	case ast.KindUnionType, ast.KindIntersectionType:
		c.checkUnionOrIntersectionType(node)
	case ast.KindParenthesizedType, ast.KindOptionalType, ast.KindRestType:
		node.ForEachChild(c.checkSourceElement)
	case ast.KindThisType:
		c.checkThisType(node)
	case ast.KindTypeOperator:
		c.checkTypeOperator(node)
	case ast.KindConditionalType:
		c.checkConditionalType(node)
	case ast.KindInferType:
		c.checkInferType(node)
	case ast.KindTemplateLiteralType:
		c.checkTemplateLiteralType(node)
	case ast.KindImportType:
		c.checkImportType(node)
	case ast.KindNamedTupleMember:
		c.checkNamedTupleMember(node)
	case ast.KindIndexedAccessType:
		c.checkIndexedAccessType(node)
	case ast.KindMappedType:
		c.checkMappedType(node)
	case ast.KindFunctionDeclaration:
		c.checkFunctionDeclaration(node)
	case ast.KindBlock, ast.KindModuleBlock:
		c.checkBlock(node)
	case ast.KindVariableStatement:
		c.checkVariableStatement(node)
	case ast.KindExpressionStatement:
		c.checkExpressionStatement(node)
	case ast.KindIfStatement:
		c.checkIfStatement(node)
	case ast.KindDoStatement:
		c.checkDoStatement(node)
	case ast.KindWhileStatement:
		c.checkWhileStatement(node)
	case ast.KindForStatement:
		c.checkForStatement(node)
	case ast.KindForInStatement:
		c.checkForInStatement(node)
	case ast.KindForOfStatement:
		c.checkForOfStatement(node)
	case ast.KindContinueStatement, ast.KindBreakStatement:
		c.checkBreakOrContinueStatement(node)
	case ast.KindReturnStatement:
		c.checkReturnStatement(node)
	case ast.KindWithStatement:
		c.checkWithStatement(node)
	case ast.KindSwitchStatement:
		c.checkSwitchStatement(node)
	case ast.KindLabeledStatement:
		c.checkLabeledStatement(node)
	case ast.KindThrowStatement:
		c.checkThrowStatement(node)
	case ast.KindTryStatement:
		c.checkTryStatement(node)
	case ast.KindVariableDeclaration:
		c.checkVariableDeclaration(node)
	case ast.KindBindingElement:
		c.checkBindingElement(node)
	case ast.KindClassDeclaration:
		c.checkClassDeclaration(node)
	case ast.KindInterfaceDeclaration:
		c.checkInterfaceDeclaration(node)
	case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration:
		c.checkTypeAliasDeclaration(node)
	case ast.KindEnumDeclaration:
		c.checkEnumDeclaration(node)
	case ast.KindEnumMember:
		c.checkEnumMember(node)
	case ast.KindModuleDeclaration:
		c.checkModuleDeclaration(node)
	case ast.KindImportDeclaration, ast.KindJSImportDeclaration:
		c.checkImportDeclaration(node)
	case ast.KindImportEqualsDeclaration:
		c.checkImportEqualsDeclaration(node)
	case ast.KindExportDeclaration:
		c.checkExportDeclaration(node)
	case ast.KindExportAssignment:
		c.checkExportAssignment(node)
	case ast.KindEmptyStatement:
		c.checkGrammarStatementInAmbientContext(node)
	case ast.KindDebuggerStatement:
		c.checkGrammarStatementInAmbientContext(node)
	case ast.KindMissingDeclaration:
		c.checkMissingDeclaration(node)
	case ast.KindJSDocNonNullableType, ast.KindJSDocNullableType, ast.KindJSDocAllType, ast.KindJSDocTypeLiteral:
		c.checkJSDocType(node)
	}
}

func (c *Checker) checkSourceElementUnreachable(node *ast.Node) bool {
	if !ast.IsPotentiallyExecutableNode(node) {
		return false
	}

	if c.reportedUnreachableNodes.Has(node) {
		return true
	}

	if !c.isSourceElementUnreachable(node) {
		return false
	}

	c.reportedUnreachableNodes.Add(node)

	sourceFile := ast.GetSourceFileOfNode(node)

	startNode := node
	endNode := node

	parent := node.Parent
	if parent.CanHaveStatements() {
		statements := parent.Statements()
		if offset := slices.Index(statements, node); offset >= 0 {
			// Scan backwards to find the first unreachable unreported node;
			// this may happen when producing region diagnostics where not all nodes
			// will have been visited.
			// TODO: enable this code once we support region diagnostics again.
			first := offset
			// for i := offset - 1; i >= 0; i-- {
			// 	prevNode := statements[i]
			// 	if !ast.IsPotentiallyExecutableNode(prevNode) || c.reportedUnreachableNodes.Has(prevNode) || !c.isSourceElementUnreachable(prevNode) {
			// 		break
			// 	}
			// 	firstUnreachableIndex = i
			// 	c.reportedUnreachableNodes.Add(prevNode)
			// }

			last := offset
			for i := offset + 1; i < len(statements); i++ {
				nextNode := statements[i]
				if !ast.IsPotentiallyExecutableNode(nextNode) || !c.isSourceElementUnreachable(nextNode) {
					break
				}
				last = i
				c.reportedUnreachableNodes.Add(nextNode)
			}

			startNode = statements[first]
			endNode = statements[last]
		}
	}

	start := scanner.GetTokenPosOfNode(startNode, sourceFile, false /*includeJSDoc*/)

	diagnostic := ast.NewDiagnostic(sourceFile, core.NewTextRange(start, endNode.End()), diagnostics.Unreachable_code_detected)
	c.addErrorOrSuggestion(c.compilerOptions.AllowUnreachableCode == core.TSFalse, diagnostic)

	return true
}

func (c *Checker) isSourceElementUnreachable(node *ast.Node) bool {
	// Precondition: ast.IsPotentiallyExecutableNode is true
	if node.Flags&ast.NodeFlagsUnreachable != 0 {
		// The binder has determined that this code is unreachable.
		// Ignore const enums unless preserveConstEnums is set.
		switch node.Kind {
		case ast.KindEnumDeclaration:
			return !ast.IsEnumConst(node) || c.compilerOptions.ShouldPreserveConstEnums()
		case ast.KindModuleDeclaration:
			return ast.IsInstantiatedModule(node, c.compilerOptions.ShouldPreserveConstEnums())
		default:
			return true
		}
	} else if flowNode := node.FlowNodeData().FlowNode; flowNode != nil {
		// For code the binder doesn't know is unreachable, use control flow / types.
		return !c.isReachableFlowNode(flowNode)
	}
	return false
}

// Function and class expression bodies are checked after all statements in the enclosing body. This is
// to ensure constructs like the following are permitted:
//
//	const foo = function () {
//	   const s = foo();
//	   return "hello";
//	}
//
// Here, performing a full type check of the body of the function expression whilst in the process of
// determining the type of foo would cause foo to be given type any because of the recursive reference.
// Delaying the type check of the body ensures foo has been assigned a type.
func (c *Checker) checkNodeDeferred(node *ast.Node) {
	enclosingFile := ast.GetSourceFileOfNode(node)
	links := c.sourceFileLinks.Get(enclosingFile)
	if !links.typeChecked {
		links.deferredNodes.Add(node)
	}
}

func (c *Checker) checkDeferredNodes(context *ast.SourceFile) {
	links := c.sourceFileLinks.Get(context)
	for node := range links.deferredNodes.Values() {
		if c.isCanceled() {
			break
		}
		c.checkDeferredNode(node)
	}
	links.deferredNodes.Clear()
}

func (c *Checker) checkDeferredNode(node *ast.Node) {
	if tr := c.tracer; tr != nil {
		defer tr.Push(tracing.PhaseCheck, "checkDeferredNode", map[string]any{"kind": node.Kind, "pos": node.Pos(), "end": node.End(), "path": ast.GetSourceFileOfNode(node).FileName()}, false)()
	}
	saveCurrentNode := c.currentNode
	c.currentNode = node
	c.instantiationCount = 0
	switch node.Kind {
	case ast.KindCallExpression, ast.KindNewExpression, ast.KindTaggedTemplateExpression, ast.KindDecorator, ast.KindJsxOpeningElement:
		// These node kinds are deferred checked when overload resolution fails. To save on work,
		// we ensure the arguments are checked just once in a deferred way.
		c.resolveUntypedCall(node)
	case ast.KindFunctionExpression, ast.KindArrowFunction, ast.KindMethodDeclaration, ast.KindMethodSignature:
		c.checkFunctionExpressionOrObjectLiteralMethodDeferred(node)
	case ast.KindGetAccessor, ast.KindSetAccessor:
		c.checkAccessorDeclaration(node)
	case ast.KindClassExpression:
		c.checkClassExpressionDeferred(node)
	case ast.KindTypeParameter:
		c.checkTypeParameterDeferred(node)
	case ast.KindJsxSelfClosingElement:
		c.checkJsxSelfClosingElementDeferred(node)
	case ast.KindJsxElement:
		c.checkJsxElementDeferred(node)
	case ast.KindTypeAssertionExpression, ast.KindAsExpression:
		c.checkAssertionDeferred(node)
	case ast.KindVoidExpression:
		c.checkExpression(node.Expression())
	case ast.KindBinaryExpression:
		if ast.IsInstanceOfExpression(node) {
			c.resolveUntypedCall(node)
		}
	}
	c.currentNode = saveCurrentNode
}

func (c *Checker) checkJSDocComments(node *ast.Node) {
	for _, comment := range node.Comments() {
		c.checkJSDocComment(comment)
	}
}

func (c *Checker) checkJSDocComment(node *ast.Node) {
	// This performs minimal checking of JSDoc nodes to ensure that @link references to entities are recorded
	// for purposes of checking unused identifiers.
	switch node.Kind {
	case ast.KindJSDocLink, ast.KindJSDocLinkCode, ast.KindJSDocLinkPlain:
		c.resolveJSDocMemberName(node.Name())
	}
}

func (c *Checker) resolveJSDocMemberName(name *ast.Node) *ast.Symbol {
	if name != nil && ast.IsEntityName(name) {
		meaning := ast.SymbolFlagsType | ast.SymbolFlagsNamespace | ast.SymbolFlagsValue
		if symbol := c.resolveEntityName(name, meaning, true /*ignoreErrors*/, true /*dontResolveAlias*/, ast.GetHostSignatureFromJSDoc(name)); symbol != nil {
			return symbol
		}
		if ast.IsQualifiedName(name) {
			if symbol := c.resolveJSDocMemberName(name.AsQualifiedName().Left); symbol != nil {
				var t *Type
				if symbol.Flags&ast.SymbolFlagsValue != 0 {
					proto := c.getPropertyOfType(c.getTypeOfSymbol(symbol), "prototype")
					if proto != nil {
						t = c.getTypeOfSymbol(proto)
					}
				}
				if t == nil {
					t = c.getDeclaredTypeOfSymbol(symbol)
				}
				return c.getPropertyOfType(t, name.AsQualifiedName().Right.Text())
			}
		}
	}
	return nil
}

func (c *Checker) checkJSDocType(node *ast.Node) {
	c.checkJSDocTypeIsInJsFile(node)
	node.ForEachChild(c.checkSourceElement)
}

func (c *Checker) checkJSDocTypeIsInJsFile(node *ast.Node) {
	if !ast.IsInJSFile(node) {
		if ast.IsJSDocNonNullableType(node) || ast.IsJSDocNullableType(node) {
			token := core.IfElse(ast.IsJSDocNonNullableType(node), "!", "?")
			postfix := node.Pos() == node.Type().Pos()
			message := core.IfElse(postfix,
				diagnostics.X_0_at_the_end_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1,
				diagnostics.X_0_at_the_start_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1)
			t := c.getTypeFromTypeNode(node.Type())
			if ast.IsJSDocNullableType(node) && t != c.neverType && t != c.voidType {
				t = c.getNullableType(t, core.IfElse(postfix, TypeFlagsUndefined, TypeFlagsNullable))
			}
			c.grammarErrorOnNode(node, message, token, c.TypeToString(t))
		} else {
			c.grammarErrorOnNode(node, diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments)
		}
	}
}

func (c *Checker) checkTypeParameter(node *ast.Node) {
	// Grammar Checking
	c.checkGrammarModifiers(node)
	if expr := node.AsTypeParameterDeclaration().Expression; expr != nil {
		c.grammarErrorOnFirstToken(expr, diagnostics.Type_expected)
	}
	tpNode := node.AsTypeParameterDeclaration()
	c.checkSourceElement(tpNode.Constraint)
	c.checkSourceElement(tpNode.DefaultType)
	typeParameter := c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(node))
	// Resolve base constraint to reveal circularity errors
	c.getBaseConstraintOfType(typeParameter)
	if c.getResolvedTypeParameterDefault(typeParameter) == c.circularConstraintType {
		c.error(tpNode.DefaultType, diagnostics.Type_parameter_0_has_a_circular_default, c.TypeToString(typeParameter))
	}
	constraintType := c.getConstraintOfTypeParameter(typeParameter)
	defaultType := c.getDefaultFromTypeParameter(typeParameter)
	if constraintType != nil && defaultType != nil {
		c.checkTypeAssignableTo(defaultType, c.getTypeWithThisArgument(c.instantiateType(constraintType, newSimpleTypeMapper(typeParameter, defaultType)), defaultType, false), tpNode.DefaultType, diagnostics.Type_0_does_not_satisfy_the_constraint_1)
	}
	c.checkTypeNameIsReserved(node.Name(), diagnostics.Type_parameter_name_cannot_be_0)
	c.checkNodeDeferred(node)
}

func (c *Checker) checkTypeParameterDeferred(node *ast.Node) {
	if ast.IsInterfaceDeclaration(node.Parent) || ast.IsClassLike(node.Parent) || ast.IsTypeOrJSTypeAliasDeclaration(node.Parent) {
		typeParameter := c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(node))
		modifiers := c.getTypeParameterModifiers(typeParameter) & (ast.ModifierFlagsIn | ast.ModifierFlagsOut)
		if modifiers != 0 {
			symbol := c.getSymbolOfDeclaration(node.Parent)
			if ast.IsTypeOrJSTypeAliasDeclaration(node.Parent) && c.getDeclaredTypeOfSymbol(symbol).objectFlags&(ObjectFlagsAnonymous|ObjectFlagsMapped) == 0 {
				c.error(node, diagnostics.Variance_annotations_are_only_supported_in_type_aliases_for_object_function_constructor_and_mapped_types)
			} else if modifiers == ast.ModifierFlagsIn || modifiers == ast.ModifierFlagsOut {
				if tr := c.tracer; tr != nil {
					defer tr.Push(tracing.PhaseCheckTypes, "checkTypeParameterDeferred", map[string]any{"parent": c.getDeclaredTypeOfSymbol(symbol).id, "id": typeParameter.id}, false)()
				}
				source := c.createMarkerType(symbol, typeParameter, core.IfElse(modifiers == ast.ModifierFlagsOut, c.markerSubTypeForCheck, c.markerSuperTypeForCheck))
				target := c.createMarkerType(symbol, typeParameter, core.IfElse(modifiers == ast.ModifierFlagsOut, c.markerSuperTypeForCheck, c.markerSubTypeForCheck))
				saveVarianceTypeParameter := typeParameter
				c.varianceTypeParameter = typeParameter
				c.checkTypeAssignableTo(source, target, node, diagnostics.Type_0_is_not_assignable_to_type_1_as_implied_by_variance_annotation)
				c.varianceTypeParameter = saveVarianceTypeParameter
			}
		}
	}
}

func (c *Checker) shouldCheckErasableSyntax(node *ast.Node) bool {
	return c.compilerOptions.ErasableSyntaxOnly.IsTrue() && !ast.IsInJSFile(node)
}

func (c *Checker) checkParameter(node *ast.Node) {
	// Grammar checking
	// It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the
	// Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code
	// or if its FunctionBody is strict code(11.1.5).
	c.checkGrammarModifiers(node)
	c.checkVariableLikeDeclaration(node)
	fn := ast.GetContainingFunction(node)
	var paramName string
	if node.Name() != nil && ast.IsIdentifier(node.Name()) {
		paramName = node.Name().Text()
	}
	if ast.HasSyntacticModifier(node, ast.ModifierFlagsParameterPropertyModifier) {
		if c.shouldCheckErasableSyntax(node) {
			c.error(node, diagnostics.This_syntax_is_not_allowed_when_erasableSyntaxOnly_is_enabled)
		}
		if !(ast.IsConstructorDeclaration(fn) && ast.NodeIsPresent(fn.Body())) {
			c.error(node, diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation)
		}
		if ast.IsConstructorDeclaration(fn) && paramName == "constructor" {
			c.error(node.Name(), diagnostics.X_constructor_cannot_be_used_as_a_parameter_property_name)
		}
	}
	if node.Initializer() == nil && isOptionalDeclaration(node) && ast.IsBindingPattern(node.Name()) && fn.Body() != nil {
		c.error(node, diagnostics.A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature)
	}
	if paramName == "this" || paramName == "new" {
		if slices.Index(fn.Parameters(), node) != 0 {
			c.error(node, diagnostics.A_0_parameter_must_be_the_first_parameter, paramName)
		}
		if ast.IsConstructorDeclaration(fn) || ast.IsConstructSignatureDeclaration(fn) || ast.IsConstructorTypeNode(fn) {
			c.error(node, diagnostics.A_constructor_cannot_have_a_this_parameter)
		}
		if ast.IsArrowFunction(fn) {
			c.error(node, diagnostics.An_arrow_function_cannot_have_a_this_parameter)
		}
		if ast.IsAccessor(fn) {
			c.error(node, diagnostics.X_get_and_set_accessors_cannot_declare_this_parameters)
		}
	}
	// Only check rest parameter type if it's not a binding pattern. Since binding patterns are
	// not allowed in a rest parameter, we already have an error from checkGrammarParameterList.
	if hasDotDotDotToken(node) && !ast.IsBindingPattern(node.Name()) && !c.isTypeAssignableTo(c.getReducedType(c.getTypeOfSymbol(node.Symbol())), c.anyReadonlyArrayType) {
		c.error(node, diagnostics.A_rest_parameter_must_be_of_an_array_type)
	}
}

func (c *Checker) checkPropertyDeclaration(node *ast.Node) {
	// Grammar checking
	if !c.checkGrammarModifiers(node) && !c.checkGrammarProperty(node) {
		c.checkGrammarComputedPropertyName(node.Name())
	}
	c.checkVariableLikeDeclaration(node)
	c.setNodeLinksForPrivateIdentifierScope(node)
	// property signatures already report "initializer not allowed in ambient context" elsewhere
	if ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract) && ast.IsPropertyDeclaration(node) {
		if node.Initializer() != nil {
			c.error(node, diagnostics.Property_0_cannot_have_an_initializer_because_it_is_marked_abstract, scanner.DeclarationNameToString(node.Name()))
		}
	}
}

func (c *Checker) checkPropertySignature(node *ast.Node) {
	if ast.IsPrivateIdentifier(node.AsPropertySignatureDeclaration().Name()) {
		c.error(node, diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies)
	}
	c.checkPropertyDeclaration(node)
}

func (c *Checker) checkSignatureDeclaration(node *ast.Node) {
	// Grammar checking
	switch node.Kind {
	case ast.KindIndexSignature:
		c.checkGrammarIndexSignature(node.AsIndexSignatureDeclaration())
	case ast.KindFunctionType, ast.KindFunctionDeclaration, ast.KindConstructorType, ast.KindCallSignature, ast.KindConstructor, ast.KindConstructSignature:
		c.checkGrammarFunctionLikeDeclaration(node)
	}
	functionFlags := ast.GetFunctionFlags(node)
	if functionFlags&ast.FunctionFlagsInvalid == 0 {
		// Async generators prior to ES2018 require the __await and __asyncGenerator helpers
		if functionFlags&ast.FunctionFlagsAsyncGenerator == ast.FunctionFlagsAsyncGenerator &&
			c.languageVersion < LanguageFeatureMinimumTarget.AsyncGenerators {
			c.checkExternalEmitHelpers(node, ExternalEmitHelpersAsyncGeneratorIncludes)
		}
		if functionFlags&ast.FunctionFlagsAsyncGenerator == ast.FunctionFlagsAsync && c.languageVersion < LanguageFeatureMinimumTarget.AsyncFunctions {
			c.checkExternalEmitHelpers(node, ExternalEmitHelpersAwaiter)
		}
	}
	c.checkTypeParameters(node.TypeParameters())
	c.checkUnmatchedJSDocParameters(node)
	c.checkSourceElements(node.Parameters())
	returnTypeNode := node.Type()
	if returnTypeNode != nil {
		c.checkSourceElement(returnTypeNode)
	}
	if c.noImplicitAny && returnTypeNode == nil {
		switch node.Kind {
		case ast.KindConstructSignature:
			c.error(node, diagnostics.Construct_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type)
		case ast.KindCallSignature:
			c.error(node, diagnostics.Call_signature_which_lacks_return_type_annotation_implicitly_has_an_any_return_type)
		}
	}
	if returnTypeNode != nil {
		if (functionFlags & (ast.FunctionFlagsInvalid | ast.FunctionFlagsGenerator)) == ast.FunctionFlagsGenerator {
			returnType := c.getTypeFromTypeNode(returnTypeNode)
			if returnType == c.voidType {
				c.error(returnTypeNode, diagnostics.A_generator_cannot_have_a_void_type_annotation)
			} else {
				c.checkGeneratorInstantiationAssignabilityToReturnType(returnType, functionFlags, returnTypeNode)
			}
		} else if (functionFlags & ast.FunctionFlagsAsyncGenerator) == ast.FunctionFlagsAsync {
			c.checkAsyncFunctionReturnType(node, returnTypeNode)
		}
	}
	if !ast.IsIndexSignatureDeclaration(node) {
		c.registerForUnusedIdentifiersCheck(node)
	}
}

// Checks the return type of an async function to ensure it is a compatible
// Promise implementation.
//
// This checks that an async function has a valid Promise-compatible return type.
// An async function has a valid Promise-compatible return type if the resolved value
// of the return type has a construct signature that takes in an `initializer` function
// that in turn supplies a `resolve` function as one of its arguments and results in an
// object with a callable `then` signature.
func (c *Checker) checkAsyncFunctionReturnType(node *ast.Node, returnTypeNode *ast.Node) {
	returnType := c.getTypeFromTypeNode(returnTypeNode)
	if c.isErrorType(returnType) {
		return
	}
	globalPromiseType := c.getGlobalPromiseTypeChecked()
	if globalPromiseType != c.emptyGenericType && !c.isReferenceToType(returnType, globalPromiseType) {
		// The promise type was not a valid type reference to the global promise type, so we
		// report an error and return the unknown type.
		c.error(returnTypeNode, diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type_Did_you_mean_to_write_Promise_0, c.TypeToString(core.OrElse(c.getAwaitedTypeNoAlias(returnType), c.voidType)))
		return
	}
	c.checkAwaitedType(returnType, false /*withAlias*/, node, diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
}

func (c *Checker) checkMethodDeclaration(node *ast.Node) {
	// Grammar checking
	if !c.checkGrammarMethod(node) {
		c.checkGrammarComputedPropertyName(node.Name())
		if ast.IsMethodDeclaration(node) && node.AsMethodDeclaration().AsteriskToken != nil && ast.IsIdentifier(node.Name()) && node.Name().Text() == "constructor" {
			c.error(node.Name(), diagnostics.Class_constructor_may_not_be_a_generator)
		}
	}
	// Grammar checking for modifiers is done inside the function checkGrammarFunctionLikeDeclaration
	c.checkFunctionOrMethodDeclaration(node)
	// method signatures already report "implementation not allowed in ambient context" elsewhere
	if ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract) && ast.IsMethodDeclaration(node) && node.Body() != nil {
		c.error(node, diagnostics.Method_0_cannot_have_an_implementation_because_it_is_marked_abstract, scanner.DeclarationNameToString(node.Name()))
	}
	// Private named methods are only allowed in class declarations
	if ast.IsPrivateIdentifier(node.Name()) && ast.GetContainingClass(node) == nil {
		c.error(node, diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies)
	}
	c.setNodeLinksForPrivateIdentifierScope(node)
}

func (c *Checker) checkClassStaticBlockDeclaration(node *ast.Node) {
	// Grammar checking
	c.checkGrammarModifiers(node)
	node.ForEachChild(c.checkSourceElement)
}

func (c *Checker) checkConstructorDeclaration(node *ast.Node) {
	// Grammar check on signature of constructor and modifier of the constructor is done in checkSignatureDeclaration function.
	c.checkSignatureDeclaration(node)
	// Grammar check for checking only related to constructorDeclaration
	ctor := node.AsConstructorDeclaration()
	if !c.checkGrammarConstructorTypeParameters(ctor) {
		c.checkGrammarConstructorTypeAnnotation(ctor)
	}
	c.checkSourceElement(node.Body())
	symbol := c.getSymbolOfDeclaration(node)
	c.checkFunctionOrConstructorSymbol(symbol)
	// exit early in the case of signature - super checks are not relevant to them
	if ast.NodeIsMissing(node.Body()) {
		return
	}
	// TS 1.0 spec (April 2014): 8.3.2
	// Constructors of classes with no extends clause may not contain super calls, whereas
	// constructors of derived classes must contain at least one super call somewhere in their function body.
	containingClassDecl := node.Parent
	if ast.GetExtendsHeritageClauseElement(containingClassDecl) == nil {
		return
	}
	classExtendsNull := c.classDeclarationExtendsNull(containingClassDecl)
	superCall := c.findFirstSuperCall(node.Body())
	if superCall != nil {
		if classExtendsNull {
			c.error(superCall, diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null)
		}
		// A super call must be root-level in a constructor if both of the following are true:
		// - The containing class is a derived class.
		// - The constructor declares parameter properties
		//   or the containing class declares instance member variables with initializers.
		superCallShouldBeRootLevel := !c.emitStandardClassFields &&
			(core.Some(node.Parent.Members(), isInstancePropertyWithInitializerOrPrivateIdentifierProperty) ||
				core.Some(node.Parameters(), func(p *ast.Node) bool {
					return ast.HasSyntacticModifier(p, ast.ModifierFlagsParameterPropertyModifier)
				}))
		if superCallShouldBeRootLevel {
			// Until we have better flow analysis, it is an error to place the super call within any kind of block or conditional
			// See GH #8277
			if !superCallIsRootLevelInConstructor(superCall, node.Body()) {
				c.error(superCall, diagnostics.A_super_call_must_be_a_root_level_statement_within_a_constructor_of_a_derived_class_that_contains_initialized_properties_parameter_properties_or_private_identifiers)
			} else {
				var superCallStatement *ast.Node
				for _, statement := range node.Body().Statements() {
					if ast.IsExpressionStatement(statement) && isSuperCall(ast.SkipOuterExpressions(statement.Expression(), ast.OEKAll)) {
						superCallStatement = statement
						break
					}
					if nodeImmediatelyReferencesSuperOrThis(statement) {
						break
					}
				}
				// Until we have better flow analysis, it is an error to place the super call within any kind of block or conditional
				// See GH #8277
				if superCallStatement == nil {
					c.error(node, diagnostics.A_super_call_must_be_the_first_statement_in_the_constructor_to_refer_to_super_or_this_when_a_derived_class_contains_initialized_properties_parameter_properties_or_private_identifiers)
				}
			}
		}
	} else if !classExtendsNull {
		c.error(node, diagnostics.Constructors_for_derived_classes_must_contain_a_super_call)
	}
}

func (c *Checker) findFirstSuperCall(node *ast.Node) *ast.Node {
	var superCall *ast.Node
	var visit func(node *ast.Node) bool
	visit = func(node *ast.Node) bool {
		switch {
		case isSuperCall(node):
			superCall = node
			return true
		case ast.IsFunctionLike(node):
			return false
		}
		return node.ForEachChild(visit)
	}
	visit(node)
	return superCall
}

func isInstancePropertyWithInitializerOrPrivateIdentifierProperty(n *ast.Node) bool {
	return ast.IsPrivateIdentifierClassElementDeclaration(n) || ast.IsPropertyDeclaration(n) && !ast.IsStatic(n) && n.Initializer() != nil
}

func superCallIsRootLevelInConstructor(superCall *ast.Node, body *ast.Node) bool {
	superCallParent := ast.WalkUpParenthesizedExpressions(superCall.Parent)
	return ast.IsExpressionStatement(superCallParent) && superCallParent.Parent == body
}

func nodeImmediatelyReferencesSuperOrThis(node *ast.Node) bool {
	switch node.Kind {
	case ast.KindSuperKeyword, ast.KindThisKeyword:
		return true
	case ast.KindArrowFunction, ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindPropertyDeclaration:
		return false
	case ast.KindBlock:
		switch node.Parent.Kind {
		case ast.KindConstructor, ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
			return false
		}
	}
	return node.ForEachChild(nodeImmediatelyReferencesSuperOrThis)
}

func (c *Checker) checkAccessorDeclaration(node *ast.Node) {
	// Grammar checking accessors
	if !c.checkGrammarFunctionLikeDeclaration(node) && !c.checkGrammarAccessor(node) {
		c.checkGrammarComputedPropertyName(node.Name())
	}
	name := node.Name()
	if ast.IsIdentifier(name) && name.Text() == "constructor" && ast.IsClassLike(node.Parent) {
		c.error(node.Name(), diagnostics.Class_constructor_may_not_be_an_accessor)
	}
	c.checkDecorators(node)
	c.checkSignatureDeclaration(node)
	if ast.IsGetAccessorDeclaration(node) {
		if node.Flags&ast.NodeFlagsAmbient == 0 && ast.NodeIsPresent(node.Body()) && node.Flags&ast.NodeFlagsHasImplicitReturn != 0 {
			if node.Flags&ast.NodeFlagsHasExplicitReturn == 0 {
				c.error(name, diagnostics.A_get_accessor_must_return_a_value)
			}
		}
	}
	// Do not use hasDynamicName here, because that returns false for well known symbols.
	// We want to perform checkComputedPropertyName for all computed properties, including
	// well known symbols.
	if ast.IsComputedPropertyName(name) {
		c.checkComputedPropertyName(name)
	}
	if c.hasBindableName(node) {
		// TypeScript 1.0 spec (April 2014): 8.4.3
		// Accessors for the same member name must specify the same accessibility.
		symbol := c.getSymbolOfDeclaration(node)
		getter := ast.GetDeclarationOfKind(symbol, ast.KindGetAccessor)
		setter := ast.GetDeclarationOfKind(symbol, ast.KindSetAccessor)
		if getter != nil && setter != nil && c.nodeLinks.Get(getter).flags&NodeCheckFlagsTypeChecked == 0 {
			c.nodeLinks.Get(getter).flags |= NodeCheckFlagsTypeChecked
			getterFlags := getter.ModifierFlags()
			setterFlags := setter.ModifierFlags()
			if (getterFlags & ast.ModifierFlagsAbstract) != (setterFlags & ast.ModifierFlagsAbstract) {
				c.error(getter.Name(), diagnostics.Accessors_must_both_be_abstract_or_non_abstract)
				c.error(setter.Name(), diagnostics.Accessors_must_both_be_abstract_or_non_abstract)
			}
			if ((getterFlags&ast.ModifierFlagsProtected != 0) && setterFlags&(ast.ModifierFlagsProtected|ast.ModifierFlagsPrivate) == 0) || ((getterFlags&ast.ModifierFlagsPrivate != 0) && setterFlags&ast.ModifierFlagsPrivate == 0) {
				c.error(getter.Name(), diagnostics.A_get_accessor_must_be_at_least_as_accessible_as_the_setter)
				c.error(setter.Name(), diagnostics.A_get_accessor_must_be_at_least_as_accessible_as_the_setter)
			}
		}
	}
	returnType := c.getTypeOfAccessors(c.getSymbolOfDeclaration(node))
	if node.Kind == ast.KindGetAccessor {
		c.checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnType)
	}
	c.checkSourceElement(node.Body())
	c.setNodeLinksForPrivateIdentifierScope(node)
}

func (c *Checker) checkTypeReferenceNode(node *ast.Node) {
	c.checkGrammarTypeArguments(node, node.TypeArgumentList())
	if ast.IsTypeReferenceNode(node) && node.Flags&ast.NodeFlagsJSDoc == 0 {
		data := node.AsTypeReferenceNode()
		if data.TypeArguments != nil && data.TypeName.End() != data.TypeArguments.Pos() {
			// If there was a token between the type name and the type arguments, check if it was a DotToken
			sourceFile := ast.GetSourceFileOfNode(node)
			if scanner.ScanTokenAtPosition(sourceFile, data.TypeName.End()) == ast.KindDotToken {
				c.grammarErrorAtPos(node, scanner.SkipTrivia(sourceFile.Text(), data.TypeName.End()), 1, diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments)
			}
		}
	}
	c.checkSourceElements(node.TypeArguments())
	c.checkTypeReferenceOrImport(node)
}

func (c *Checker) checkTypeReferenceOrImport(node *ast.Node) {
	t := c.getTypeFromTypeNode(node)
	if !c.isErrorType(t) {
		if len(node.TypeArguments()) != 0 {
			typeParameters := c.getTypeParametersForTypeReferenceOrImport(node)
			if len(typeParameters) != 0 {
				c.checkTypeArgumentConstraints(node, typeParameters)
			}
		}
		symbol := c.getResolvedSymbolOrNil(node)
		if symbol != nil {
			if core.Some(symbol.Declarations, func(d *ast.Node) bool { return ast.IsTypeDeclaration(d) && c.IsDeprecatedDeclaration(d) }) {
				c.addDeprecatedSuggestion(c.getDeprecatedSuggestionNode(node), symbol.Declarations, symbol.Name)
			}
		}
	}
}

func (c *Checker) checkTypeArgumentConstraints(node *ast.Node, typeParameters []*Type) bool {
	var typeArguments []*Type
	var mapper *TypeMapper
	result := true
	for i, typeParameter := range typeParameters {
		constraint := c.getConstraintOfTypeParameter(typeParameter)
		if constraint != nil {
			if typeArguments == nil {
				typeArguments = c.getEffectiveTypeArguments(node, typeParameters)
				mapper = newTypeMapper(typeParameters, typeArguments)
			}
			result = result && c.checkTypeAssignableTo(typeArguments[i], c.instantiateType(constraint, mapper), core.ElementOrNil(node.TypeArguments(), i), diagnostics.Type_0_does_not_satisfy_the_constraint_1)
		}
	}
	return result
}

func (c *Checker) getDeprecatedSuggestionNode(node *ast.Node) *ast.Node {
	node = ast.SkipParentheses(node)
	switch node.Kind {
	case ast.KindCallExpression, ast.KindDecorator, ast.KindNewExpression:
		return c.getDeprecatedSuggestionNode(node.Expression())
	case ast.KindTaggedTemplateExpression:
		return c.getDeprecatedSuggestionNode(node.AsTaggedTemplateExpression().Tag)
	case ast.KindJsxOpeningElement, ast.KindJsxSelfClosingElement:
		return c.getDeprecatedSuggestionNode(node.TagName())
	case ast.KindElementAccessExpression:
		return node.AsElementAccessExpression().ArgumentExpression
	case ast.KindPropertyAccessExpression:
		return node.Name()
	case ast.KindTypeReference:
		typeName := node.AsTypeReferenceNode().TypeName
		if ast.IsQualifiedName(typeName) {
			return typeName.AsQualifiedName().Right
		}
	}
	return node
}

func (c *Checker) checkTypePredicate(node *ast.Node) {
	parent := c.getTypePredicateParent(node)
	if parent == nil {
		// The parent must not be valid.
		c.error(node, diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods)
		return
	}
	signature := c.getSignatureFromDeclaration(parent)
	typePredicate := c.getTypePredicateOfSignature(signature)
	if typePredicate == nil {
		return
	}
	c.checkSourceElement(node.Type())
	parameterName := node.AsTypePredicateNode().ParameterName
	if typePredicate.kind != TypePredicateKindThis && typePredicate.kind != TypePredicateKindAssertsThis {
		if typePredicate.parameterIndex >= 0 {
			if signatureHasRestParameter(signature) && int(typePredicate.parameterIndex) == len(signature.parameters)-1 {
				c.error(parameterName, diagnostics.A_type_predicate_cannot_reference_a_rest_parameter)
			} else {
				if typePredicate.t != nil {
					var diags []*ast.Diagnostic
					if !c.checkTypeAssignableToEx(typePredicate.t, c.getTypeOfSymbol(signature.parameters[typePredicate.parameterIndex]), node.Type(), nil /*headMessage*/, &diags) {
						c.addDiagnostic(ast.NewDiagnosticChain(diags[0], diagnostics.A_type_predicate_s_type_must_be_assignable_to_its_parameter_s_type))
					}
				}
			}
		} else if parameterName != nil {
			hasReportedError := false
			for _, param := range parent.Parameters() {
				name := param.Name()
				if ast.IsBindingPattern(name) && c.checkIfTypePredicateVariableIsDeclaredInBindingPattern(name, parameterName, typePredicate.parameterName) {
					hasReportedError = true
					break
				}
			}
			if !hasReportedError {
				c.error(parameterName, diagnostics.Cannot_find_parameter_0, typePredicate.parameterName)
			}
		}
	}
}

func (c *Checker) getTypePredicateParent(node *ast.Node) *ast.SignatureDeclaration {
	parent := node.Parent
	switch parent.Kind {
	case ast.KindArrowFunction, ast.KindCallSignature, ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindFunctionType,
		ast.KindMethodDeclaration, ast.KindMethodSignature:
		if node == parent.Type() {
			return parent
		}
	}
	return nil
}

func (c *Checker) checkIfTypePredicateVariableIsDeclaredInBindingPattern(pattern *ast.Node, predicateVariableNode *ast.Node, predicateVariableName string) bool {
	for _, element := range pattern.Elements() {
		name := element.Name()
		if name == nil {
			continue
		}
		if ast.IsIdentifier(name) && name.Text() == predicateVariableName {
			c.error(predicateVariableNode, diagnostics.A_type_predicate_cannot_reference_element_0_in_a_binding_pattern, predicateVariableName)
			return true
		}
		if ast.IsArrayBindingPattern(name) || ast.IsObjectBindingPattern(name) {
			if c.checkIfTypePredicateVariableIsDeclaredInBindingPattern(name, predicateVariableNode, predicateVariableName) {
				return true
			}
		}
	}
	return false
}

func (c *Checker) checkTypeQuery(node *ast.Node) {
	c.getTypeFromTypeQueryNode(node)
}

func (c *Checker) checkTypeLiteral(node *ast.Node) {
	c.checkSourceElements(node.Members())
	t := c.getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node)
	c.checkIndexConstraints(t, t.symbol, false /*isStaticIndex*/)
	c.checkTypeForDuplicateIndexSignatures(node)
	c.checkObjectTypeForDuplicateDeclarations(node, false /*checkPrivateNames*/)
}

func (c *Checker) checkObjectTypeForDuplicateDeclarations(node *ast.Node, checkPrivateNames bool) {
	var instanceNames map[string]int
	var staticNames map[string]int
	var privateNames map[string]int
	nodeInAmbientContext := node.Flags&ast.NodeFlagsAmbient != 0
	checkPropertyOrAccessor := func(symbol *ast.Symbol, kind int, isStatic bool) {
		if len(symbol.Declarations) > 1 {
			var names map[string]int
			if isStatic {
				if staticNames == nil {
					staticNames = make(map[string]int)
				}
				names = staticNames
			} else {
				if instanceNames == nil {
					instanceNames = make(map[string]int)
				}
				names = instanceNames
			}
			state := names[symbol.Name]
			switch {
			case state == 0:
				// On first occurrence just record the kind
				names[symbol.Name] = kind
			case state == 1 || state == 2 && kind != 2:
				// Error on second property or combination of property and accessor
				c.reportDuplicateMemberErrors(node, symbol.Name, true, isStatic, diagnostics.Duplicate_identifier_0)
				// Record that errors have been reported
				names[symbol.Name] = 3
			}
		}
	}
	for _, member := range node.Members() {
		if ast.IsConstructorDeclaration(member) {
			for _, param := range member.Parameters() {
				if ast.IsParameterPropertyDeclaration(param, member) && !ast.IsBindingPattern(param.Name()) {
					checkPropertyOrAccessor(c.getSymbolOfDeclaration(param), 1, false /*isStatic*/)
				}
			}
		} else {
			symbol := c.getSymbolOfDeclaration(member)
			isStatic := ast.HasStaticModifier(member)
			// In non-ambient contexts, check that static members are not named 'prototype'.
			if !nodeInAmbientContext && isStatic && symbol != nil && symbol.Name == "prototype" {
				c.error(member.Name(), diagnostics.Static_property_0_conflicts_with_built_in_property_Function_0_of_constructor_function_1, symbol.Name, c.symbolToString(c.getSymbolOfDeclaration(node)))
			}
			// Check that this object type declaration doesn't contain multiple declarations of the same property,
			// or accessor and property declarations with the same name.
			if ast.IsPropertyDeclaration(member) && !ast.HasAccessorModifier(member) || ast.IsPropertySignatureDeclaration(member) {
				checkPropertyOrAccessor(symbol, 1, isStatic)
			} else if ast.IsAccessor(member) || ast.IsPropertyDeclaration(member) && ast.HasAccessorModifier(member) {
				checkPropertyOrAccessor(symbol, 2, isStatic)
			}
			// Check that each private identifier is used only for instance members or only for static members. It is an
			// error for an instance and a static member to have the same private identifier.
			if checkPrivateNames && member.Name() != nil && ast.IsPrivateIdentifier(member.Name()) {
				if flags := privateNames[symbol.Name]; flags != 3 {
					flags |= core.IfElse(ast.IsStatic(member), 2, 1)
					if privateNames == nil {
						privateNames = make(map[string]int)
					}
					privateNames[symbol.Name] = flags
					if flags == 3 {
						c.reportDuplicateMemberErrors(node, symbol.Name, false, false, diagnostics.Duplicate_identifier_0_Static_and_instance_elements_cannot_share_the_same_private_name)
					}
				}
			}
		}
	}
}

func (c *Checker) reportDuplicateMemberErrors(node *ast.Node, name string, checkStatic bool, isStatic bool, message *diagnostics.Message) {
	for _, member := range node.Members() {
		if ast.IsConstructorDeclaration(member) {
			for _, param := range member.Parameters() {
				if ast.IsParameterPropertyDeclaration(param, member) && !ast.IsBindingPattern(param.Name()) {
					if symbol := c.getSymbolOfDeclaration(param); symbol.Name == name {
						c.error(param.Name(), message, c.symbolToString(symbol))
					}
				}
			}
		} else if symbol := c.getSymbolOfDeclaration(member); symbol != nil && symbol.Name == name && (!checkStatic || isStatic == ast.IsStatic(member)) {
			c.error(member.Name(), message, c.symbolToString(symbol))
		}
	}
}

func (c *Checker) checkArrayType(node *ast.Node) {
	c.checkSourceElement(node.AsArrayTypeNode().ElementType)
}

func (c *Checker) checkTupleType(node *ast.Node) {
	seenOptionalElement := false
	seenRestElement := false
	elements := node.Elements()
	for _, e := range elements {
		flags := c.getTupleElementFlags(e)
		if flags&ElementFlagsVariadic != 0 {
			t := c.getTypeFromTypeNode(e.Type())
			if !c.isArrayLikeType(t) {
				c.error(e, diagnostics.A_rest_element_type_must_be_an_array_type)
				break
			}
			if c.isArrayType(t) || isTupleType(t) && t.TargetTupleType().combinedFlags&ElementFlagsRest != 0 {
				flags |= ElementFlagsRest
			}
		}
		if flags&ElementFlagsRest != 0 {
			if seenRestElement {
				c.grammarErrorOnNode(e, diagnostics.A_rest_element_cannot_follow_another_rest_element)
				break
			}
			seenRestElement = true
		} else if flags&ElementFlagsOptional != 0 {
			if seenRestElement {
				c.grammarErrorOnNode(e, diagnostics.An_optional_element_cannot_follow_a_rest_element)
				break
			}
			seenOptionalElement = true
		} else if flags&ElementFlagsRequired != 0 && seenOptionalElement {
			c.grammarErrorOnNode(e, diagnostics.A_required_element_cannot_follow_an_optional_element)
			break
		}
	}
	c.checkSourceElements(elements)
	c.getTypeFromTypeNode(node)
}

func (c *Checker) checkUnionOrIntersectionType(node *ast.Node) {
	node.ForEachChild(c.checkSourceElement)
	c.getTypeFromTypeNode(node)
}

func (c *Checker) checkThisType(node *ast.Node) {
	c.getTypeFromThisTypeNode(node)
}

func (c *Checker) checkTypeOperator(node *ast.Node) {
	c.checkGrammarTypeOperatorNode(node.AsTypeOperatorNode())
	c.checkSourceElement(node.Type())
}

func (c *Checker) checkConditionalType(node *ast.Node) {
	node.ForEachChild(c.checkSourceElement)
}

func (c *Checker) checkInferType(node *ast.Node) {
	if ast.FindAncestor(node, func(n *ast.Node) bool {
		return n.Parent != nil && n.Parent.Kind == ast.KindConditionalType && n.Parent.AsConditionalTypeNode().ExtendsType == n
	}) == nil {
		c.grammarErrorOnNode(node, diagnostics.X_infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type)
	}
	typeParameterDeclarationNode := node.AsInferTypeNode().TypeParameter
	c.checkSourceElement(typeParameterDeclarationNode)
	symbol := c.getSymbolOfDeclaration(typeParameterDeclarationNode)
	if len(symbol.Declarations) > 1 {
		links := c.declaredTypeLinks.Get(symbol)
		if !links.typeParametersChecked {
			links.typeParametersChecked = true
			typeParameter := c.getDeclaredTypeOfTypeParameter(symbol)
			declarations := getDeclarationsOfKind(symbol, ast.KindTypeParameter)
			if !c.areTypeParametersIdentical(declarations, []*Type{typeParameter}, func(decl *ast.Node) []*ast.Node { return []*ast.Node{decl} }) {
				// Report an error on every conflicting declaration.
				name := c.symbolToString(symbol)
				for _, declaration := range declarations {
					c.error(declaration.Name(), diagnostics.All_declarations_of_0_must_have_identical_constraints, name)
				}
			}
		}
	}
	c.registerForUnusedIdentifiersCheck(node)
}

func (c *Checker) checkTemplateLiteralType(node *ast.Node) {
	for _, span := range node.AsTemplateLiteralTypeNode().TemplateSpans.Nodes {
		c.checkSourceElement(span.Type())
		t := c.getTypeFromTypeNode(span.Type())
		c.checkTypeAssignableTo(t, c.templateConstraintType, span.Type(), nil)
	}
	c.getTypeFromTypeNode(node)
}

func (c *Checker) checkImportType(node *ast.Node) {
	c.checkSourceElement(node.AsImportTypeNode().Argument)
	if attributes := node.AsImportTypeNode().Attributes; attributes != nil {
		c.getResolutionModeOverride(attributes.AsImportAttributes(), true /*reportErrors*/)
	}
	c.checkTypeReferenceOrImport(node)
}

func (c *Checker) getResolutionModeOverride(node *ast.ImportAttributes, reportErrors bool) core.ResolutionMode {
	if len(node.Attributes.Nodes) != 1 {
		if reportErrors {
			c.grammarErrorOnNode(node.AsNode(), diagnostics.Type_import_attributes_should_have_exactly_one_key_resolution_mode_with_value_import_or_require)
		}
		return core.ResolutionModeNone
	}
	elem := node.Attributes.Nodes[0]
	if !ast.IsStringLiteralLike(elem.Name()) {
		return core.ResolutionModeNone
	}
	if elem.Name().Text() != "resolution-mode" {
		if reportErrors {
			c.grammarErrorOnNode(elem.Name(), diagnostics.X_resolution_mode_is_the_only_valid_key_for_type_import_attributes)
		}
		return core.ResolutionModeNone
	}
	value := elem.AsImportAttribute().Value
	if !ast.IsStringLiteralLike(value) {
		return core.ResolutionModeNone
	}
	if value.Text() != "import" && value.Text() != "require" {
		if reportErrors {
			c.grammarErrorOnNode(value, diagnostics.X_resolution_mode_should_be_either_require_or_import)
		}
		return core.ResolutionModeNone
	}
	if value.Text() == "import" {
		return core.ResolutionModeESM
	}
	return core.ResolutionModeCommonJS
}

func (c *Checker) checkNamedTupleMember(node *ast.Node) {
	tupleMember := node.AsNamedTupleMember()
	if tupleMember.DotDotDotToken != nil && tupleMember.QuestionToken != nil {
		c.grammarErrorOnNode(node, diagnostics.A_tuple_member_cannot_be_both_optional_and_rest)
	}
	if tupleMember.Type.Kind == ast.KindOptionalType {
		c.grammarErrorOnNode(tupleMember.Type, diagnostics.A_labeled_tuple_element_is_declared_as_optional_with_a_question_mark_after_the_name_and_before_the_colon_rather_than_after_the_type)
	}
	if tupleMember.Type.Kind == ast.KindRestType {
		c.grammarErrorOnNode(tupleMember.Type, diagnostics.A_labeled_tuple_element_is_declared_as_rest_with_a_before_the_name_rather_than_before_the_type)
	}
	c.checkSourceElement(node.Type())
	c.getTypeFromTypeNode(node)
}

func (c *Checker) checkIndexedAccessType(node *ast.Node) {
	node.ForEachChild(c.checkSourceElement)
	c.checkIndexedAccessIndexType(c.getTypeFromIndexedAccessTypeNode(node), node)
}

func (c *Checker) checkMappedType(node *ast.Node) {
	mappedTypeNode := node.AsMappedTypeNode()
	c.checkGrammarMappedType(mappedTypeNode)
	c.checkSourceElement(mappedTypeNode.TypeParameter)
	c.checkSourceElement(mappedTypeNode.NameType)
	c.checkSourceElement(mappedTypeNode.Type)
	if mappedTypeNode.Type == nil {
		c.reportImplicitAny(node, c.anyType, WideningKindNormal)
	}
	t := c.getTypeFromMappedTypeNode(node)
	nameType := c.getNameTypeFromMappedType(t)
	if nameType != nil {
		c.checkTypeAssignableTo(nameType, c.stringNumberSymbolType, mappedTypeNode.NameType, nil)
	} else {
		constraintType := c.getConstraintTypeFromMappedType(t)
		c.checkTypeAssignableTo(constraintType, c.stringNumberSymbolType, mappedTypeNode.TypeParameter.AsTypeParameterDeclaration().Constraint, nil)
	}
}

func (c *Checker) checkFunctionDeclaration(node *ast.Node) {
	c.checkFunctionOrMethodDeclaration(node)
	c.checkGrammarForGenerator(node)
	c.checkCollisionsForDeclarationName(node, node.Name())
}

func (c *Checker) checkFunctionOrMethodDeclaration(node *ast.Node) {
	c.checkDecorators(node)
	c.checkSignatureDeclaration(node)
	functionFlags := ast.GetFunctionFlags(node)
	// Do not use hasDynamicName here, because that returns false for well known symbols.
	// We want to perform checkComputedPropertyName for all computed properties, including
	// well known symbols.
	if node.Name() != nil && ast.IsComputedPropertyName(node.Name()) {
		// This check will account for methods in class/interface declarations,
		// as well as accessors in classes/object literals
		c.checkComputedPropertyName(node.Name())
	}
	if c.hasBindableName(node) {
		// first we want to check the local symbol that contain this declaration
		// - if node.localSymbol !== undefined - this is current declaration is exported and localSymbol points to the local symbol
		// - if node.localSymbol === undefined - this node is non-exported so we can just pick the result of getSymbolOfNode
		symbol := c.getSymbolOfDeclaration(node)
		localSymbol := core.OrElse(node.LocalSymbol(), symbol)
		// Since the javascript won't do semantic analysis like typescript, ignore javascript function
		// declarations so that redeclaring a function in a JS file is not reported as a duplicate.
		if node.Flags&ast.NodeFlagsJavaScriptFile == 0 {
			c.checkFunctionOrConstructorSymbol(localSymbol)
		}
		if symbol.Parent != nil {
			// run check on export symbol to check that modifiers agree across all exported declarations
			c.checkFunctionOrConstructorSymbol(symbol)
		}
	}
	body := node.Body()
	c.checkSourceElement(body)
	c.checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, c.getReturnTypeFromAnnotation(node))
	if node.FunctionLikeData().FullSignature != nil {
		if c.getContextualCallSignature(c.getTypeFromTypeNode(node.FunctionLikeData().FullSignature), node) == nil {
			c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_on_a_function_must_have_a_signature_with_the_correct_number_of_arguments)
		}
	}
	if node.Type() == nil {
		// Report an implicit any error if there is no body, no explicit return type, and node is not a private method
		// in an ambient context
		if ast.NodeIsMissing(body) && !isPrivateWithinAmbient(node) {
			c.reportImplicitAny(node, c.anyType, WideningKindNormal)
		}
		if functionFlags&ast.FunctionFlagsGenerator != 0 && ast.NodeIsPresent(body) {
			// A generator with a body and no type annotation can still cause errors. It can error if the
			// yielded values have no common supertype, or it can give an implicit any error if it has no
			// yielded values. The only way to trigger these errors is to try checking its return type.
			c.getReturnTypeOfSignature(c.getSignatureFromDeclaration(node))
		}
	}
}

func (c *Checker) checkFunctionOrConstructorSymbol(symbol *ast.Symbol) {
	// Only check the symbol once
	if links := c.valueSymbolLinks.Get(symbol); !links.functionOrConstructorChecked {
		links.functionOrConstructorChecked = true
		c.checkFunctionOrConstructorSymbolWorker(symbol)
	}
}

func (c *Checker) checkFunctionOrConstructorSymbolWorker(symbol *ast.Symbol) {
	flagsToCheck := ast.ModifierFlagsExport | ast.ModifierFlagsAmbient | ast.ModifierFlagsPrivate | ast.ModifierFlagsProtected | ast.ModifierFlagsAbstract
	someNodeFlags := ast.ModifierFlagsNone
	allNodeFlags := flagsToCheck
	someHaveQuestionToken := false
	allHaveQuestionToken := true
	hasOverloads := false
	var bodyDeclaration *ast.Node
	var lastSeenNonAmbientDeclaration *ast.Node
	var previousDeclaration *ast.Node
	declarations := symbol.Declarations
	isConstructor := symbol.Flags&ast.SymbolFlagsConstructor != 0
	duplicateFunctionDeclaration := false
	multipleConstructorImplementation := false
	hasNonAmbientClass := false
	var functionDeclarations []*ast.Node
	getCanonicalOverload := func(overloads []*ast.Node, implementation *ast.Node) *ast.Node {
		// Consider the canonical set of flags to be the flags of the bodyDeclaration or the first declaration
		// Error on all deviations from this canonical set of flags
		// The caveat is that if some overloads are defined in lib.d.ts, we don't want to
		// report the errors on those. To achieve this, we will say that the implementation is
		// the canonical signature only if it is in the same container as the first overload
		implementationSharesContainerWithFirstOverload := implementation != nil && implementation.Parent == overloads[0].Parent
		if implementationSharesContainerWithFirstOverload {
			return implementation
		}
		return overloads[0]
	}
	checkFlagAgreementBetweenOverloads := func(overloads []*ast.Node, implementation *ast.Node, flagsToCheck ast.ModifierFlags, someOverloadFlags ast.ModifierFlags, allOverloadFlags ast.ModifierFlags) {
		// Error if some overloads have a flag that is not shared by all overloads. To find the
		// deviations, we XOR someOverloadFlags with allOverloadFlags
		someButNotAllOverloadFlags := someOverloadFlags ^ allOverloadFlags
		if someButNotAllOverloadFlags != 0 {
			canonicalFlags := c.getEffectiveDeclarationFlags(getCanonicalOverload(overloads, implementation), flagsToCheck)
			groups := make(map[*ast.SourceFile][]*ast.Node)
			for _, overload := range overloads {
				sourceFile := ast.GetSourceFileOfNode(overload)
				groups[sourceFile] = append(groups[sourceFile], overload)
			}
			for _, overloadsInFile := range groups {
				canonicalFlagsForFile := c.getEffectiveDeclarationFlags(getCanonicalOverload(overloadsInFile, implementation), flagsToCheck)
				for _, overload := range overloadsInFile {
					deviation := c.getEffectiveDeclarationFlags(overload, flagsToCheck) ^ canonicalFlags
					deviationInFile := c.getEffectiveDeclarationFlags(overload, flagsToCheck) ^ canonicalFlagsForFile
					switch {
					case deviationInFile&ast.ModifierFlagsExport != 0:
						// Overloads in different files need not all have export modifiers. This is ok:
						//   // lib.d.ts
						//   declare function foo(s: number): string;
						//   declare function foo(s: string): number;
						//   export { foo };
						//
						//   // app.ts
						//   declare module "lib" {
						//     export function foo(s: boolean): boolean;
						//   }
						c.error(ast.GetNameOfDeclaration(overload), diagnostics.Overload_signatures_must_all_be_exported_or_non_exported)
					case deviationInFile&ast.ModifierFlagsAmbient != 0:
						// Though rare, a module augmentation (necessarily ambient) is allowed to add overloads
						// to a non-ambient function in an implementation file.
						c.error(ast.GetNameOfDeclaration(overload), diagnostics.Overload_signatures_must_all_be_ambient_or_non_ambient)
					case deviation&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0:
						c.error(core.OrElse(ast.GetNameOfDeclaration(overload), overload), diagnostics.Overload_signatures_must_all_be_public_private_or_protected)
					case deviation&ast.ModifierFlagsAbstract != 0:
						c.error(ast.GetNameOfDeclaration(overload), diagnostics.Overload_signatures_must_all_be_abstract_or_non_abstract)
					}
				}
			}
		}
	}
	checkQuestionTokenAgreementBetweenOverloads := func(overloads []*ast.Node, implementation *ast.Node, someHaveQuestionToken bool, allHaveQuestionToken bool) {
		if someHaveQuestionToken != allHaveQuestionToken {
			canonicalHasQuestionToken := isOptionalDeclaration(getCanonicalOverload(overloads, implementation))
			for _, o := range overloads {
				if isOptionalDeclaration(o) != canonicalHasQuestionToken {
					c.error(ast.GetNameOfDeclaration(o), diagnostics.Overload_signatures_must_all_be_optional_or_required)
				}
			}
		}
	}
	reportImplementationExpectedError := func(node *ast.Node) {
		name := node.Name()
		if name != nil && ast.NodeIsMissing(name) {
			return
		}
		seen := false
		var subsequentNode *ast.Node
		node.Parent.ForEachChild(func(child *ast.Node) bool {
			if seen {
				subsequentNode = child
				return true
			}
			seen = child == node
			return false
		})
		// We may be here because of some extra nodes between overloads that could not be parsed into a valid node.
		// In this case the subsequent node is not really consecutive (.pos !== node.end), and we must ignore it here.
		if subsequentNode != nil && subsequentNode.Pos() == node.End() {
			if subsequentNode.Kind == node.Kind {
				subsequentName := subsequentNode.Name()
				errorNode := core.OrElse(subsequentName, subsequentNode)
				if name != nil && subsequentName != nil &&
					(ast.IsPrivateIdentifier(name) && ast.IsPrivateIdentifier(subsequentName) && name.Text() == subsequentName.Text() ||
						ast.IsComputedPropertyName(name) && ast.IsComputedPropertyName(subsequentName) && c.isTypeIdenticalTo(c.checkComputedPropertyName(name), c.checkComputedPropertyName(subsequentName)) ||
						ast.IsPropertyNameLiteral(name) && ast.IsPropertyNameLiteral(subsequentName) && name.Text() == subsequentName.Text()) {
					reportError := (ast.IsMethodDeclaration(node) || ast.IsMethodSignatureDeclaration(node)) && ast.IsStatic(node) != ast.IsStatic(subsequentNode)
					// we can get here in two cases
					// 1. mixed static and instance class members
					// 2. something with the same name was defined before the set of overloads that prevents them from merging
					// here we'll report error only for the first case since for second we should already report error in binder
					if reportError {
						diagnostic := core.IfElse(ast.IsStatic(node), diagnostics.Function_overload_must_be_static, diagnostics.Function_overload_must_not_be_static)
						c.error(errorNode, diagnostic)
					}
					return
				}
				if ast.NodeIsPresent(subsequentNode.Body()) {
					c.error(errorNode, diagnostics.Function_implementation_name_must_be_0, scanner.DeclarationNameToString(name))
					return
				}
			}
		}
		errorNode := core.OrElse(name, node)
		if isConstructor {
			c.error(errorNode, diagnostics.Constructor_implementation_is_missing)
		} else {
			// Report different errors regarding non-consecutive blocks of declarations depending on whether
			// the node in question is abstract.
			if ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract) {
				c.error(errorNode, diagnostics.All_declarations_of_an_abstract_method_must_be_consecutive)
			} else {
				c.error(errorNode, diagnostics.Function_implementation_is_missing_or_not_immediately_following_the_declaration)
			}
		}
	}
	for _, node := range declarations {
		inAmbientContext := node.Flags&ast.NodeFlagsAmbient != 0
		inAmbientContextOrInterface := inAmbientContext || node.Parent != nil && (ast.IsInterfaceDeclaration(node.Parent) || ast.IsTypeLiteralNode(node.Parent))
		if inAmbientContextOrInterface {
			// check if declarations are consecutive only if they are non-ambient
			// 1. ambient declarations can be interleaved
			// i.e. this is legal
			//     declare function foo();
			//     declare function bar();
			//     declare function foo();
			// 2. mixing ambient and non-ambient declarations is a separate error that will be reported - do not want to report an extra one
			previousDeclaration = nil
		}
		if ast.IsClassLike(node) && !inAmbientContext {
			hasNonAmbientClass = true
		}
		if ast.IsFunctionDeclaration(node) || ast.IsMethodDeclaration(node) || ast.IsMethodSignatureDeclaration(node) || ast.IsConstructorDeclaration(node) {
			functionDeclarations = append(functionDeclarations, node)
			currentNodeFlags := c.getEffectiveDeclarationFlags(node, flagsToCheck)
			someNodeFlags |= currentNodeFlags
			allNodeFlags &= currentNodeFlags
			someHaveQuestionToken = someHaveQuestionToken || isOptionalDeclaration(node)
			allHaveQuestionToken = allHaveQuestionToken && isOptionalDeclaration(node)
			bodyIsPresent := ast.NodeIsPresent(node.Body())
			if bodyIsPresent && bodyDeclaration != nil {
				if isConstructor {
					multipleConstructorImplementation = true
				} else {
					duplicateFunctionDeclaration = true
				}
			} else if previousDeclaration != nil && previousDeclaration.Parent == node.Parent && previousDeclaration.End() != node.Pos() && previousDeclaration.Flags&ast.NodeFlagsReparsed == 0 {
				reportImplementationExpectedError(previousDeclaration)
			}
			if bodyIsPresent {
				if bodyDeclaration == nil {
					bodyDeclaration = node
				}
			} else {
				hasOverloads = true
			}
			previousDeclaration = node
			if !inAmbientContextOrInterface {
				lastSeenNonAmbientDeclaration = node
			}
		}
	}
	if multipleConstructorImplementation {
		for _, declaration := range functionDeclarations {
			c.error(declaration, diagnostics.Multiple_constructor_implementations_are_not_allowed)
		}
	}
	if duplicateFunctionDeclaration {
		for _, declaration := range functionDeclarations {
			c.error(core.OrElse(ast.GetNameOfDeclaration(declaration), declaration), diagnostics.Duplicate_function_implementation)
		}
	}
	if hasNonAmbientClass && !isConstructor && symbol.Flags&ast.SymbolFlagsFunction != 0 && len(declarations) != 0 {
		var relatedDiagnostics []*ast.Diagnostic
		for _, declaration := range declarations {
			if ast.IsClassDeclaration(declaration) {
				relatedDiagnostics = append(relatedDiagnostics, createDiagnosticForNode(declaration, diagnostics.Consider_adding_a_declare_modifier_to_this_class))
			}
		}
		for _, declaration := range declarations {
			var diagnostic *diagnostics.Message
			switch declaration.Kind {
			case ast.KindClassDeclaration:
				diagnostic = diagnostics.Class_declaration_cannot_implement_overload_list_for_0
			case ast.KindFunctionDeclaration:
				diagnostic = diagnostics.Function_with_bodies_can_only_merge_with_classes_that_are_ambient
			}
			if diagnostic != nil {
				c.error(core.OrElse(ast.GetNameOfDeclaration(declaration), declaration), diagnostic, symbol.Name).SetRelatedInfo(relatedDiagnostics)
			}
		}
	}
	// Abstract methods can't have an implementation -- in particular, they don't need one.
	if lastSeenNonAmbientDeclaration != nil && lastSeenNonAmbientDeclaration.Body() == nil && !ast.HasSyntacticModifier(lastSeenNonAmbientDeclaration, ast.ModifierFlagsAbstract) && !isOptionalDeclaration(lastSeenNonAmbientDeclaration) {
		reportImplementationExpectedError(lastSeenNonAmbientDeclaration)
	}
	if hasOverloads {
		checkFlagAgreementBetweenOverloads(declarations, bodyDeclaration, flagsToCheck, someNodeFlags, allNodeFlags)
		checkQuestionTokenAgreementBetweenOverloads(declarations, bodyDeclaration, someHaveQuestionToken, allHaveQuestionToken)
		if bodyDeclaration != nil {
			signatures := c.getSignaturesOfSymbol(symbol)
			bodySignature := c.getSignatureFromDeclaration(bodyDeclaration)
			for _, signature := range signatures {
				if !c.isImplementationCompatibleWithOverload(bodySignature, signature) {
					errorNode := signature.declaration
					c.error(errorNode, diagnostics.This_overload_signature_is_not_compatible_with_its_implementation_signature).AddRelatedInfo(createDiagnosticForNode(bodyDeclaration, diagnostics.The_implementation_signature_is_declared_here))
					break
				}
			}
		}
	}
}

func (c *Checker) getEffectiveDeclarationFlags(n *ast.Node, flagsToCheck ast.ModifierFlags) ast.ModifierFlags {
	flags := c.getCombinedModifierFlagsCached(n)
	// children of classes (even ambient classes) should not be marked as ambient or export
	// because those flags have no useful semantics there.
	if !ast.IsInterfaceDeclaration(n.Parent) && !ast.IsClassDeclaration(n.Parent) && !ast.IsClassExpression(n.Parent) && n.Flags&ast.NodeFlagsAmbient != 0 {
		container := getEnclosingContainer(n)
		if container != nil && container.Flags&ast.NodeFlagsExportContext != 0 && flags&ast.ModifierFlagsAmbient == 0 && !(ast.IsModuleBlock(n.Parent) && ast.IsGlobalScopeAugmentation(n.Parent.Parent)) {
			// It is nested in an ambient export context, which means it is automatically exported
			flags |= ast.ModifierFlagsExport
		}
		flags |= ast.ModifierFlagsAmbient
	}
	return flags & flagsToCheck
}

func (c *Checker) isImplementationCompatibleWithOverload(implementation *Signature, overload *Signature) bool {
	erasedSource := c.getErasedSignature(implementation)
	erasedTarget := c.getErasedSignature(overload)
	// First see if the return types are compatible in either direction.
	sourceReturnType := c.getReturnTypeOfSignature(erasedSource)
	targetReturnType := c.getReturnTypeOfSignature(erasedTarget)
	if targetReturnType == c.voidType || c.isTypeRelatedTo(targetReturnType, sourceReturnType, c.assignableRelation) || c.isTypeRelatedTo(sourceReturnType, targetReturnType, c.assignableRelation) {
		return c.isSignatureAssignableTo(erasedSource, erasedTarget, true /*ignoreReturnTypes*/)
	}
	return false
}

func (c *Checker) checkAllCodePathsInNonVoidFunctionReturnOrThrow(fn *ast.Node, returnType *Type) {
	functionFlags := ast.GetFunctionFlags(fn)
	var t *Type
	if returnType != nil {
		t = c.unwrapReturnType(returnType, functionFlags)
	}
	// Functions with an explicitly specified return type that includes `void` or is exactly `any` or `undefined` don't
	// need any return statements.
	if t != nil && (c.maybeTypeOfKind(t, TypeFlagsVoid) || t.flags&(TypeFlagsAny|TypeFlagsUndefined) != 0) {
		return
	}
	// If all we have is a function signature, or an arrow function with an expression body, then there is nothing to check.
	// also if HasImplicitReturn flag is not set this means that all codepaths in function body end with return or throw
	if ast.IsMethodSignatureDeclaration(fn) || ast.NodeIsMissing(fn.Body()) || !ast.IsBlock(fn.Body()) || !c.functionHasImplicitReturn(fn) {
		return
	}
	hasExplicitReturn := fn.Flags&ast.NodeFlagsHasExplicitReturn != 0
	errorNode := fn.Type()
	if errorNode == nil {
		if data := fn.FunctionLikeData(); data != nil && data.FullSignature != nil {
			errorNode = data.FullSignature
		}
	}
	if errorNode == nil {
		errorNode = fn
	}
	switch {
	case t != nil && t.flags&TypeFlagsNever != 0:
		c.error(errorNode, diagnostics.A_function_returning_never_cannot_have_a_reachable_end_point)
	case t != nil && !hasExplicitReturn:
		// minimal check: function has syntactic return type annotation and no explicit return statements in the body
		// this function does not conform to the specification.
		c.error(errorNode, diagnostics.A_function_whose_declared_type_is_neither_undefined_void_nor_any_must_return_a_value)
	case t != nil && c.strictNullChecks && !c.isTypeAssignableTo(c.undefinedType, t):
		c.error(errorNode, diagnostics.Function_lacks_ending_return_statement_and_return_type_does_not_include_undefined)
	case c.compilerOptions.NoImplicitReturns == core.TSTrue:
		if t == nil {
			// If return type annotation is omitted check if function has any explicit return statements.
			// If it does not have any - its inferred return type is void - don't do any checks.
			// Otherwise get inferred return type from function body and report error only if it is not void / anytype
			if !hasExplicitReturn {
				return
			}
			inferredReturnType := c.getReturnTypeOfSignature(c.getSignatureFromDeclaration(fn))
			if c.isUnwrappedReturnTypeUndefinedVoidOrAny(fn, inferredReturnType) {
				return
			}
		}
		c.error(errorNode, diagnostics.Not_all_code_paths_return_a_value)
	}
}

func (c *Checker) isUnwrappedReturnTypeUndefinedVoidOrAny(fn *ast.Node, returnType *Type) bool {
	t := c.unwrapReturnType(returnType, ast.GetFunctionFlags(fn))
	return t != nil && (c.maybeTypeOfKind(t, TypeFlagsVoid) || t.flags&(TypeFlagsAny|TypeFlagsUndefined) != 0)
}

func (c *Checker) checkBlock(node *ast.Node) {
	// Grammar checking for SyntaxKind.Block
	if node.Kind == ast.KindBlock {
		c.checkGrammarStatementInAmbientContext(node)
	}
	if ast.IsFunctionOrModuleBlock(node) {
		saveFlowAnalysisDisabled := c.flowAnalysisDisabled
		c.checkSourceElements(node.Statements())
		c.flowAnalysisDisabled = saveFlowAnalysisDisabled
	} else {
		c.checkSourceElements(node.Statements())
	}
	if len(node.Locals()) != 0 {
		c.registerForUnusedIdentifiersCheck(node)
	}
}

func (c *Checker) checkIfStatement(node *ast.Node) {
	c.checkGrammarStatementInAmbientContext(node)
	t := c.checkTruthinessExpression(node.Expression(), CheckModeNormal)
	data := node.AsIfStatement()
	c.checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(node.Expression(), t, data.ThenStatement)
	c.checkSourceElement(data.ThenStatement)
	if ast.IsEmptyStatement(data.ThenStatement) {
		c.error(data.ThenStatement, diagnostics.The_body_of_an_if_statement_cannot_be_the_empty_statement)
	}
	c.checkSourceElement(data.ElseStatement)
}

func (c *Checker) checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(condExpr *ast.Node, condType *Type, body *ast.Node) {
	if !c.strictNullChecks {
		return
	}
	c.checkTestingKnownTruthyTypes(condExpr, condType, body)
}

func (c *Checker) checkTestingKnownTruthyTypes(condExpr *ast.Node, condType *Type, body *ast.Node) {
	condExpr = ast.SkipParentheses(condExpr)
	c.checkTestingKnownTruthyType(condExpr, condType, body)
	for ast.IsBinaryExpression(condExpr) && (condExpr.AsBinaryExpression().OperatorToken.Kind == ast.KindBarBarToken || condExpr.AsBinaryExpression().OperatorToken.Kind == ast.KindQuestionQuestionToken) {
		condExpr = ast.SkipParentheses(condExpr.AsBinaryExpression().Left)
		c.checkTestingKnownTruthyType(condExpr, condType, body)
	}
}

func (c *Checker) checkTestingKnownTruthyType(condExpr *ast.Node, condType *Type, body *ast.Node) {
	location := condExpr
	if ast.IsLogicalOrCoalescingBinaryExpression(condExpr) {
		location = ast.SkipParentheses(condExpr.AsBinaryExpression().Right)
	}
	if ast.IsModuleExportsAccessExpression(location) {
		return
	}
	if ast.IsLogicalOrCoalescingBinaryExpression(location) {
		c.checkTestingKnownTruthyTypes(location, condType, body)
		return
	}
	t := condType
	if location != condExpr {
		t = c.checkExpression(location)
	}
	if t.flags&TypeFlagsEnumLiteral != 0 && ast.IsPropertyAccessExpression(location) && core.OrElse(c.getResolvedSymbolOrNil(location.Expression()), c.unknownSymbol).Flags&ast.SymbolFlagsEnum != 0 {
		// EnumLiteral type at condition with known value is always truthy or always falsy, likely an error
		c.error(location, diagnostics.This_condition_will_always_return_0, core.IfElse(evaluator.IsTruthy(t.AsLiteralType().value), "true", "false"))
		return
	}
	isPropertyExpressionCast := ast.IsPropertyAccessExpression(location) && isTypeAssertion(location.Expression())
	if !c.hasTypeFacts(t, TypeFactsTruthy) || isPropertyExpressionCast {
		return
	}
	// While it technically should be invalid for any known-truthy value
	// to be tested, we de-scope to functions and Promises unreferenced in
	// the block as a heuristic to identify the most common bugs. There
	// are too many false positives for values sourced from type
	// definitions without strictNullChecks otherwise.
	callSignatures := c.getSignaturesOfType(t, SignatureKindCall)
	isPromise := c.getAwaitedTypeOfPromise(t) != nil
	if len(callSignatures) == 0 && !isPromise {
		return
	}
	var testedNode *ast.Node
	switch {
	case ast.IsIdentifier(location):
		testedNode = location
	case ast.IsPropertyAccessExpression(location):
		testedNode = location.Name()
	}
	var testedSymbol *ast.Symbol
	if testedNode != nil {
		testedSymbol = c.getSymbolAtLocation(testedNode, false)
	}
	if testedSymbol == nil && !isPromise {
		return
	}
	isUsed := testedSymbol != nil && ast.IsBinaryExpression(condExpr.Parent) && c.isSymbolUsedInBinaryExpressionChain(condExpr.Parent, testedSymbol) ||
		testedSymbol != nil && body != nil && c.isSymbolUsedInConditionBody(condExpr, body, testedNode, testedSymbol)
	if !isUsed {
		if isPromise {
			c.errorAndMaybeSuggestAwait(location, true, diagnostics.This_condition_will_always_return_true_since_this_0_is_always_defined, c.getTypeNameForErrorDisplay(t))
		} else {
			c.error(location, diagnostics.This_condition_will_always_return_true_since_this_function_is_always_defined_Did_you_mean_to_call_it_instead)
		}
	}
}

func (c *Checker) isSymbolUsedInBinaryExpressionChain(node *ast.Node, testedSymbol *ast.Symbol) bool {
	var visit func(*ast.Node) bool
	visit = func(child *ast.Node) bool {
		if ast.IsIdentifier(child) {
			symbol := c.getSymbolAtLocation(child, false)
			if symbol != nil && symbol == testedSymbol {
				return true
			}
		}
		return child.ForEachChild(visit)
	}
	for ast.IsBinaryExpression(node) && node.AsBinaryExpression().OperatorToken.Kind == ast.KindAmpersandAmpersandToken {
		isUsed := node.AsBinaryExpression().Right.ForEachChild(visit)
		if isUsed {
			return true
		}
		node = node.Parent
	}
	return false
}

func (c *Checker) isSymbolUsedInConditionBody(expr *ast.Node, body *ast.Node, testedNode *ast.Node, testedSymbol *ast.Symbol) bool {
	var visit func(*ast.Node) bool
	visit = func(childNode *ast.Node) bool {
		if ast.IsIdentifier(childNode) {
			childSymbol := c.getSymbolAtLocation(childNode, false)
			if childSymbol != nil && childSymbol == testedSymbol {
				// If the test was a simple identifier, the above check is sufficient
				if ast.IsIdentifier(expr) || ast.IsIdentifier(testedNode) && ast.IsBinaryExpression(testedNode.Parent) {
					return true
				}
				// Otherwise we need to ensure the symbol is called on the same target
				testedExpression := testedNode.Parent
				childExpression := childNode.Parent
				for testedExpression != nil && childExpression != nil {
					if ast.IsIdentifier(testedExpression) && ast.IsIdentifier(childExpression) || testedExpression.Kind == ast.KindThisKeyword && childExpression.Kind == ast.KindThisKeyword {
						return c.getSymbolAtLocation(testedExpression, false) == c.getSymbolAtLocation(childExpression, false)
					} else if ast.IsPropertyAccessExpression(testedExpression) && ast.IsPropertyAccessExpression(childExpression) {
						if c.getSymbolAtLocation(testedExpression.Name(), false) != c.getSymbolAtLocation(childExpression.Name(), false) {
							return false
						}
						childExpression = childExpression.Expression()
						testedExpression = testedExpression.Expression()
					} else if ast.IsCallExpression(testedExpression) && ast.IsCallExpression(childExpression) {
						childExpression = childExpression.Expression()
						testedExpression = testedExpression.Expression()
					} else {
						return false
					}
				}
			}
		}
		return childNode.ForEachChild(visit)
	}
	return body.ForEachChild(visit)
}

func (c *Checker) checkDoStatement(node *ast.Node) {
	c.checkGrammarStatementInAmbientContext(node)
	c.checkSourceElement(node.Statement())
	c.checkTruthinessExpression(node.Expression(), CheckModeNormal)
}

func (c *Checker) checkWhileStatement(node *ast.Node) {
	c.checkGrammarStatementInAmbientContext(node)
	c.checkTruthinessExpression(node.Expression(), CheckModeNormal)
	c.checkSourceElement(node.Statement())
}

func (c *Checker) checkForStatement(node *ast.Node) {
	if !c.checkGrammarStatementInAmbientContext(node) {
		if init := node.Initializer(); init != nil && init.Kind == ast.KindVariableDeclarationList {
			c.checkGrammarVariableDeclarationList(init.AsVariableDeclarationList())
		}
	}
	data := node.AsForStatement()
	if data.Initializer != nil {
		if ast.IsVariableDeclarationList(data.Initializer) {
			c.checkVariableDeclarationList(data.Initializer)
		} else {
			c.checkExpression(data.Initializer)
		}
	}
	if data.Condition != nil {
		c.checkTruthinessExpression(data.Condition, CheckModeNormal)
	}
	if data.Incrementor != nil {
		c.checkExpression(data.Incrementor)
	}
	c.checkSourceElement(data.Statement)
	if node.Locals() != nil {
		c.registerForUnusedIdentifiersCheck(node)
	}
}

func (c *Checker) checkForInStatement(node *ast.Node) {
	data := node.AsForInOrOfStatement()
	c.checkGrammarForInOrForOfStatement(data)
	rightType := c.getNonNullableTypeIfNeeded(c.checkExpression(data.Expression))
	// TypeScript 1.0 spec (April 2014): 5.4
	// In a 'for-in' statement of the form
	// for (let VarDecl in Expr) Statement
	//   VarDecl must be a variable declaration without a type annotation that declares a variable of type Any,
	//   and Expr must be an expression of type Any, an object type, or a type parameter type.
	if ast.IsVariableDeclarationList(data.Initializer) {
		declarations := data.Initializer.AsVariableDeclarationList().Declarations.Nodes
		if len(declarations) != 0 && ast.IsBindingPattern(declarations[0].Name()) {
			c.error(declarations[0].Name(), diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern)
		}
		c.checkVariableDeclarationList(data.Initializer)
	} else {
		// In a 'for-in' statement of the form
		// for (Var in Expr) Statement
		//   Var must be an expression classified as a reference of type Any or the String primitive type,
		//   and Expr must be an expression of type Any, an object type, or a type parameter type.
		varExpr := data.Initializer
		leftType := c.checkExpression(varExpr)
		if ast.IsArrayLiteralExpression(varExpr) || ast.IsObjectLiteralExpression(varExpr) {
			c.error(varExpr, diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern)
		} else if !c.isTypeAssignableTo(c.getIndexTypeOrString(rightType), leftType) {
			c.error(varExpr, diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_of_type_string_or_any)
		} else {
			// run check only former check succeeded to avoid cascading errors
			c.checkReferenceExpression(varExpr, diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access, diagnostics.The_left_hand_side_of_a_for_in_statement_may_not_be_an_optional_property_access)
		}
	}
	// unknownType is returned i.e. if node.expression is identifier whose name cannot be resolved
	// in this case error about missing name is already reported - do not report extra one
	if rightType == c.neverType || !c.isTypeAssignableToKind(rightType, TypeFlagsNonPrimitive|TypeFlagsInstantiableNonPrimitive) {
		c.error(data.Expression, diagnostics.The_right_hand_side_of_a_for_in_statement_must_be_of_type_any_an_object_type_or_a_type_parameter_but_here_has_type_0, c.TypeToString(rightType))
	}
	c.checkSourceElement(data.Statement)
	if node.Locals() != nil {
		c.registerForUnusedIdentifiersCheck(node)
	}
}

func (c *Checker) getIndexTypeOrString(t *Type) *Type {
	indexType := c.getExtractStringType(c.getIndexType(t))
	return core.IfElse(indexType.flags&TypeFlagsNever != 0, c.stringType, indexType)
}

func (c *Checker) checkForOfStatement(node *ast.Node) {
	data := node.AsForInOrOfStatement()
	c.checkGrammarForInOrForOfStatement(data)
	container := getContainingFunctionOrClassStaticBlock(node)
	if data.AwaitModifier != nil {
		if container != nil && ast.IsClassStaticBlockDeclaration(container) {
			c.grammarErrorOnNode(data.AwaitModifier, diagnostics.X_for_await_loops_cannot_be_used_inside_a_class_static_block)
		} else {
			functionFlags := ast.GetFunctionFlags(container)
			if functionFlags&(ast.FunctionFlagsInvalid|ast.FunctionFlagsAsync) == ast.FunctionFlagsAsync && c.languageVersion < LanguageFeatureMinimumTarget.ForAwaitOf {
				// for..await..of in an async function or async generator function prior to ESNext requires the __asyncValues helper
				c.checkExternalEmitHelpers(node, ExternalEmitHelpersForAwaitOfIncludes)
			}
		}
	} // Check the LHS and RHS
	// If the LHS is a declaration, just check it as a variable declaration, which will in turn check the RHS
	// via checkRightHandSideOfForOf.
	// If the LHS is an expression, check the LHS, as a destructuring assignment or as a reference.
	// Then check that the RHS is assignable to it.
	if ast.IsVariableDeclarationList(data.Initializer) {
		c.checkVariableDeclarationList(data.Initializer)
	} else {
		varExpr := data.Initializer
		iteratedType := c.checkRightHandSideOfForOf(node)
		// There may be a destructuring assignment on the left side
		if ast.IsArrayLiteralExpression(varExpr) || ast.IsObjectLiteralExpression(varExpr) {
			// iteratedType may be undefined. In this case, we still want to check the structure of
			// varExpr, in particular making sure it's a valid LeftHandSideExpression. But we'd like
			// to short circuit the type relation checking as much as possible, so we pass the unknownType.
			c.checkDestructuringAssignment(varExpr, core.OrElse(iteratedType, c.errorType), CheckModeNormal, false)
		} else {
			leftType := c.checkExpression(varExpr)
			c.checkReferenceExpression(varExpr, diagnostics.The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access, diagnostics.The_left_hand_side_of_a_for_of_statement_may_not_be_an_optional_property_access)
			// iteratedType will be undefined if the rightType was missing properties/signatures
			// required to get its iteratedType (like [Symbol.iterator] or next). This may be
			// because we accessed properties from anyType, or it may have led to an error inside
			// getElementTypeOfIterable.
			if iteratedType != nil {
				c.checkTypeAssignableToAndOptionallyElaborate(iteratedType, leftType, varExpr, data.Expression, nil, nil)
			}
		}
	}
	c.checkSourceElement(data.Statement)
	if node.Locals() != nil {
		c.registerForUnusedIdentifiersCheck(node)
	}
}

func (c *Checker) checkBreakOrContinueStatement(node *ast.Node) {
	if !c.checkGrammarStatementInAmbientContext(node) {
		c.checkGrammarBreakOrContinueStatement(node)
	}
}

func (c *Checker) checkReturnStatement(node *ast.Node) {
	if c.checkGrammarStatementInAmbientContext(node) {
		return
	}
	container := getContainingFunctionOrClassStaticBlock(node)
	if container != nil && ast.IsClassStaticBlockDeclaration(container) {
		c.grammarErrorOnFirstToken(node, diagnostics.A_return_statement_cannot_be_used_inside_a_class_static_block)
		return
	}
	if container == nil {
		c.grammarErrorOnFirstToken(node, diagnostics.A_return_statement_can_only_be_used_within_a_function_body)
		return
	}
	signature := c.getSignatureFromDeclaration(container)
	returnType := c.getReturnTypeOfSignature(signature)
	functionFlags := ast.GetFunctionFlags(container)
	exprNode := node.Expression()
	if c.strictNullChecks || exprNode != nil || returnType.flags&TypeFlagsNever != 0 {
		exprType := c.undefinedType
		if exprNode != nil {
			exprType = c.checkExpressionCached(exprNode)
		}
		if ast.IsSetAccessorDeclaration(container) {
			if exprNode != nil {
				c.error(node, diagnostics.Setters_cannot_return_a_value)
			}
		} else if ast.IsConstructorDeclaration(container) {
			if exprNode != nil && !c.checkTypeAssignableToAndOptionallyElaborate(exprType, returnType, node, exprNode, nil, nil) {
				c.error(node, diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class)
			}
		} else if c.getReturnTypeFromAnnotation(container) != nil {
			unwrappedReturnType := core.OrElse(c.unwrapReturnType(returnType, functionFlags), returnType)
			c.checkReturnExpression(container, unwrappedReturnType, node, node.Expression(), exprType, false)
		}
	} else if !ast.IsConstructorDeclaration(container) && c.compilerOptions.NoImplicitReturns.IsTrue() && !c.isUnwrappedReturnTypeUndefinedVoidOrAny(container, returnType) {
		// The function has a return type, but the return statement doesn't have an expression.
		c.error(node, diagnostics.Not_all_code_paths_return_a_value)
	}
}

// When checking an arrow expression such as `(x) => exp`, then `node` is the expression `exp`.
// Otherwise, `node` is a return statement.
func (c *Checker) checkReturnExpression(container *ast.Node, unwrappedReturnType *Type, node *ast.Node, expr *ast.Node, exprType *Type, inConditionalExpression bool) {
	unwrappedExprType := exprType
	functionFlags := ast.GetFunctionFlags(container)
	if expr != nil {
		unwrappedExpr := ast.SkipParentheses(expr)
		if ast.IsConditionalExpression(unwrappedExpr) {
			whenTrue := unwrappedExpr.AsConditionalExpression().WhenTrue
			whenFalse := unwrappedExpr.AsConditionalExpression().WhenFalse
			c.checkReturnExpression(container, unwrappedReturnType, node, whenTrue, c.checkExpression(whenTrue), true /*inConditionalExpression*/)
			c.checkReturnExpression(container, unwrappedReturnType, node, whenFalse, c.checkExpression(whenFalse), true /*inConditionalExpression*/)
			return
		}
	}
	inReturnStatement := node.Kind == ast.KindReturnStatement
	if functionFlags&ast.FunctionFlagsAsync != 0 {
		unwrappedExprType = c.checkAwaitedType(exprType, false /*withAlias*/, node, diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
	}
	effectiveExpr := expr // The effective expression for diagnostics purposes.
	if expr != nil {
		effectiveExpr = c.getEffectiveCheckNode(expr)
	}
	errorNode := core.IfElse(inReturnStatement && !inConditionalExpression, node, effectiveExpr)
	c.checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, errorNode, effectiveExpr, nil, nil)
}

func (c *Checker) checkWithStatement(node *ast.Node) {
	if !c.checkGrammarStatementInAmbientContext(node) {
		if node.Flags&ast.NodeFlagsAwaitContext != 0 {
			c.grammarErrorOnFirstToken(node, diagnostics.X_with_statements_are_not_allowed_in_an_async_function_block)
		}
	}
	c.checkExpression(node.Expression())
	sourceFile := ast.GetSourceFileOfNode(node)
	if !c.hasParseDiagnostics(sourceFile) {
		start := scanner.SkipTrivia(sourceFile.Text(), node.Pos())
		end := node.Statement().Pos()
		c.grammarErrorAtPos(sourceFile.AsNode(), start, end-start, diagnostics.The_with_statement_is_not_supported_All_symbols_in_a_with_block_will_have_type_any)
	}
}

func (c *Checker) checkSwitchStatement(node *ast.Node) {
	// Grammar checking
	c.checkGrammarStatementInAmbientContext(node)
	var firstDefaultClause *ast.Node
	hasDuplicateDefaultClause := false
	expressionType := c.checkExpression(node.Expression())
	caseBlock := node.AsSwitchStatement().CaseBlock
	for _, clause := range caseBlock.AsCaseBlock().Clauses.Nodes {
		// Grammar check for duplicate default clauses, skip if we already report duplicate default clause
		if ast.IsDefaultClause(clause) && !hasDuplicateDefaultClause {
			if firstDefaultClause == nil {
				firstDefaultClause = clause
			} else {
				c.grammarErrorOnNode(clause, diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement)
				hasDuplicateDefaultClause = true
			}
		}
		if ast.IsCaseClause(clause) {
			caseType := c.checkExpression(clause.Expression())
			if !c.isTypeEqualityComparableTo(expressionType, caseType) {
				// expressionType is not comparable to caseType, try the reversed check and report errors if it fails
				c.checkTypeComparableTo(caseType, expressionType, clause.Expression(), nil /*headMessage*/)
			}
		}
		c.checkSourceElements(clause.Statements())
		if c.compilerOptions.NoFallthroughCasesInSwitch.IsTrue() {
			if flowNode := clause.AsCaseOrDefaultClause().FallthroughFlowNode; flowNode != nil && c.isReachableFlowNode(flowNode) {
				c.error(clause, diagnostics.Fallthrough_case_in_switch)
			}
		}
	}
	if caseBlock.Locals() != nil {
		c.registerForUnusedIdentifiersCheck(caseBlock)
	}
}

func (c *Checker) checkLabeledStatement(node *ast.Node) {
	labeledStatement := node.AsLabeledStatement()
	labelNode := labeledStatement.Label
	labelText := labelNode.Text()
	if !c.checkGrammarStatementInAmbientContext(node) {
		for current := node.Parent; current != nil && !ast.IsFunctionLike(current); current = current.Parent {
			if ast.IsLabeledStatement(current) && current.Label().Text() == labelText {
				c.grammarErrorOnNode(labelNode, diagnostics.Duplicate_label_0, labelText)
				break
			}
		}
	}
	if labelNode.Flags&ast.NodeFlagsUnreachable != 0 && c.compilerOptions.AllowUnusedLabels != core.TSTrue {
		c.errorOrSuggestion(c.compilerOptions.AllowUnusedLabels == core.TSFalse, labelNode, diagnostics.Unused_label)
	}
	c.checkSourceElement(labeledStatement.Statement)
}

func (c *Checker) checkThrowStatement(node *ast.Node) {
	throwExpr := node.Expression()
	if !c.checkGrammarStatementInAmbientContext(node) {
		if ast.IsIdentifier(throwExpr) && len(throwExpr.Text()) == 0 {
			c.grammarErrorAtPos(node, throwExpr.Pos(), 0 /*length*/, diagnostics.Line_break_not_permitted_here)
		}
	}
	c.checkExpression(throwExpr)
}

func (c *Checker) checkTryStatement(node *ast.Node) {
	c.checkGrammarStatementInAmbientContext(node)
	data := node.AsTryStatement()
	c.checkBlock(data.TryBlock)
	if data.CatchClause != nil {
		c.checkCatchClause(data.CatchClause)
	}
	if data.FinallyBlock != nil {
		c.checkBlock(data.FinallyBlock)
	}
}

func (c *Checker) checkCatchClause(node *ast.Node) {
	declaration := node.AsCatchClause().VariableDeclaration
	if declaration != nil {
		c.checkVariableLikeDeclaration(declaration)
		typeNode := declaration.Type()
		if typeNode != nil {
			t := c.getTypeFromTypeNode(typeNode)
			if t != nil && t.flags&TypeFlagsAnyOrUnknown == 0 {
				c.grammarErrorOnFirstToken(typeNode, diagnostics.Catch_clause_variable_type_annotation_must_be_any_or_unknown_if_specified)
			}
		} else if declaration.Initializer() != nil {
			c.grammarErrorOnFirstToken(declaration.Initializer(), diagnostics.Catch_clause_variable_cannot_have_an_initializer)
		} else {
			blockLocals := node.AsCatchClause().Block.Locals()
			if blockLocals != nil {
				for caughtName := range node.Locals() {
					if blockLocal := blockLocals[caughtName]; blockLocal != nil && blockLocal.ValueDeclaration != nil && blockLocal.Flags&ast.SymbolFlagsBlockScopedVariable != 0 {
						c.grammarErrorOnNode(blockLocal.ValueDeclaration, diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, caughtName)
					}
				}
			}
		}
	}
	c.checkBlock(node.AsCatchClause().Block)
}

func (c *Checker) checkBindingElement(node *ast.Node) {
	c.checkGrammarBindingElement(node.AsBindingElement())
	c.checkVariableLikeDeclaration(node)
}

func (c *Checker) checkClassDeclaration(node *ast.Node) {
	firstDecorator := core.Find(node.ModifierNodes(), ast.IsDecorator)
	if c.legacyDecorators && firstDecorator != nil && core.Some(node.Members(), func(p *ast.Node) bool {
		return ast.HasStaticModifier(p) && ast.IsPrivateIdentifierClassElementDeclaration(p)
	}) {
		c.grammarErrorOnNode(firstDecorator, diagnostics.Class_decorators_can_t_be_used_with_static_private_identifier_Consider_removing_the_experimental_decorator)
	}
	if node.Name() == nil && !ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) {
		c.grammarErrorOnFirstToken(node, diagnostics.A_class_declaration_without_the_default_modifier_must_have_a_name)
	}
	c.checkClassLikeDeclaration(node)
	c.checkSourceElements(node.Members())
	c.registerForUnusedIdentifiersCheck(node)
}

func (c *Checker) checkClassLikeDeclaration(node *ast.Node) {
	c.checkGrammarClassLikeDeclaration(node)
	c.checkDecorators(node)
	c.checkCollisionsForDeclarationName(node, node.Name())
	c.checkTypeParameters(node.TypeParameters())
	c.checkExportsOnMergedDeclarations(node)
	symbol := c.getSymbolOfDeclaration(node)
	classType := c.getDeclaredTypeOfSymbol(symbol)
	classTypeData := classType.AsInterfaceType()
	typeWithThis := c.getTypeWithThisArgument(classType, nil, false)
	staticType := c.getTypeOfSymbol(symbol)
	c.checkTypeParameterListsIdentical(symbol)
	c.checkFunctionOrConstructorSymbol(symbol)
	c.checkObjectTypeForDuplicateDeclarations(node, true /*checkPrivateNames*/)

	// Only check for reserved static identifiers on non-ambient context.
	nodeInAmbientContext := node.Flags&ast.NodeFlagsAmbient != 0
	if !nodeInAmbientContext {
		c.checkClassForStaticPropertyNameConflicts(node)
	}

	baseTypeNode := ast.GetExtendsHeritageClauseElement(node)
	if baseTypeNode != nil {
		c.checkSourceElements(baseTypeNode.TypeArguments())
		baseTypes := c.getBaseTypes(classType)
		if len(baseTypes) != 0 {
			baseType := baseTypes[0]
			baseConstructorType := c.getBaseConstructorTypeOfClass(classType)
			staticBaseType := c.getApparentType(baseConstructorType)
			c.checkBaseTypeAccessibility(staticBaseType, baseTypeNode)
			c.checkSourceElement(baseTypeNode.Expression())
			if len(baseTypeNode.TypeArguments()) != 0 {
				c.checkSourceElements(baseTypeNode.TypeArguments())
				for _, constructor := range c.getConstructorsForTypeArguments(staticBaseType, baseTypeNode.TypeArguments(), baseTypeNode) {
					if !c.checkTypeArgumentConstraints(baseTypeNode, constructor.typeParameters) {
						break
					}
				}
			}
			baseWithThis := c.getTypeWithThisArgument(baseType, classTypeData.thisType, false)
			if !c.checkTypeAssignableTo(typeWithThis, baseWithThis, nil, nil) {
				c.issueMemberSpecificError(node, typeWithThis, baseWithThis, diagnostics.Class_0_incorrectly_extends_base_class_1)
			} else {
				// Report static side error only when instance type is assignable
				c.checkTypeAssignableTo(staticType, c.getTypeWithoutSignatures(staticBaseType), core.OrElse(node.Name(), node), diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1)
			}
			if baseConstructorType.flags&TypeFlagsTypeVariable != 0 {
				if !c.isMixinConstructorType(staticType) {
					c.error(core.OrElse(node.Name(), node), diagnostics.A_mixin_class_must_have_a_constructor_with_a_single_rest_parameter_of_type_any)
				} else {
					constructSignatures := c.getSignaturesOfType(baseConstructorType, SignatureKindConstruct)
					if core.Some(constructSignatures, func(signature *Signature) bool {
						return signature.flags&SignatureFlagsAbstract != 0
					}) && !ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract) {
						c.error(core.OrElse(node.Name(), node), diagnostics.A_mixin_class_that_extends_from_a_type_variable_containing_an_abstract_construct_signature_must_also_be_declared_abstract)
					}
				}
			}
			if !(staticBaseType.symbol != nil && staticBaseType.symbol.Flags&ast.SymbolFlagsClass != 0) && baseConstructorType.flags&TypeFlagsTypeVariable == 0 {
				// When the static base type is a "class-like" constructor function (but not actually a class), we verify
				// that all instantiated base constructor signatures return the same type.
				constructors := c.getInstantiatedConstructorsForTypeArguments(staticBaseType, baseTypeNode.TypeArguments(), baseTypeNode)
				if !core.Every(constructors, func(sig *Signature) bool {
					return c.isTypeIdenticalTo(c.getReturnTypeOfSignature(sig), baseType)
				}) {
					c.error(baseTypeNode.Expression(), diagnostics.Base_constructors_must_all_have_the_same_return_type)
				}
			}
			c.checkKindsOfPropertyMemberOverrides(classType, baseType)
		}
	}
	c.checkMembersForOverrideModifier(node, classType, typeWithThis, staticType)
	implementedTypeNodes := ast.GetImplementsHeritageClauseElements(node)
	for _, typeRefNode := range implementedTypeNodes {
		expr := typeRefNode.Expression()
		if !ast.IsEntityNameExpression(expr) || ast.IsOptionalChain(expr) {
			c.error(expr, diagnostics.A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments)
		}
		c.checkTypeReferenceNode(typeRefNode)
		t := c.getReducedType(c.getTypeFromTypeNode(typeRefNode))
		if !c.isErrorType(t) {
			if c.isValidBaseType(t) {
				genericDiag := core.IfElse(t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsClass != 0,
					diagnostics.Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass,
					diagnostics.Class_0_incorrectly_implements_interface_1)
				baseWithThis := c.getTypeWithThisArgument(t, classType.AsInterfaceType().thisType, false)
				if !c.checkTypeAssignableTo(typeWithThis, baseWithThis, nil, nil) {
					c.issueMemberSpecificError(node, typeWithThis, baseWithThis, genericDiag)
				}
			} else {
				c.error(typeRefNode, diagnostics.A_class_can_only_implement_an_object_type_or_intersection_of_object_types_with_statically_known_members)
			}
		}
	}
	c.checkIndexConstraints(classType, symbol, false /*isStaticIndex*/)
	c.checkIndexConstraints(staticType, symbol, true /*isStaticIndex*/)
	c.checkClassOrInterfaceForDuplicateIndexSignatures(node)
	c.checkPropertyInitialization(node)
}

func (c *Checker) checkClassForStaticPropertyNameConflicts(node *ast.Node) {
	if c.compilerOptions.GetUseDefineForClassFields() {
		return
	}
	for _, member := range node.Members() {
		memberNameNode := member.Name()
		isStaticMember := ast.IsStatic(member)
		if isStaticMember && memberNameNode != nil {
			memberName, _ := c.getEffectivePropertyNameForPropertyNameNode(memberNameNode)
			switch memberName {
			case "name", "length", "caller", "arguments":
				c.error(
					memberNameNode,
					diagnostics.Static_property_0_conflicts_with_built_in_property_Function_0_of_constructor_function_1,
					memberName,
					c.symbolToString(c.getSymbolOfDeclaration(node)),
				)
			}
		}
	}
}

// Check that type parameter lists are identical across multiple declarations
func (c *Checker) checkTypeParameterListsIdentical(symbol *ast.Symbol) {
	if len(symbol.Declarations) == 1 {
		return
	}
	links := c.declaredTypeLinks.Get(symbol)
	if !links.typeParametersChecked {
		links.typeParametersChecked = true
		declarations := c.getClassOrInterfaceDeclarationsOfSymbol(symbol)
		if len(declarations) <= 1 {
			return
		}
		t := c.getDeclaredTypeOfSymbol(symbol)
		if !c.areTypeParametersIdentical(declarations, t.AsInterfaceType().LocalTypeParameters(), (*ast.Node).TypeParameters) {
			// Report an error on every conflicting declaration.
			name := c.symbolToString(symbol)
			for _, declaration := range declarations {
				c.error(declaration.Name(), diagnostics.All_declarations_of_0_must_have_identical_type_parameters, name)
			}
		}
	}
}

func (c *Checker) getClassOrInterfaceDeclarationsOfSymbol(symbol *ast.Symbol) []*ast.Node {
	return core.Filter(symbol.Declarations, func(d *ast.Node) bool {
		return ast.IsClassDeclaration(d) || ast.IsInterfaceDeclaration(d)
	})
}

func (c *Checker) areTypeParametersIdentical(declarations []*ast.Node, targetParameters []*Type, getTypeParameterDeclarations func(node *ast.Node) []*ast.Node) bool {
	maxTypeArgumentCount := len(targetParameters)
	minTypeArgumentCount := c.getMinTypeArgumentCount(targetParameters)
	for _, declaration := range declarations {
		// If this declaration has too few or too many type parameters, we report an error
		sourceParameters := getTypeParameterDeclarations(declaration)
		if len(sourceParameters) < minTypeArgumentCount || len(sourceParameters) > maxTypeArgumentCount {
			return false
		}
		for i, source := range sourceParameters {
			target := targetParameters[i]
			// If the type parameter node does not have the same name as the resolved type
			// parameter at this position, we report an error.
			if source.Name().Text() != target.symbol.Name {
				return false
			}
			// If the type parameter node does not have an identical constraintNode as the resolved
			// type parameter at this position, we report an error.
			constraintNode := source.AsTypeParameterDeclaration().Constraint
			targetConstraint := c.getConstraintOfTypeParameter(target)
			// relax check if later interface augmentation has no constraint, it's more broad and is OK to merge with
			// a more constrained interface (this could be generalized to a full hierarchy check, but that's maybe overkill)
			if constraintNode != nil && targetConstraint != nil && !c.isTypeIdenticalTo(c.getTypeFromTypeNode(constraintNode), targetConstraint) {
				return false
			}
			// If the type parameter node has a default and it is not identical to the default
			// for the type parameter at this position, we report an error.
			defaultNode := source.AsTypeParameterDeclaration().DefaultType
			targetDefault := c.getDefaultFromTypeParameter(target)
			if defaultNode != nil && targetDefault != nil && !c.isTypeIdenticalTo(c.getTypeFromTypeNode(defaultNode), targetDefault) {
				return false
			}
		}
	}
	return true
}

func (c *Checker) checkBaseTypeAccessibility(t *Type, node *ast.Node) {
	signatures := c.getSignaturesOfType(t, SignatureKindConstruct)
	if len(signatures) != 0 {
		declaration := signatures[0].declaration
		if declaration != nil && ast.HasModifier(declaration, ast.ModifierFlagsPrivate) {
			typeClassDeclaration := ast.GetClassLikeDeclarationOfSymbol(t.symbol)
			if !c.isNodeWithinClass(node, typeClassDeclaration) {
				c.error(node, diagnostics.Cannot_extend_a_class_0_Class_constructor_is_marked_as_private, c.getFullyQualifiedName(t.symbol, nil))
			}
		}
	}
}

func (c *Checker) issueMemberSpecificError(node *ast.Node, typeWithThis *Type, baseWithThis *Type, broadDiag *diagnostics.Message) {
	// iterate over all implemented properties and issue errors on each one which isn't compatible, rather than the class as a whole, if possible
	issuedMemberError := false
	for _, member := range node.Members() {
		if ast.IsStatic(member) {
			continue
		}
		if declaredProp := c.getSymbolOfDeclaration(member); declaredProp != nil && declaredProp.Name != ast.InternalSymbolNameComputed {
			prop := c.getPropertyOfType(typeWithThis, declaredProp.Name)
			baseProp := c.getPropertyOfType(baseWithThis, declaredProp.Name)
			if prop != nil && baseProp != nil {
				var diags []*ast.Diagnostic
				if !c.checkTypeAssignableToEx(c.getTypeOfSymbol(prop), c.getTypeOfSymbol(baseProp), core.OrElse(member.Name(), member), nil /*headMessage*/, &diags) {
					c.addDiagnostic(ast.NewDiagnosticChain(diags[0], diagnostics.Property_0_in_type_1_is_not_assignable_to_the_same_property_in_base_type_2, c.symbolToString(declaredProp), c.TypeToString(typeWithThis), c.TypeToString(baseWithThis)))
					issuedMemberError = true
				}
			}
		}
	}
	if !issuedMemberError {
		// check again with diagnostics to generate a less-specific error
		c.checkTypeAssignableTo(typeWithThis, baseWithThis, core.OrElse(node.Name(), node), broadDiag)
	}
}

func (c *Checker) getTypeWithoutSignatures(t *Type) *Type {
	switch {
	case t.flags&TypeFlagsObject != 0:
		resolved := c.resolveStructuredTypeMembers(t)
		if len(resolved.signatures) != 0 {
			result := c.newObjectType(ObjectFlagsAnonymous, t.symbol)
			result.objectFlags |= ObjectFlagsMembersResolved
			result.AsObjectType().members = resolved.members
			result.AsObjectType().properties = resolved.properties
			return result
		}
	case t.flags&TypeFlagsIntersection != 0:
		return c.getIntersectionType(core.Map(t.AsIntersectionType().types, c.getTypeWithoutSignatures))
	}
	return t
}

func (c *Checker) checkKindsOfPropertyMemberOverrides(t *Type, baseType *Type) {
	// TypeScript 1.0 spec (April 2014): 8.2.3
	// A derived class inherits all members from its base class it doesn't override.
	// Inheritance means that a derived class implicitly contains all non - overridden members of the base class.
	// Both public and private property members are inherited, but only public property members can be overridden.
	// A property member in a derived class is said to override a property member in a base class
	// when the derived class property member has the same name and kind(instance or static)
	// as the base class property member.
	// The type of an overriding property member must be assignable(section 3.8.4)
	// to the type of the overridden property member, or otherwise a compile - time error occurs.
	// Base class instance member functions can be overridden by derived class instance member functions,
	// but not by other kinds of members.
	// Base class instance member variables and accessors can be overridden by
	// derived class instance member variables and accessors, but not by other kinds of members.
	// NOTE: assignability is checked in checkClassDeclaration
	type MemberInfo struct {
		missedProperties []string
		baseTypeName     string
		typeName         string
	}
	var notImplementedInfo map[*ast.Node]MemberInfo
basePropertyCheck:
	for _, baseProperty := range c.getPropertiesOfType(baseType) {
		base := c.getTargetSymbol(baseProperty)
		if base.Flags&ast.SymbolFlagsPrototype != 0 {
			continue
		}
		baseSymbol := c.getPropertyOfObjectType(t, base.Name)
		if baseSymbol == nil {
			continue
		}
		derived := c.getTargetSymbol(baseSymbol)
		baseDeclarationFlags := getDeclarationModifierFlagsFromSymbol(base)
		// In order to resolve whether the inherited method was overridden in the base class or not,
		// we compare the Symbols obtained. Since getTargetSymbol returns the symbol on the *uninstantiated*
		// type declaration, derived and base resolve to the same symbol even in the case of generic classes.
		if derived == base {
			// derived class inherits base without override/redeclaration.
			if baseDeclarationFlags&ast.ModifierFlagsAbstract != 0 {
				// It is an error to inherit an abstract member without implementing it or being declared abstract.
				// If there is no declaration for the derived class (as in the case of class expressions),
				// then the class cannot be declared abstract.
				derivedClassDecl := ast.GetClassLikeDeclarationOfSymbol(t.symbol)
				if derivedClassDecl == nil || !ast.HasSyntacticModifier(derivedClassDecl, ast.ModifierFlagsAbstract) {
					// Searches other base types for a declaration that would satisfy the inherited abstract member.
					// (The class may have more than one base type via declaration merging with an interface with the
					// same name.)
					for _, otherBaseType := range c.getBaseTypes(t) {
						if otherBaseType == baseType {
							continue
						}
						if baseSymbol := c.getPropertyOfObjectType(otherBaseType, base.Name); baseSymbol != nil && base != c.getTargetSymbol(baseSymbol) {
							// Derived property exists elsewhere.
							continue basePropertyCheck
						}
					}
					baseTypeName := c.TypeToString(baseType)
					typeName := c.TypeToString(t)
					missedProperties := append(notImplementedInfo[derivedClassDecl].missedProperties, c.symbolToString(baseProperty))
					if notImplementedInfo == nil {
						notImplementedInfo = make(map[*ast.Node]MemberInfo)
					}
					notImplementedInfo[derivedClassDecl] = MemberInfo{
						baseTypeName:     baseTypeName,
						typeName:         typeName,
						missedProperties: missedProperties,
					}
				}
			}
		} else {
			// derived overrides base.
			derivedDeclarationFlags := getDeclarationModifierFlagsFromSymbol(derived)
			if baseDeclarationFlags&ast.ModifierFlagsPrivate != 0 || derivedDeclarationFlags&ast.ModifierFlagsPrivate != 0 {
				// either base or derived property is private - not override, skip it
				continue
			}
			var errorMessage *diagnostics.Message
			basePropertyFlags := base.Flags & ast.SymbolFlagsPropertyOrAccessor
			derivedPropertyFlags := derived.Flags & ast.SymbolFlagsPropertyOrAccessor
			if basePropertyFlags != 0 && derivedPropertyFlags != 0 {
				// property/accessor is overridden with property/accessor
				if base.CheckFlags&ast.CheckFlagsMapped != 0 ||
					derived.ValueDeclaration != nil && ast.IsBinaryExpression(derived.ValueDeclaration) ||
					c.arePropertiesAbstractOrInterface(base, baseDeclarationFlags) {
					// when the base property is abstract or from an interface, base/derived flags don't need to match
					// for intersection properties, this must be true of *any* of the declarations, for others it must be true of *all*
					// same when the derived property is from an assignment
					continue
				}
				overriddenInstanceProperty := basePropertyFlags != ast.SymbolFlagsProperty && derivedPropertyFlags == ast.SymbolFlagsProperty
				overriddenInstanceAccessor := basePropertyFlags == ast.SymbolFlagsProperty && derivedPropertyFlags != ast.SymbolFlagsProperty
				if overriddenInstanceProperty || overriddenInstanceAccessor {
					errorMessage := core.IfElse(overriddenInstanceProperty,
						diagnostics.X_0_is_defined_as_an_accessor_in_class_1_but_is_overridden_here_in_2_as_an_instance_property,
						diagnostics.X_0_is_defined_as_a_property_in_class_1_but_is_overridden_here_in_2_as_an_accessor)
					c.error(core.OrElse(ast.GetNameOfDeclaration(derived.ValueDeclaration), derived.ValueDeclaration), errorMessage, c.symbolToString(base), c.TypeToString(baseType), c.TypeToString(t))
				} else if c.compilerOptions.GetUseDefineForClassFields() {
					uninitialized := core.Find(derived.Declarations, func(d *ast.Node) bool {
						return ast.IsPropertyDeclaration(d) && d.Initializer() == nil
					})
					if uninitialized != nil && derived.Flags&ast.SymbolFlagsTransient == 0 && baseDeclarationFlags&ast.ModifierFlagsAbstract == 0 && derivedDeclarationFlags&ast.ModifierFlagsAbstract == 0 && !core.Some(derived.Declarations, func(d *ast.Node) bool {
						return d.Flags&ast.NodeFlagsAmbient != 0
					}) {
						constructor := ast.FindConstructorDeclaration(ast.GetClassLikeDeclarationOfSymbol(t.symbol))
						propName := uninitialized.Name()
						if isExclamationToken(uninitialized.PostfixToken()) || constructor == nil || !ast.IsIdentifier(propName) || !c.strictNullChecks || !c.isPropertyInitializedInConstructor(propName, t, constructor) {
							errorMessage := diagnostics.Property_0_will_overwrite_the_base_property_in_1_If_this_is_intentional_add_an_initializer_Otherwise_add_a_declare_modifier_or_remove_the_redundant_declaration
							c.error(core.OrElse(ast.GetNameOfDeclaration(derived.ValueDeclaration), derived.ValueDeclaration), errorMessage, c.symbolToString(base), c.TypeToString(baseType))
						}
					}
				}
				// correct case
				continue
			} else if isPrototypeProperty(base) {
				if isPrototypeProperty(derived) || derived.Flags&ast.SymbolFlagsProperty != 0 {
					// method is overridden with method or property -- correct case
					continue
				} else {
					errorMessage = diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_accessor
				}
			} else if base.Flags&ast.SymbolFlagsAccessor != 0 {
				errorMessage = diagnostics.Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_function
			} else {
				errorMessage = diagnostics.Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_function
			}
			c.error(core.OrElse(ast.GetNameOfDeclaration(derived.ValueDeclaration), derived.ValueDeclaration), errorMessage, c.TypeToString(baseType), c.symbolToString(base), c.TypeToString(t))
		}
	}
	for errorNode, memberInfo := range notImplementedInfo {
		switch {
		case len(memberInfo.missedProperties) == 1:
			missedProperty := memberInfo.missedProperties[0]
			if ast.IsClassExpression(errorNode) {
				c.error(errorNode, diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1, missedProperty, memberInfo.baseTypeName)
			} else {
				c.error(errorNode, diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2, memberInfo.typeName, missedProperty, memberInfo.baseTypeName)
			}
		case len(memberInfo.missedProperties) > 5:
			missedProperties := strings.Join(core.Map(memberInfo.missedProperties[:4], func(prop string) string { return "'" + prop + "'" }), ", ")
			remainingMissedProperties := len(memberInfo.missedProperties) - 4
			if ast.IsClassExpression(errorNode) {
				c.error(errorNode, diagnostics.Non_abstract_class_expression_is_missing_implementations_for_the_following_members_of_0_Colon_1_and_2_more, memberInfo.baseTypeName, missedProperties, remainingMissedProperties)
			} else {
				c.error(errorNode, diagnostics.Non_abstract_class_0_is_missing_implementations_for_the_following_members_of_1_Colon_2_and_3_more, memberInfo.typeName, memberInfo.baseTypeName, missedProperties, remainingMissedProperties)
			}
		default:
			missedProperties := strings.Join(core.Map(memberInfo.missedProperties, func(prop string) string { return "'" + prop + "'" }), ", ")
			if ast.IsClassExpression(errorNode) {
				c.error(errorNode, diagnostics.Non_abstract_class_expression_is_missing_implementations_for_the_following_members_of_0_Colon_1, memberInfo.baseTypeName, missedProperties)
			} else {
				c.error(errorNode, diagnostics.Non_abstract_class_0_is_missing_implementations_for_the_following_members_of_1_Colon_2, memberInfo.typeName, memberInfo.baseTypeName, missedProperties)
			}
		}
	}
}

func (c *Checker) arePropertiesAbstractOrInterface(base *ast.Symbol, baseDeclarationFlags ast.ModifierFlags) bool {
	if base.CheckFlags&ast.CheckFlagsSynthetic != 0 {
		return core.Some(base.Declarations, func(d *ast.Node) bool { return c.isPropertyAbstractOrInterface(d, baseDeclarationFlags) })
	}
	return core.Every(base.Declarations, func(d *ast.Node) bool { return c.isPropertyAbstractOrInterface(d, baseDeclarationFlags) })
}

func (c *Checker) isPropertyAbstractOrInterface(declaration *ast.Node, baseDeclarationFlags ast.ModifierFlags) bool {
	return ast.IsInterfaceDeclaration(declaration.Parent) ||
		baseDeclarationFlags&ast.ModifierFlagsAbstract != 0 && (!ast.IsPropertyDeclaration(declaration) || declaration.Initializer() == nil)
}

func (c *Checker) checkMembersForOverrideModifier(node *ast.Node, t *Type, typeWithThis *Type, staticType *Type) {
	var baseWithThis *Type
	baseTypeNode := ast.GetExtendsHeritageClauseElement(node)
	if baseTypeNode != nil {
		baseTypes := c.getBaseTypes(t)
		if len(baseTypes) > 0 {
			baseWithThis = c.getTypeWithThisArgument(core.FirstOrNil(baseTypes), t.AsInterfaceType().thisType, false)
		}
	}
	baseStaticType := c.getBaseConstructorTypeOfClass(t)
	for _, member := range node.Members() {
		if !ast.HasAmbientModifier(member) {
			if ast.IsConstructorDeclaration(member) {
				for _, param := range member.Parameters() {
					if ast.IsParameterPropertyDeclaration(param, member) {
						c.checkMemberForOverrideModifier(node, staticType, baseStaticType, baseWithThis, t, typeWithThis, param)
					}
				}
			} else {
				c.checkMemberForOverrideModifier(node, staticType, baseStaticType, baseWithThis, t, typeWithThis, member)
			}
		}
	}
}

func (c *Checker) checkMemberForOverrideModifier(node *ast.Node, staticType *Type, baseStaticType *Type, baseWithThis *Type, t *Type, typeWithThis *Type, member *ast.Node) {
	isJs := ast.IsInJSFile(node)
	memberHasOverrideModifier := hasOverrideModifier(member)
	if baseWithThis == nil {
		if memberHasOverrideModifier {
			c.error(member, core.IfElse(isJs, diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_its_containing_class_0_does_not_extend_another_class, diagnostics.This_member_cannot_have_an_override_modifier_because_its_containing_class_0_does_not_extend_another_class), c.TypeToString(t))
		}
		return
	}
	if sym := member.Symbol(); memberHasOverrideModifier && sym != nil && sym.ValueDeclaration != nil && ast.IsClassElement(member) && member.Name() != nil && c.isNonBindableDynamicName(member.Name()) {
		c.error(member, core.IfElse(isJs, diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_its_name_is_dynamic, diagnostics.This_member_cannot_have_an_override_modifier_because_its_name_is_dynamic))
		return
	}
	if !memberHasOverrideModifier && !c.compilerOptions.NoImplicitOverride.IsTrue() {
		return
	}
	// Here we have a base class and also an override modifier or no override modifier in noImplicitOverride mode
	symbol := c.getSymbolOfDeclaration(member)
	if symbol == nil {
		return
	}
	memberIsStatic := ast.IsStatic(member)
	thisType := core.IfElse(memberIsStatic, staticType, typeWithThis)
	prop := c.getPropertyOfType(thisType, symbol.Name)
	if prop == nil {
		return
	}
	baseType := core.IfElse(memberIsStatic, baseStaticType, baseWithThis)
	baseProp := c.getPropertyOfType(baseType, symbol.Name)
	if baseProp == nil && memberHasOverrideModifier {
		suggestion := c.getSuggestedSymbolForNonexistentClassMember(ast.SymbolName(symbol), baseType)
		if suggestion != nil {
			c.error(member, core.IfElse(isJs, diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_it_is_not_declared_in_the_base_class_0_Did_you_mean_1, diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0_Did_you_mean_1), c.TypeToString(baseWithThis), c.symbolToString(suggestion))
			return
		}
		c.error(member, core.IfElse(isJs, diagnostics.This_member_cannot_have_a_JSDoc_comment_with_an_override_tag_because_it_is_not_declared_in_the_base_class_0, diagnostics.This_member_cannot_have_an_override_modifier_because_it_is_not_declared_in_the_base_class_0), c.TypeToString(baseWithThis))
		return
	}
	if baseProp != nil && len(baseProp.Declarations) != 0 && !memberHasOverrideModifier && c.compilerOptions.NoImplicitOverride.IsTrue() && node.Flags&ast.NodeFlagsAmbient == 0 {
		baseHasAbstract := core.Some(baseProp.Declarations, ast.HasAbstractModifier)
		if !baseHasAbstract {
			message := core.IfElse(ast.IsParameterDeclaration(member),
				core.IfElse(isJs, diagnostics.This_parameter_property_must_have_a_JSDoc_comment_with_an_override_tag_because_it_overrides_a_member_in_the_base_class_0, diagnostics.This_parameter_property_must_have_an_override_modifier_because_it_overrides_a_member_in_base_class_0),
				core.IfElse(isJs, diagnostics.This_member_must_have_a_JSDoc_comment_with_an_override_tag_because_it_overrides_a_member_in_the_base_class_0, diagnostics.This_member_must_have_an_override_modifier_because_it_overrides_a_member_in_the_base_class_0))
			c.error(member, message, c.TypeToString(baseWithThis))
			return
		}
		if ast.HasAbstractModifier(member) && baseHasAbstract {
			c.error(member, diagnostics.This_member_must_have_an_override_modifier_because_it_overrides_an_abstract_method_that_is_declared_in_the_base_class_0, c.TypeToString(baseWithThis))
		}
	}
}

func (c *Checker) getSuggestedSymbolForNonexistentClassMember(name string, baseType *Type) *ast.Symbol {
	return c.getSpellingSuggestionForName(name, slices.Values(c.getPropertiesOfType(baseType)), ast.SymbolFlagsClassMember)
}

func (c *Checker) checkIndexConstraints(t *Type, symbol *ast.Symbol, isStaticIndex bool) {
	indexInfos := c.getIndexInfosOfType(t)
	if len(indexInfos) == 0 {
		return
	}
	for _, prop := range c.getPropertiesOfObjectType(t) {
		if !(isStaticIndex && prop.Flags&ast.SymbolFlagsPrototype != 0) {
			c.checkIndexConstraintForProperty(t, prop, c.getLiteralTypeFromProperty(prop, TypeFlagsStringOrNumberLiteralOrUnique, true /*includeNonPublic*/), c.getNonMissingTypeOfSymbol(prop))
		}
	}
	typeDeclaration := symbol.ValueDeclaration
	if typeDeclaration != nil && ast.IsClassLike(typeDeclaration) {
		for _, member := range typeDeclaration.Members() {
			// Only process instance properties against instance index signatures and static properties against static index signatures
			if ast.IsStatic(member) == isStaticIndex && !c.hasBindableName(member) {
				symbol := c.getSymbolOfDeclaration(member)
				c.checkIndexConstraintForProperty(t, symbol, c.getTypeOfExpression(member.Name().Expression()), c.getNonMissingTypeOfSymbol(symbol))
			}
		}
	}
	if len(indexInfos) > 1 {
		for _, info := range indexInfos {
			c.checkIndexConstraintForIndexSignature(t, info)
		}
	}
}

func (c *Checker) checkIndexConstraintForProperty(t *Type, prop *ast.Symbol, propNameType *Type, propType *Type) {
	declaration := prop.ValueDeclaration
	name := ast.GetNameOfDeclaration(declaration)
	if name != nil && ast.IsPrivateIdentifier(name) {
		return
	}
	indexInfos := c.getApplicableIndexInfos(t, propNameType)
	if len(indexInfos) == 0 {
		return
	}
	var interfaceDeclaration *ast.Node
	if t.objectFlags&ObjectFlagsInterface != 0 {
		interfaceDeclaration = ast.GetDeclarationOfKind(t.symbol, ast.KindInterfaceDeclaration)
	}
	var propDeclaration *ast.Node
	if declaration != nil && ast.IsBinaryExpression(declaration) || name != nil && ast.IsComputedPropertyName(name) {
		propDeclaration = declaration
	}
	var localPropDeclaration *ast.Node
	if c.getParentOfSymbol(prop) == t.symbol {
		localPropDeclaration = declaration
	}
	for _, info := range indexInfos {
		var localIndexDeclaration *ast.Node
		if info.declaration != nil && c.getParentOfSymbol(c.getSymbolOfDeclaration(info.declaration)) == t.symbol {
			localIndexDeclaration = info.declaration
		}
		// We check only when (a) the property is declared in the containing type, or (b) the applicable index signature is declared
		// in the containing type, or (c) the containing type is an interface and no base interface contains both the property and
		// the index signature (i.e. property and index signature are declared in separate inherited interfaces).
		errorNode := core.OrElse(localPropDeclaration, localIndexDeclaration)
		if errorNode == nil && interfaceDeclaration != nil && !core.Some(c.getBaseTypes(t), func(base *Type) bool {
			return c.getPropertyOfObjectType(base, prop.Name) != nil && c.getIndexTypeOfType(base, info.keyType) != nil
		}) {
			errorNode = interfaceDeclaration
		}
		if errorNode != nil && !c.isTypeAssignableTo(propType, info.valueType) {
			diagnostic := NewDiagnosticForNode(errorNode, diagnostics.Property_0_of_type_1_is_not_assignable_to_2_index_type_3, c.symbolToString(prop), c.TypeToString(propType), c.TypeToString(info.keyType), c.TypeToString(info.valueType))
			if propDeclaration != nil && errorNode != propDeclaration {
				diagnostic.AddRelatedInfo(NewDiagnosticForNode(propDeclaration, diagnostics.X_0_is_declared_here, c.symbolToString(prop)))
			}
			c.addDiagnostic(diagnostic)
		}
	}
}

func (c *Checker) checkIndexConstraintForIndexSignature(t *Type, checkInfo *IndexInfo) {
	declaration := checkInfo.declaration
	indexInfos := c.getApplicableIndexInfos(t, checkInfo.keyType)
	if len(indexInfos) == 0 {
		return
	}
	var interfaceDeclaration *ast.Node
	if t.objectFlags&ObjectFlagsInterface != 0 {
		interfaceDeclaration = ast.GetDeclarationOfKind(t.symbol, ast.KindInterfaceDeclaration)
	}
	var localCheckDeclaration *ast.Node
	if declaration != nil && c.getParentOfSymbol(c.getSymbolOfDeclaration(declaration)) == t.symbol {
		localCheckDeclaration = declaration
	}
	for _, info := range indexInfos {
		if info == checkInfo {
			continue
		}
		var localIndexDeclaration *ast.Node
		if info.declaration != nil && c.getParentOfSymbol(c.getSymbolOfDeclaration(info.declaration)) == t.symbol {
			localIndexDeclaration = info.declaration
		}
		// We check only when (a) the check index signature is declared in the containing type, or (b) the applicable index
		// signature is declared in the containing type, or (c) the containing type is an interface and no base interface contains
		// both index signatures (i.e. the index signatures are declared in separate inherited interfaces).
		errorNode := core.OrElse(localCheckDeclaration, localIndexDeclaration)
		if errorNode == nil && interfaceDeclaration != nil && !core.Some(c.getBaseTypes(t), func(base *Type) bool {
			return c.getIndexInfoOfType(base, checkInfo.keyType) != nil && c.getIndexTypeOfType(base, info.keyType) != nil
		}) {
			errorNode = interfaceDeclaration
		}
		if errorNode != nil && !c.isTypeAssignableTo(checkInfo.valueType, info.valueType) {
			c.error(errorNode, diagnostics.X_0_index_type_1_is_not_assignable_to_2_index_type_3, c.TypeToString(checkInfo.keyType), c.TypeToString(checkInfo.valueType), c.TypeToString(info.keyType), c.TypeToString(info.valueType))
		}
	}
}

func (c *Checker) checkClassOrInterfaceForDuplicateIndexSignatures(node *ast.Node) {
	// Only check the type once
	if links := c.declaredTypeLinks.Get(c.getSymbolOfDeclaration(node)); !links.indexSignaturesChecked {
		links.indexSignaturesChecked = true
		c.checkTypeForDuplicateIndexSignatures(node)
	}
}

func (c *Checker) checkTypeForDuplicateIndexSignatures(node *ast.Node) {
	// TypeScript 1.0 spec (April 2014)
	// 3.7.4: An object type can contain at most one string index signature and one numeric index signature.
	// 8.5: A class declaration can have at most one string index member declaration and one numeric index member declaration
	indexSymbol := c.getIndexSymbol(c.getSymbolOfDeclaration(node))
	if indexSymbol == nil || len(indexSymbol.Declarations) <= 1 {
		return
	}
	indexSignatureMap := make(map[*Type][]*ast.Node)
	for _, declaration := range indexSymbol.Declarations {
		if ast.IsIndexSignatureDeclaration(declaration) {
			parameters := declaration.Parameters()
			if len(parameters) == 1 && parameters[0].Type() != nil {
				for _, t := range c.getTypeFromTypeNode(parameters[0].Type()).Distributed() {
					indexSignatureMap[t] = append(indexSignatureMap[t], declaration)
				}
			}
		}
		// Do nothing for late-bound index signatures: allow these to duplicate one another and explicit indexes
	}
	for t, declarations := range indexSignatureMap {
		if len(declarations) > 1 {
			for _, declaration := range declarations {
				c.error(declaration, diagnostics.Duplicate_index_signature_for_type_0, c.TypeToString(t))
			}
		}
	}
}

func (c *Checker) checkPropertyInitialization(node *ast.Node) {
	if !c.strictNullChecks || !c.strictPropertyInitialization || node.Flags&ast.NodeFlagsAmbient != 0 {
		return
	}
	constructor := ast.FindConstructorDeclaration(node)
	for _, member := range node.Members() {
		if member.ModifierFlags()&ast.ModifierFlagsAmbient != 0 {
			continue
		}
		if !ast.IsStatic(member) && c.isPropertyWithoutInitializer(member) {
			propName := member.Name()
			if ast.IsIdentifier(propName) || ast.IsPrivateIdentifier(propName) || ast.IsComputedPropertyName(propName) {
				t := c.getTypeOfSymbol(c.getSymbolOfDeclaration(member))
				if !(t.flags&TypeFlagsAnyOrUnknown != 0 || c.containsUndefinedType(t)) {
					if constructor == nil || !c.isPropertyInitializedInConstructor(propName, t, constructor) {
						c.error(member.Name(), diagnostics.Property_0_has_no_initializer_and_is_not_definitely_assigned_in_the_constructor, scanner.DeclarationNameToString(propName))
					}
				}
			}
		}
	}
}

func (c *Checker) isPropertyWithoutInitializer(node *ast.Node) bool {
	return ast.IsPropertyDeclaration(node) && !ast.HasAbstractModifier(node) && !isExclamationToken(node.PostfixToken()) && node.Initializer() == nil
}

func (c *Checker) isPropertyInitializedInStaticBlocks(propName *ast.Node, propType *Type, staticBlocks []*ast.Node, startPos int, endPos int) bool {
	for _, staticBlock := range staticBlocks {
		// static block must be within the provided range as they are evaluated in document order (unlike constructors)
		if staticBlock.Pos() >= startPos && staticBlock.Pos() <= endPos {
			reference := c.factory.NewPropertyAccessExpression(c.factory.NewKeywordExpression(ast.KindThisKeyword), nil, propName, ast.NodeFlagsNone)
			reference.Expression().Parent = reference
			reference.Parent = staticBlock
			reference.FlowNodeData().FlowNode = staticBlock.AsClassStaticBlockDeclaration().ReturnFlowNode
			flowType := c.getFlowTypeOfReferenceEx(reference, propType, c.getOptionalType(propType, false), nil, nil)
			if !c.containsUndefinedType(flowType) {
				return true
			}
		}
	}
	return false
}

func (c *Checker) isPropertyInitializedInConstructor(propName *ast.Node, propType *Type, constructor *ast.Node) bool {
	var reference *ast.Node
	if ast.IsComputedPropertyName(propName) {
		reference = c.factory.NewElementAccessExpression(c.factory.NewKeywordExpression(ast.KindThisKeyword), nil, propName.Expression(), ast.NodeFlagsNone)
	} else {
		reference = c.factory.NewPropertyAccessExpression(c.factory.NewKeywordExpression(ast.KindThisKeyword), nil, propName, ast.NodeFlagsNone)
	}
	reference.Expression().Parent = reference
	reference.Parent = constructor
	reference.FlowNodeData().FlowNode = constructor.AsConstructorDeclaration().ReturnFlowNode
	flowType := c.getFlowTypeOfReferenceEx(reference, propType, c.getOptionalType(propType, false), nil, nil)
	return !c.containsUndefinedType(flowType)
}

func (c *Checker) checkInterfaceDeclaration(node *ast.Node) {
	if !c.checkGrammarModifiers(node) {
		c.checkGrammarInterfaceDeclaration(node.AsInterfaceDeclaration())
	}
	if !c.containerAllowsBlockScopedVariable(node.Parent) {
		c.grammarErrorOnNode(node, diagnostics.X_0_declarations_can_only_be_declared_inside_a_block, "interface")
	}
	c.checkTypeParameters(node.TypeParameters())
	c.checkTypeNameIsReserved(node.Name(), diagnostics.Interface_name_cannot_be_0)
	c.checkExportsOnMergedDeclarations(node)
	symbol := c.getSymbolOfDeclaration(node)
	c.checkTypeParameterListsIdentical(symbol)
	// Only check this symbol once
	if links := c.declaredTypeLinks.Get(symbol); !links.interfaceChecked {
		links.interfaceChecked = true
		t := c.getDeclaredTypeOfSymbol(symbol)
		typeWithThis := c.getTypeWithThisArgument(t, nil, false)
		// run subsequent checks only if first set succeeded
		if c.checkInheritedPropertiesAreIdentical(t, node.Name()) {
			for _, baseType := range c.getBaseTypes(t) {
				c.checkTypeAssignableTo(typeWithThis, c.getTypeWithThisArgument(baseType, t.AsInterfaceType().thisType, false), node.Name(), diagnostics.Interface_0_incorrectly_extends_interface_1)
			}
			c.checkIndexConstraints(t, symbol /*isStaticIndex*/, false)
		}
	}
	c.checkObjectTypeForDuplicateDeclarations(node, false /*checkPrivateNames*/)
	for _, heritageElement := range ast.GetExtendsHeritageClauseElements(node) {
		expr := heritageElement.Expression()
		if !ast.IsEntityNameExpression(expr) || ast.IsOptionalChain(expr) {
			c.error(expr, diagnostics.An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments)
		}
		c.checkTypeReferenceNode(heritageElement)
	}
	c.checkSourceElements(node.Members())
	c.checkClassOrInterfaceForDuplicateIndexSignatures(node)
	c.registerForUnusedIdentifiersCheck(node)
}

type InheritanceInfo struct {
	prop           *ast.Symbol
	containingType *Type
}

func (c *Checker) checkInheritedPropertiesAreIdentical(t *Type, typeNode *ast.Node) bool {
	baseTypes := c.getBaseTypes(t)
	if len(baseTypes) < 2 {
		return true
	}
	seen := make(map[string]InheritanceInfo)
	for id, p := range c.resolveDeclaredMembers(t).declaredMembers {
		if c.isNamedMember(p, id) {
			seen[p.Name] = InheritanceInfo{prop: p, containingType: t}
		}
	}
	identical := true
	for _, base := range baseTypes {
		properties := c.getPropertiesOfType(c.getTypeWithThisArgument(base, t.AsInterfaceType().thisType, false))
		for _, prop := range properties {
			if existing, ok := seen[prop.Name]; !ok {
				seen[prop.Name] = InheritanceInfo{prop: prop, containingType: base}
			} else {
				isInheritedProperty := existing.containingType != t
				if isInheritedProperty && !c.isPropertyIdenticalTo(existing.prop, prop) {
					identical = false
					typeName1 := c.TypeToString(existing.containingType)
					typeName2 := c.TypeToString(base)
					errorInfo := NewDiagnosticForNode(typeNode, diagnostics.Named_property_0_of_types_1_and_2_are_not_identical, c.symbolToString(prop), typeName1, typeName2)
					c.addDiagnostic(ast.NewDiagnosticChain(errorInfo, diagnostics.Interface_0_cannot_simultaneously_extend_types_1_and_2, c.TypeToString(t), typeName1, typeName2))
				}
			}
		}
	}
	return identical
}

func (c *Checker) isPropertyIdenticalTo(sourceProp *ast.Symbol, targetProp *ast.Symbol) bool {
	return c.compareProperties(sourceProp, targetProp, c.compareTypesIdentical) != TernaryFalse
}

func (c *Checker) checkEnumDeclaration(node *ast.Node) {
	c.checkGrammarModifiers(node)
	c.checkCollisionsForDeclarationName(node, node.Name())
	c.checkExportsOnMergedDeclarations(node)
	c.checkSourceElements(node.Members())

	if c.shouldCheckErasableSyntax(node) && node.Flags&ast.NodeFlagsAmbient == 0 {
		c.error(node, diagnostics.This_syntax_is_not_allowed_when_erasableSyntaxOnly_is_enabled)
	}

	c.computeEnumMemberValues(node)
	// Spec 2014 - Section 9.3:
	// It isn't possible for one enum declaration to continue the automatic numbering sequence of another,
	// and when an enum type has multiple declarations, only one declaration is permitted to omit a value
	// for the first member.
	//
	// Only perform this check once per symbol
	enumSymbol := c.getSymbolOfDeclaration(node)
	if links := c.declaredTypeLinks.Get(enumSymbol); !links.enumChecked {
		links.enumChecked = true
		if len(enumSymbol.Declarations) > 1 {
			enumIsConst := ast.IsEnumConst(node)
			// check that const is placed\omitted on all enum declarations
			for _, decl := range enumSymbol.Declarations {
				if ast.IsEnumDeclaration(decl) && ast.IsEnumConst(decl) != enumIsConst {
					c.error(ast.GetNameOfDeclaration(decl), diagnostics.Enum_declarations_must_all_be_const_or_non_const)
				}
			}
		}
		seenEnumMissingInitialInitializer := false
		for _, declaration := range enumSymbol.Declarations {
			// return true if we hit a violation of the rule, false otherwise
			if declaration.Kind != ast.KindEnumDeclaration {
				continue
			}
			members := declaration.Members()
			if len(members) == 0 {
				continue
			}
			firstEnumMember := members[0]
			if firstEnumMember.Initializer() == nil {
				if seenEnumMissingInitialInitializer {
					c.error(firstEnumMember.Name(), diagnostics.In_an_enum_with_multiple_declarations_only_one_declaration_can_omit_an_initializer_for_its_first_enum_element)
				} else {
					seenEnumMissingInitialInitializer = true
				}
			}
		}
	}
}

func (c *Checker) checkEnumMember(node *ast.Node) {
	if ast.IsPrivateIdentifier(node.Name()) {
		c.error(node, diagnostics.An_enum_member_cannot_be_named_with_a_private_identifier)
	}
	if node.Initializer() != nil {
		c.checkExpression(node.Initializer())
	}
}

func (c *Checker) checkModuleDeclaration(node *ast.Node) {
	if body := node.Body(); body != nil {
		c.checkSourceElement(body)
		if !ast.IsGlobalScopeAugmentation(node) {
			c.registerForUnusedIdentifiersCheck(node)
		}
	}
	isGlobalAugmentation := ast.IsGlobalScopeAugmentation(node)
	inAmbientContext := node.Flags&ast.NodeFlagsAmbient != 0
	if isGlobalAugmentation && !inAmbientContext {
		c.error(node.Name(), diagnostics.Augmentations_for_the_global_scope_should_have_declare_modifier_unless_they_appear_in_already_ambient_context)
	}
	isAmbientExternalModule := ast.IsAmbientModule(node)
	contextErrorMessage := core.IfElse(isAmbientExternalModule,
		diagnostics.An_ambient_module_declaration_is_only_allowed_at_the_top_level_in_a_file,
		diagnostics.A_namespace_declaration_is_only_allowed_at_the_top_level_of_a_namespace_or_module)
	if c.checkGrammarModuleElementContext(node, contextErrorMessage) {
		// If we hit a module declaration in an illegal context, just bail out to avoid cascading errors.
		return
	}
	if !c.checkGrammarModifiers(node) {
		if !inAmbientContext && ast.IsStringLiteral(node.Name()) {
			c.grammarErrorOnNode(node.Name(), diagnostics.Only_ambient_modules_can_use_quoted_names)
		}
	}
	if ast.IsIdentifier(node.Name()) {
		c.checkCollisionsForDeclarationName(node, node.Name())
		if node.AsModuleDeclaration().Keyword == ast.KindModuleKeyword {
			c.error(node.Name(), diagnostics.A_namespace_declaration_should_not_be_declared_using_the_module_keyword_Please_use_the_namespace_keyword_instead)
		}
	}
	c.checkExportsOnMergedDeclarations(node)
	symbol := c.getSymbolOfDeclaration(node)
	// The following checks only apply on a non-ambient instantiated module declaration.
	if symbol.Flags&ast.SymbolFlagsValueModule != 0 && !inAmbientContext && isInstantiatedModule(node, c.compilerOptions.ShouldPreserveConstEnums()) {
		if c.shouldCheckErasableSyntax(node) {
			c.error(node, diagnostics.This_syntax_is_not_allowed_when_erasableSyntaxOnly_is_enabled)
		}
		if c.compilerOptions.GetIsolatedModules() && ast.GetSourceFileOfNode(node).ExternalModuleIndicator == nil {
			// This could be loosened a little if needed. The only problem we are trying to avoid is unqualified
			// references to namespace members declared in other files. But use of namespaces is discouraged anyway,
			// so for now we will just not allow them in scripts, which is the only place they can merge cross-file.
			c.error(node.Name(), diagnostics.Namespaces_are_not_allowed_in_global_script_files_when_0_is_enabled_If_this_file_is_not_intended_to_be_a_global_script_set_moduleDetection_to_force_or_add_an_empty_export_statement, c.getIsolatedModulesLikeFlagName())
		}
		if len(symbol.Declarations) > 1 {
			firstNonAmbientClassOrFunc := getFirstNonAmbientClassOrFunctionDeclaration(symbol)
			if firstNonAmbientClassOrFunc != nil {
				if ast.GetSourceFileOfNode(node) != ast.GetSourceFileOfNode(firstNonAmbientClassOrFunc) {
					c.error(node.Name(), diagnostics.A_namespace_declaration_cannot_be_in_a_different_file_from_a_class_or_function_with_which_it_is_merged)
				} else if node.Pos() < firstNonAmbientClassOrFunc.Pos() {
					c.error(node.Name(), diagnostics.A_namespace_declaration_cannot_be_located_prior_to_a_class_or_function_with_which_it_is_merged)
				}
			}
		}
		if c.compilerOptions.VerbatimModuleSyntax.IsTrue() && ast.IsSourceFile(node.Parent) && node.ModifierFlags()&ast.ModifierFlagsExport != 0 && c.program.GetEmitModuleFormatOfFile(node.Parent.AsSourceFile()) == core.ModuleKindCommonJS {
			exportModifier := core.Find(node.ModifierNodes(), func(m *ast.Node) bool { return m.Kind == ast.KindExportKeyword })
			c.error(exportModifier, diagnostics.A_top_level_export_modifier_cannot_be_used_on_value_declarations_in_a_CommonJS_module_when_verbatimModuleSyntax_is_enabled)
		}
	}
	if isAmbientExternalModule {
		if ast.IsExternalModuleAugmentation(node) {
			// body of the augmentation should be checked for consistency only if augmentation was applied to its target (either global scope or module)
			// otherwise we'll be swamped in cascading errors.
			// We can detect if augmentation was applied using following rules:
			// - augmentation for a global scope is always applied
			// - augmentation for some external module is applied if symbol for augmentation is merged (it was combined with target module).
			checkBody := isGlobalAugmentation || c.getSymbolOfDeclaration(node).Flags&ast.SymbolFlagsTransient != 0
			if checkBody && node.Body() != nil {
				for _, statement := range node.Body().Statements() {
					c.checkModuleAugmentationElement(statement)
				}
			}
		} else if ast.IsGlobalSourceFile(node.Parent) {
			if isGlobalAugmentation {
				c.error(node.Name(), diagnostics.Augmentations_for_the_global_scope_can_only_be_directly_nested_in_external_modules_or_ambient_module_declarations)
			} else if tspath.IsExternalModuleNameRelative(node.Name().Text()) {
				c.error(node.Name(), diagnostics.Ambient_module_declaration_cannot_specify_relative_module_name)
			}
		} else {
			if isGlobalAugmentation {
				c.error(node.Name(), diagnostics.Augmentations_for_the_global_scope_can_only_be_directly_nested_in_external_modules_or_ambient_module_declarations)
			} else {
				// Node is not an augmentation and is not located on the script level.
				// This means that this is declaration of ambient module that is located in other module or namespace which is prohibited.
				c.error(node.Name(), diagnostics.Ambient_modules_cannot_be_nested_in_other_modules_or_namespaces)
			}
		}
	}
}

func isInstantiatedModule(node *ast.Node, preserveConstEnums bool) bool {
	moduleState := ast.GetModuleInstanceState(node)
	return moduleState == ast.ModuleInstanceStateInstantiated || preserveConstEnums && moduleState == ast.ModuleInstanceStateConstEnumOnly
}

func getFirstNonAmbientClassOrFunctionDeclaration(symbol *ast.Symbol) *ast.Node {
	for _, declaration := range symbol.Declarations {
		if (ast.IsClassDeclaration(declaration) || ast.IsFunctionDeclaration(declaration) && ast.NodeIsPresent(declaration.Body())) && declaration.Flags&ast.NodeFlagsAmbient == 0 {
			return declaration
		}
	}
	return nil
}

func (c *Checker) getIsolatedModulesLikeFlagName() string {
	return core.IfElse(c.compilerOptions.VerbatimModuleSyntax.IsTrue(), "verbatimModuleSyntax", "isolatedModules")
}

func (c *Checker) checkModuleAugmentationElement(node *ast.Node) {
	switch node.Kind {
	case ast.KindVariableStatement:
		// error each individual name in variable statement instead of marking the entire variable statement
		for _, decl := range node.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
			c.checkModuleAugmentationElement(decl)
		}
	case ast.KindExportAssignment, ast.KindExportDeclaration:
		c.grammarErrorOnFirstToken(node, diagnostics.Exports_and_export_assignments_are_not_permitted_in_module_augmentations)
	case ast.KindImportEqualsDeclaration:
		// import a = e.x; in module augmentation is ok, but not import a = require('fs)
		if ast.IsInternalModuleImportEqualsDeclaration(node) {
			break
		}
		fallthrough
	case ast.KindImportDeclaration, ast.KindJSImportDeclaration:
		c.grammarErrorOnFirstToken(node, diagnostics.Imports_are_not_permitted_in_module_augmentations_Consider_moving_them_to_the_enclosing_external_module)
	case ast.KindBindingElement, ast.KindVariableDeclaration:
		name := node.Name()
		if ast.IsBindingPattern(name) {
			for _, el := range name.Elements() {
				// mark individual names in binding pattern
				c.checkModuleAugmentationElement(el)
			}
		}
	}
}

func (c *Checker) checkImportDeclaration(node *ast.ImportDeclarationNode) {
	// Grammar checking
	var diagnostic *diagnostics.Message
	if ast.IsInJSFile(node) {
		diagnostic = diagnostics.An_import_declaration_can_only_be_used_at_the_top_level_of_a_module
	} else {
		diagnostic = diagnostics.An_import_declaration_can_only_be_used_at_the_top_level_of_a_namespace_or_module
	}
	if c.checkGrammarModuleElementContext(node, diagnostic) {
		// If we hit an import declaration in an illegal context, just bail out to avoid cascading errors.
		return
	}
	if !c.checkGrammarModifiers(node) && node.Modifiers() != nil {
		c.grammarErrorOnFirstToken(node, diagnostics.An_import_declaration_cannot_have_modifiers)
	}
	if c.checkExternalImportOrExportDeclaration(node) {
		var resolvedModule *ast.Symbol
		importClause := node.ImportClause()
		moduleSpecifier := node.ModuleSpecifier()
		if importClause != nil && !c.checkGrammarImportClause(importClause.AsImportClause()) {
			if importClause.Name() != nil {
				c.checkImportBinding(importClause)
			}
			var needsImportStar bool
			namedBindings := importClause.AsImportClause().NamedBindings
			if namedBindings != nil {
				if ast.IsNamespaceImport(namedBindings) {
					c.checkImportBinding(namedBindings)
					if c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) == core.ModuleKindCommonJS {
						// import * as ns from "foo";
						needsImportStar = true
						c.checkExternalEmitHelpers(node, ExternalEmitHelpersImportStar)
					}
				} else {
					resolvedModule = c.resolveExternalModuleName(node, node.ModuleSpecifier(), false)
					if resolvedModule != nil {
						for _, binding := range namedBindings.Elements() {
							c.checkImportBinding(binding)
						}
					}
				}
			}
			if importClause.Name() != nil &&
				!needsImportStar &&
				c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) == core.ModuleKindCommonJS {
				// import d from "foo";
				c.checkExternalEmitHelpers(node, ExternalEmitHelpersImportDefault)
			}

			if !importClause.IsTypeOnly() &&
				core.ModuleKindNode18 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext &&
				c.isOnlyImportableAsDefault(moduleSpecifier, resolvedModule) &&
				!hasTypeJsonImportAttribute(node) {
				c.error(moduleSpecifier, diagnostics.Importing_a_JSON_file_into_an_ECMAScript_module_requires_a_type_Colon_json_import_attribute_when_module_is_set_to_0, c.moduleKind.String())
			}
		} else if c.compilerOptions.NoUncheckedSideEffectImports.IsTrueOrUnknown() && importClause == nil {
			ignoreErrors := c.compilerOptions.NoCheck.IsTrue()
			var errorMessage *diagnostics.Message
			if !ignoreErrors {
				errorMessage = diagnostics.Cannot_find_module_or_type_declarations_for_side_effect_import_of_0
			}
			c.resolveExternalModuleNameWorker(node, moduleSpecifier, errorMessage, ignoreErrors, false /*isForAugmentation*/)
		}
	}
	c.checkImportAttributes(node)
}

func (c *Checker) checkExternalImportOrExportDeclaration(node *ast.Node) bool {
	moduleName := ast.GetExternalModuleName(node)
	if moduleName == nil || ast.NodeIsMissing(moduleName) {
		// Should be a parse error.
		return false
	}
	if !ast.IsStringLiteral(moduleName) {
		c.error(moduleName, diagnostics.String_literal_expected)
		return false
	}
	inAmbientExternalModule := ast.IsModuleBlock(node.Parent) && ast.IsAmbientModule(node.Parent.Parent)
	if !ast.IsSourceFile(node.Parent) && !inAmbientExternalModule {
		c.error(moduleName, core.IfElse(ast.IsExportDeclaration(node), diagnostics.Export_declarations_are_not_permitted_in_a_namespace, diagnostics.Import_declarations_in_a_namespace_cannot_reference_a_module))
		return false
	}
	if inAmbientExternalModule && tspath.IsExternalModuleNameRelative(moduleName.Text()) {
		// we have already reported errors on top level imports/exports in external module augmentations in checkModuleDeclaration
		// no need to do this again.
		if !isTopLevelInExternalModuleAugmentation(node) {
			// TypeScript 1.0 spec (April 2013): 12.1.6
			// An ExternalImportDeclaration in an AmbientExternalModuleDeclaration may reference
			// other external modules only through top - level external module names.
			// Relative external module names are not permitted.
			c.error(node, diagnostics.Import_or_export_declaration_in_an_ambient_module_declaration_cannot_reference_module_through_relative_module_name)
			return false
		}
	}
	if !ast.IsImportEqualsDeclaration(node) {
		attributes := ast.GetImportAttributes(node)
		if attributes != nil {
			hasError := false
			for _, attr := range attributes.AsImportAttributes().Attributes.Nodes {
				if !ast.IsStringLiteral(attr.AsImportAttribute().Value) {
					hasError = true
					c.error(attr.AsImportAttribute().Value, diagnostics.Import_attribute_values_must_be_string_literal_expressions)
				}
			}
			return !hasError
		}
	}
	return true
}

func (c *Checker) checkImportBinding(node *ast.Node) {
	c.checkCollisionsForDeclarationName(node, node.Name())
	c.checkAliasSymbol(node)
	if ast.IsImportSpecifier(node) {
		c.checkModuleExportName(node.PropertyName(), true /*allowStringLiteral*/)
		if ast.ModuleExportNameIsDefault(node.PropertyNameOrName()) &&
			c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) == core.ModuleKindCommonJS {
			c.checkExternalEmitHelpers(node, ExternalEmitHelpersImportDefault)
		}
	}
}

func (c *Checker) checkModuleExportName(name *ast.Node, allowStringLiteral bool) {
	if name == nil || name.Kind != ast.KindStringLiteral {
		return
	}
	if !allowStringLiteral {
		c.grammarErrorOnNode(name, diagnostics.Identifier_expected)
	} else if c.moduleKind == core.ModuleKindES2015 || c.moduleKind == core.ModuleKindES2020 {
		if !ast.GetSourceFileOfNode(name).IsDeclarationFile {
			c.grammarErrorOnNode(name, diagnostics.String_literal_import_and_export_names_are_not_supported_when_the_module_flag_is_set_to_es2015_or_es2020)
		}
	}
}

func hasTypeJsonImportAttribute(node *ast.Node) bool {
	attributes := node.AsImportDeclaration().Attributes
	return attributes != nil && core.Some(attributes.AsImportAttributes().Attributes.Nodes, func(attr *ast.Node) bool {
		return attr.Name().Text() == "type" && ast.IsStringLiteralLike(attr.AsImportAttribute().Value) && attr.AsImportAttribute().Value.Text() == "json"
	})
}

func (c *Checker) checkImportAttributes(declaration *ast.Node) {
	node := ast.GetImportAttributes(declaration)
	if node == nil {
		return
	}
	importAttributesType := c.getGlobalImportAttributesTypeChecked()
	if importAttributesType != c.emptyObjectType {
		c.checkTypeAssignableTo(c.getTypeFromImportAttributes(node), c.getNullableType(importAttributesType, TypeFlagsUndefined), node, nil)
	}
	isTypeOnly := ast.IsExclusivelyTypeOnlyImportOrExport(declaration)
	override := c.getResolutionModeOverride(node.AsImportAttributes(), isTypeOnly)
	if isTypeOnly && override != core.ResolutionModeNone {
		return // Other grammar checks do not apply to type-only imports with resolution mode attributes
	}

	if !c.moduleKind.SupportsImportAttributes() {
		c.grammarErrorOnNode(node, diagnostics.Import_attributes_are_only_supported_when_the_module_option_is_set_to_esnext_node18_node20_nodenext_or_preserve)
		return
	}

	if moduleSpecifier := getModuleSpecifierFromNode(declaration); moduleSpecifier != nil {
		if c.getEmitSyntaxForModuleSpecifierExpression(moduleSpecifier) == core.ModuleKindCommonJS {
			c.grammarErrorOnNode(node, diagnostics.Import_attributes_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls)
			return
		}
	}

	if isTypeOnly {
		c.grammarErrorOnNode(node, diagnostics.Import_attributes_cannot_be_used_with_type_only_imports_or_exports)
		return
	}
	if override != core.ResolutionModeNone {
		c.grammarErrorOnNode(node, diagnostics.X_resolution_mode_can_only_be_set_for_type_only_imports)
	}
}

func (c *Checker) getTypeFromImportAttributes(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		symbol := c.newSymbol(ast.SymbolFlagsObjectLiteral, ast.InternalSymbolNameImportAttributes)
		members := make(ast.SymbolTable)
		for _, attr := range node.AsImportAttributes().Attributes.Nodes {
			member := c.newSymbol(ast.SymbolFlagsProperty, attr.Name().Text())
			c.valueSymbolLinks.Get(member).resolvedType = c.getRegularTypeOfLiteralType(c.checkExpression(attr.AsImportAttribute().Value))
			members[member.Name] = member
		}
		t := c.newAnonymousType(symbol, members, nil, nil, nil)
		t.objectFlags |= ObjectFlagsObjectLiteral | ObjectFlagsNonInferrableType
		links.resolvedType = t
	}
	return links.resolvedType
}

func (c *Checker) checkImportEqualsDeclaration(node *ast.Node) {
	diagnostic := core.IfElse(ast.IsInJSFile(node),
		diagnostics.An_import_declaration_can_only_be_used_at_the_top_level_of_a_module,
		diagnostics.An_import_declaration_can_only_be_used_at_the_top_level_of_a_namespace_or_module)
	if c.checkGrammarModuleElementContext(node, diagnostic) {
		return // If we hit an import declaration in an illegal context, just bail out to avoid cascading errors.
	}
	c.checkGrammarModifiers(node)
	if c.shouldCheckErasableSyntax(node) && node.Flags&ast.NodeFlagsAmbient == 0 {
		c.error(node, diagnostics.This_syntax_is_not_allowed_when_erasableSyntaxOnly_is_enabled)
	}
	if ast.IsInternalModuleImportEqualsDeclaration(node) || c.checkExternalImportOrExportDeclaration(node) {
		c.checkImportBinding(node)
		c.markLinkedReferences(node, ReferenceHintExportImportEquals, nil, nil)
		moduleReference := node.AsImportEqualsDeclaration().ModuleReference
		if !ast.IsExternalModuleReference(moduleReference) {
			target := c.resolveAlias(c.getSymbolOfDeclaration(node))
			if target != c.unknownSymbol {
				targetFlags := c.getSymbolFlags(target)
				if targetFlags&ast.SymbolFlagsValue != 0 {
					// Target is a value symbol, check that it is not hidden by a local declaration with the same name
					moduleName := ast.GetFirstIdentifier(moduleReference)
					if c.resolveEntityName(moduleName, ast.SymbolFlagsValue|ast.SymbolFlagsNamespace, false, false, nil).Flags&ast.SymbolFlagsNamespace == 0 {
						c.error(moduleName, diagnostics.Module_0_is_hidden_by_a_local_declaration_with_the_same_name, scanner.DeclarationNameToString(moduleName))
					}
				}
				if targetFlags&ast.SymbolFlagsType != 0 {
					c.checkTypeNameIsReserved(node.Name(), diagnostics.Import_name_cannot_be_0)
				}
			}
			if node.IsTypeOnly() {
				c.grammarErrorOnNode(node, diagnostics.An_import_alias_cannot_use_import_type)
			}
		} else {
			if core.ModuleKindES2015 <= c.moduleKind && c.moduleKind <= core.ModuleKindESNext && !node.IsTypeOnly() && node.Flags&ast.NodeFlagsAmbient == 0 {
				// Import equals declaration cannot be emitted as ESM
				c.grammarErrorOnNode(node, diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead)
			}
		}
	}
}

func (c *Checker) checkExportDeclaration(node *ast.ExportDeclarationNode) {
	diagnostic := core.IfElse(ast.IsInJSFile(node),
		diagnostics.An_export_declaration_can_only_be_used_at_the_top_level_of_a_module,
		diagnostics.An_export_declaration_can_only_be_used_at_the_top_level_of_a_namespace_or_module)
	if c.checkGrammarModuleElementContext(node, diagnostic) {
		return // If we hit an export in an illegal context, just bail out to avoid cascading errors.
	}
	exportDecl := node.AsExportDeclaration()
	if !c.checkGrammarModifiers(node) && exportDecl.Modifiers() != nil {
		c.grammarErrorOnFirstToken(node, diagnostics.An_export_declaration_cannot_have_modifiers)
	}
	c.checkGrammarExportDeclaration(exportDecl)
	if exportDecl.ModuleSpecifier == nil || c.checkExternalImportOrExportDeclaration(node) {
		if exportDecl.ExportClause != nil && !ast.IsNamespaceExport(exportDecl.ExportClause) {
			// export { x, y }
			// export { x, y } from "foo"
			for _, binding := range exportDecl.ExportClause.Elements() {
				c.checkExportSpecifier(binding)
			}
			inAmbientExternalModule := ast.IsModuleBlock(node.Parent) && ast.IsAmbientModule(node.Parent.Parent)
			inAmbientNamespaceDeclaration := !inAmbientExternalModule && ast.IsModuleBlock(node.Parent) && exportDecl.ModuleSpecifier == nil && node.Flags&ast.NodeFlagsAmbient != 0
			if !ast.IsSourceFile(node.Parent) && !inAmbientExternalModule && !inAmbientNamespaceDeclaration {
				c.error(node, diagnostics.Export_declarations_are_not_permitted_in_a_namespace)
			}
		} else {
			// export * from "foo"
			// export * as ns from "foo";
			moduleSymbol := c.resolveExternalModuleName(node, exportDecl.ModuleSpecifier, false)
			if moduleSymbol != nil && hasExportAssignmentSymbol(moduleSymbol) {
				c.error(exportDecl.ModuleSpecifier, diagnostics.Module_0_uses_export_and_cannot_be_used_with_export_Asterisk, c.symbolToString(moduleSymbol))
			} else if exportDecl.ExportClause != nil {
				c.checkAliasSymbol(exportDecl.ExportClause)
				c.checkModuleExportName(exportDecl.ExportClause.Name(), true /*allowStringLiteral*/)
			}
			if c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) == core.ModuleKindCommonJS {
				if node.AsExportDeclaration().ExportClause != nil {
					// export * as ns from "foo";
					c.checkExternalEmitHelpers(node, ExternalEmitHelpersImportStar)
				} else {
					// export * from "foo"
					c.checkExternalEmitHelpers(node, ExternalEmitHelpersExportStar)
				}
			}
		}
	}
	c.checkImportAttributes(node)
}

func (c *Checker) checkExportSpecifier(node *ast.ExportSpecifierNode) {
	c.checkAliasSymbol(node)
	hasModuleSpecifier := node.Parent.Parent.ModuleSpecifier() != nil
	c.checkModuleExportName(node.PropertyName(), hasModuleSpecifier)
	c.checkModuleExportName(node.Name(), true /*allowStringLiteral*/)

	if !hasModuleSpecifier {
		exportedName := node.PropertyNameOrName()
		if exportedName.Kind == ast.KindStringLiteral {
			return // Skip for invalid syntax like this: export { "x" }
		}
		// find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases)
		symbol := c.resolveName(exportedName, exportedName.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, nil /*nameNotFoundMessage*/, true /*isUse*/, false)
		if symbol != nil && (symbol == c.undefinedSymbol || symbol == c.globalThisSymbol || symbol.Declarations != nil && ast.IsGlobalSourceFile(ast.GetDeclarationContainer(symbol.Declarations[0]))) {
			c.error(exportedName, diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, exportedName.Text())
		} else {
			c.markLinkedReferences(node, ReferenceHintExportSpecifier, nil /*propSymbol*/, nil /*parentType*/)
		}
	} else if c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) == core.ModuleKindCommonJS &&
		ast.ModuleExportNameIsDefault(node.PropertyNameOrName()) {
		c.checkExternalEmitHelpers(node, ExternalEmitHelpersImportDefault)
	}
}

func (c *Checker) checkExportAssignment(node *ast.Node) {
	isExportEquals := node.AsExportAssignment().IsExportEquals
	illegalContextMessage := core.IfElse(isExportEquals,
		diagnostics.An_export_assignment_must_be_at_the_top_level_of_a_file_or_module_declaration,
		diagnostics.A_default_export_must_be_at_the_top_level_of_a_file_or_module_declaration)
	if c.checkGrammarModuleElementContext(node, illegalContextMessage) {
		return // If we hit an export assignment in an illegal context, just bail out to avoid cascading errors.
	}
	if c.shouldCheckErasableSyntax(node) && node.AsExportAssignment().IsExportEquals && node.Flags&ast.NodeFlagsAmbient == 0 {
		c.error(node, diagnostics.This_syntax_is_not_allowed_when_erasableSyntaxOnly_is_enabled)
	}
	container := node.Parent
	if !ast.IsSourceFile(container) {
		container = container.Parent
	}
	if ast.IsModuleDeclaration(container) && !ast.IsAmbientModule(container) {
		// TODO(danielr): should these be grammar errors?
		if isExportEquals {
			c.error(node, diagnostics.An_export_assignment_cannot_be_used_in_a_namespace)
		} else {
			c.error(node, diagnostics.A_default_export_can_only_be_used_in_an_ECMAScript_style_module)
		}
		return
	}
	if !c.checkGrammarModifiers(node) && ast.IsExportAssignment(node) && node.AsExportAssignment().Modifiers() != nil {
		c.grammarErrorOnFirstToken(node, diagnostics.An_export_assignment_cannot_have_modifiers)
	}
	isIllegalExportDefaultInCJS := !isExportEquals && node.Flags&ast.NodeFlagsAmbient == 0 && c.compilerOptions.VerbatimModuleSyntax.IsTrue() && c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) == core.ModuleKindCommonJS
	if ast.IsIdentifier(node.Expression()) {
		id := node.Expression()
		sym := c.getExportSymbolOfValueSymbolIfExported(c.resolveEntityName(id, ast.SymbolFlagsAll, true /*ignoreErrors*/, true /*dontResolveAlias*/, node))
		if sym != nil {
			c.markLinkedReferences(node, ReferenceHintExportAssignment, nil, nil)
			typeOnlyDeclaration := c.getTypeOnlyAliasDeclarationEx(sym, ast.SymbolFlagsValue)
			// If not a value, we're interpreting the identifier as a type export, along the lines of (`export { Id as default }`)
			if c.getSymbolFlags(sym)&ast.SymbolFlagsValue != 0 {
				// However if it is a value, we need to check it's being used correctly
				c.checkExpressionCached(id)
				if !isIllegalExportDefaultInCJS && node.Flags&ast.NodeFlagsAmbient == 0 && c.compilerOptions.VerbatimModuleSyntax.IsTrue() && typeOnlyDeclaration != nil {
					message := core.IfElse(isExportEquals,
						diagnostics.An_export_declaration_must_reference_a_real_value_when_verbatimModuleSyntax_is_enabled_but_0_resolves_to_a_type_only_declaration,
						diagnostics.An_export_default_must_reference_a_real_value_when_verbatimModuleSyntax_is_enabled_but_0_resolves_to_a_type_only_declaration)
					c.error(id, message, id.Text())
				}
			} else if !isIllegalExportDefaultInCJS && node.Flags&ast.NodeFlagsAmbient == 0 && c.compilerOptions.VerbatimModuleSyntax.IsTrue() {
				message := core.IfElse(isExportEquals,
					diagnostics.An_export_declaration_must_reference_a_value_when_verbatimModuleSyntax_is_enabled_but_0_only_refers_to_a_type,
					diagnostics.An_export_default_must_reference_a_value_when_verbatimModuleSyntax_is_enabled_but_0_only_refers_to_a_type)
				c.error(id, message, id.Text())
			}
			if !isIllegalExportDefaultInCJS && node.Flags&ast.NodeFlagsAmbient == 0 && c.compilerOptions.GetIsolatedModules() && sym.Flags&ast.SymbolFlagsValue == 0 {
				nonLocalMeanings := c.getSymbolFlagsEx(sym, false /*excludeTypeOnlyMeanings*/, true /*excludeLocalMeanings*/)
				if sym.Flags&ast.SymbolFlagsAlias != 0 && nonLocalMeanings&ast.SymbolFlagsType != 0 && nonLocalMeanings&ast.SymbolFlagsValue == 0 && (typeOnlyDeclaration == nil || ast.GetSourceFileOfNode(typeOnlyDeclaration) != ast.GetSourceFileOfNode(node)) {
					// import { SomeType } from "./someModule";
					// export default SomeType; OR
					// export = SomeType;
					message := core.IfElse(isExportEquals,
						diagnostics.X_0_resolves_to_a_type_and_must_be_marked_type_only_in_this_file_before_re_exporting_when_1_is_enabled_Consider_using_import_type_where_0_is_imported,
						diagnostics.X_0_resolves_to_a_type_and_must_be_marked_type_only_in_this_file_before_re_exporting_when_1_is_enabled_Consider_using_export_type_0_as_default)
					c.error(id, message, id.Text(), c.getIsolatedModulesLikeFlagName())
				} else if typeOnlyDeclaration != nil && ast.GetSourceFileOfNode(typeOnlyDeclaration) != ast.GetSourceFileOfNode(node) {
					// import { SomeTypeOnlyValue } from "./someModule";
					// export default SomeTypeOnlyValue; OR
					// export = SomeTypeOnlyValue;
					message := core.IfElse(isExportEquals,
						diagnostics.X_0_resolves_to_a_type_only_declaration_and_must_be_marked_type_only_in_this_file_before_re_exporting_when_1_is_enabled_Consider_using_import_type_where_0_is_imported,
						diagnostics.X_0_resolves_to_a_type_only_declaration_and_must_be_marked_type_only_in_this_file_before_re_exporting_when_1_is_enabled_Consider_using_export_type_0_as_default)
					c.addTypeOnlyDeclarationRelatedInfo(c.error(id, message, id.Text(), c.getIsolatedModulesLikeFlagName()), typeOnlyDeclaration, id.Text())
				}
			}
		} else {
			c.checkExpressionCached(id)
			// doesn't resolve, check as expression to mark as error
		}

	} else {
		c.checkExpressionCached(node.Expression())
	}
	if isIllegalExportDefaultInCJS {
		c.error(node, getVerbatimModuleSyntaxErrorMessage(node))
	}
	c.checkExternalModuleExports(container)
	if typeNode := node.Type(); typeNode != nil && node.Kind == ast.KindExportAssignment {
		t := c.getTypeFromTypeNode(typeNode)
		initializerType := c.checkExpressionCached(node.Expression())
		c.checkTypeAssignableToAndOptionallyElaborate(initializerType, t, node.Expression(), node.Expression(), nil /*headMessage*/, nil)
	}
	if (node.Flags&ast.NodeFlagsAmbient != 0) && !ast.IsEntityNameExpression(node.Expression()) {
		c.grammarErrorOnNode(node.Expression(), diagnostics.The_expression_of_an_export_assignment_must_be_an_identifier_or_qualified_name_in_an_ambient_context)
	}
	if isExportEquals {
		// Forbid export= in esm implementation files, and esm mode declaration files
		if c.moduleKind >= core.ModuleKindES2015 && c.moduleKind != core.ModuleKindPreserve && ((node.Flags&ast.NodeFlagsAmbient != 0 && c.program.GetImpliedNodeFormatForEmit(ast.GetSourceFileOfNode(node)) == core.ModuleKindESNext) || (node.Flags&ast.NodeFlagsAmbient == 0 && c.program.GetImpliedNodeFormatForEmit(ast.GetSourceFileOfNode(node)) != core.ModuleKindCommonJS)) {
			// export assignment is not supported in es6 modules
			c.grammarErrorOnNode(node, diagnostics.Export_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_export_default_or_another_module_format_instead)
		} else if c.moduleKind == core.ModuleKindSystem && node.Flags&ast.NodeFlagsAmbient == 0 {
			// system modules does not support export assignment
			c.grammarErrorOnNode(node, diagnostics.Export_assignment_is_not_supported_when_module_flag_is_system)
		}
	}
}

func getVerbatimModuleSyntaxErrorMessage(node *ast.Node) *diagnostics.Message {
	sourceFile := ast.GetSourceFileOfNode(node)
	fileName := sourceFile.FileName()

	// Check if the file is .cts or .cjs (CommonJS-specific extensions)
	if tspath.FileExtensionIsOneOf(fileName, []string{tspath.ExtensionCts, tspath.ExtensionCjs}) {
		return diagnostics.ECMAScript_imports_and_exports_cannot_be_written_in_a_CommonJS_file_under_verbatimModuleSyntax
	}
	// For .ts, .tsx, .js, etc.
	return diagnostics.ECMAScript_imports_and_exports_cannot_be_written_in_a_CommonJS_file_under_verbatimModuleSyntax_Adjust_the_type_field_in_the_nearest_package_json_to_make_this_file_an_ECMAScript_module_or_adjust_your_verbatimModuleSyntax_module_and_moduleResolution_settings_in_TypeScript
}

func (c *Checker) checkExternalModuleExports(node *ast.Node) {
	moduleSymbol := c.getSymbolOfDeclaration(node)
	links := c.moduleSymbolLinks.Get(moduleSymbol)
	if !links.exportsChecked {
		exportEqualsSymbol := moduleSymbol.Exports[ast.InternalSymbolNameExportEquals]
		// An export assignment is in error if (a) the module exports value members or (b) if the module exports type or
		// namespace members and the exported entity also exports type or namespace members.
		if exportEqualsSymbol != nil && (c.hasExportedMembersOfKind(moduleSymbol, ast.SymbolFlagsValue) || c.hasShadowedNamespace(exportEqualsSymbol)) {
			declaration := core.OrElse(c.getDeclarationOfAliasSymbol(exportEqualsSymbol), exportEqualsSymbol.ValueDeclaration)
			if declaration != nil && !isTopLevelInExternalModuleAugmentation(declaration) {
				c.error(declaration, diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements)
			}
		}
		// Checks for export * conflicts
		for id, symbol := range c.getExportsOfModule(moduleSymbol) {
			if id == ast.InternalSymbolNameExportStar {
				continue
			}
			// ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries.
			// (TS Exceptions: namespaces, function overloads, enums, and interfaces)
			if symbol.Flags&(ast.SymbolFlagsNamespace|ast.SymbolFlagsEnum) != 0 {
				continue
			}
			exportedDeclarationsCount := core.CountWhere(symbol.Declarations, func(d *ast.Node) bool {
				return isNotOverload(d) && !ast.IsAccessor(d) && !ast.IsInterfaceDeclaration(d)
			})
			if symbol.Flags&ast.SymbolFlagsTypeAlias != 0 && exportedDeclarationsCount <= 2 {
				// it is legal to merge type alias with other values
				// so count should be either 1 (just type alias) or 2 (type alias + merged value)
				continue
			}
			if exportedDeclarationsCount > 1 && !core.Every(symbol.Declarations, func(node *ast.Node) bool {
				return ast.GetAssignmentDeclarationKind(node) == ast.JSDeclarationKindExportsProperty
			}) {
				for _, declaration := range symbol.Declarations {
					if isNotOverload(declaration) {
						c.error(declaration, diagnostics.Cannot_redeclare_exported_variable_0, id)
					}
				}
			}
		}
		links.exportsChecked = true
	}
}

func (c *Checker) hasExportedMembersOfKind(moduleSymbol *ast.Symbol, kind ast.SymbolFlags) bool {
	for _, symbol := range moduleSymbol.Exports {
		if symbol.Name != ast.InternalSymbolNameExportEquals && c.getSymbolFlags(symbol)&kind != 0 {
			return true
		}
	}
	return false
}

func (c *Checker) hasShadowedNamespace(symbol *ast.Symbol) bool {
	if symbol.Flags&ast.SymbolFlagsNamespaceModule != 0 && symbol.Flags&ast.SymbolFlagsAlias != 0 {
		if target := c.resolveAlias(symbol); target.Flags&ast.SymbolFlagsNamespace != 0 && c.hasExportedMembersOfKind(target, ast.SymbolFlagsType|ast.SymbolFlagsNamespace) {
			return true
		}
	}
	return false
}

func isNotOverload(node *ast.Node) bool {
	return !ast.IsFunctionDeclaration(node) && !ast.IsMethodDeclaration(node) || node.Body() != nil
}

func (c *Checker) checkMissingDeclaration(node *ast.Node) {
	c.checkDecorators(node)
}

func (c *Checker) checkVariableStatement(node *ast.Node) {
	varStatement := node.AsVariableStatement()
	declarationList := varStatement.DeclarationList
	if !c.checkGrammarModifiers(node) && !c.checkGrammarVariableDeclarationList(declarationList.AsVariableDeclarationList()) {
		c.checkGrammarForDisallowedBlockScopedVariableStatement(varStatement)
	}
	c.checkVariableDeclarationList(declarationList)
}

func (c *Checker) checkVariableDeclarationList(node *ast.Node) {
	blockScopeKind := ast.GetCombinedNodeFlags(node) & ast.NodeFlagsBlockScoped
	if (blockScopeKind == ast.NodeFlagsUsing || blockScopeKind == ast.NodeFlagsAwaitUsing) && c.languageVersion < LanguageFeatureMinimumTarget.UsingAndAwaitUsing {
		c.checkExternalEmitHelpers(node, ExternalEmitHelpersAddDisposableResourceAndDisposeResources)
	}
	c.checkSourceElements(node.AsVariableDeclarationList().Declarations.Nodes)
}

func (c *Checker) checkVariableDeclaration(node *ast.Node) {
	if tr := c.tracer; tr != nil {
		defer tr.Push(tracing.PhaseCheck, "checkVariableDeclaration", map[string]any{"kind": node.Kind, "pos": node.Pos(), "end": node.End(), "path": ast.GetSourceFileOfNode(node).FileName()}, false)()
	}
	c.checkGrammarVariableDeclaration(node.AsVariableDeclaration())
	c.checkVariableLikeDeclaration(node)
}

// Check variable, parameter, or property declaration
func (c *Checker) checkVariableLikeDeclaration(node *ast.Node) {
	c.checkDecorators(node)
	name := node.Name()
	if name == nil {
		return // Missing array binding elements have no name
	}
	typeNode := node.Type()
	initializer := node.Initializer()
	if !ast.IsBindingElement(node) {
		c.checkSourceElement(typeNode)
	}
	// For a computed property, just check the initializer and exit
	// Do not use hasDynamicName here, because that returns false for well known symbols.
	// We want to perform checkComputedPropertyName for all computed properties, including
	// well known symbols.
	if ast.IsComputedPropertyName(name) {
		c.checkComputedPropertyName(name)
		if initializer != nil {
			c.checkExpressionCached(initializer)
		}
	}
	if ast.IsBindingElement(node) {
		propName := node.PropertyName()
		if propName != nil && ast.IsIdentifier(node.Name()) && ast.IsPartOfParameterDeclaration(node) && ast.NodeIsMissing(ast.GetContainingFunction(node).Body()) {
			// type F = ({a: string}) => void;
			//               ^^^^^^
			// variable renaming in function type notation is confusing,
			// so we forbid it even if noUnusedLocals is not enabled
			c.renamedBindingElementsInTypes = append(c.renamedBindingElementsInTypes, node)
			return
		}
		if ast.IsObjectBindingPattern(node.Parent) && hasDotDotDotToken(node) && c.languageVersion < LanguageFeatureMinimumTarget.ObjectSpreadRest {
			c.checkExternalEmitHelpers(node, ExternalEmitHelpersRest)
		}
		// check computed properties inside property names of binding elements
		if propName != nil && ast.IsComputedPropertyName(propName) {
			c.checkComputedPropertyName(propName)
		}
		// check private/protected variable access
		parent := node.Parent.Parent
		parentCheckMode := core.IfElse(hasDotDotDotToken(node), CheckModeRestBindingElement, CheckModeNormal)
		parentType := c.getTypeForBindingElementParent(parent, parentCheckMode)
		propNameName := node.PropertyNameOrName()
		if parentType != nil && !ast.IsBindingPattern(propNameName) {
			exprType := c.getLiteralTypeFromPropertyName(propNameName)
			if isTypeUsableAsPropertyName(exprType) {
				nameText := getPropertyNameFromType(exprType)
				property := c.getPropertyOfType(parentType, nameText)
				if property != nil {
					c.markPropertyAsReferenced(property, nil /*nodeForCheckWriteOnly*/, false /*isSelfTypeAccess*/)
					// A destructuring is never a write-only reference.
					c.checkPropertyAccessibility(node, parent.Initializer() != nil && parent.Initializer().Kind == ast.KindSuperKeyword, false /*writing*/, parentType, property)
				}
			}
		}
	}
	// For a binding pattern, check contained binding elements
	if ast.IsBindingPattern(name) {
		c.checkSourceElements(name.Elements())
	}
	// For a parameter declaration with an initializer, error and exit if the containing function doesn't have a body
	if initializer != nil && ast.IsPartOfParameterDeclaration(node) && ast.NodeIsMissing(ast.GetContainingFunction(node).Body()) {
		c.error(node, diagnostics.A_parameter_initializer_is_only_allowed_in_a_function_or_constructor_implementation)
		return
	}
	// For a binding pattern, validate the initializer and exit
	if ast.IsBindingPattern(name) {
		if isInAmbientOrTypeNode(node) {
			return
		}
		needCheckInitializer := initializer != nil && node.Parent.Parent.Kind != ast.KindForInStatement
		needCheckWidenedType := !core.Some(name.Elements(), func(n *ast.Node) bool { return n.Name() != nil })
		if needCheckInitializer || needCheckWidenedType {
			// Don't validate for-in initializer as it is already an error
			widenedType := c.getWidenedTypeForVariableLikeDeclaration(node, false /*reportErrors*/)
			if needCheckInitializer {
				initializerType := c.checkExpressionCached(initializer)
				if c.strictNullChecks && needCheckWidenedType {
					c.checkNonNullNonVoidType(initializerType, node)
				} else {
					c.checkTypeAssignableToAndOptionallyElaborate(initializerType, c.getWidenedTypeForVariableLikeDeclaration(node, false), node, initializer, nil, nil)
				}
			}
			// check the binding pattern with empty elements
			if needCheckWidenedType {
				if ast.IsArrayBindingPattern(name) {
					c.checkIteratedTypeOrElementType(IterationUseDestructuring, widenedType, c.undefinedType, node)
				} else if c.strictNullChecks {
					c.checkNonNullNonVoidType(widenedType, node)
				}
			}
		}
		return
	}
	// For a commonjs `const x = require`, validate the alias and exit
	symbol := c.getSymbolOfDeclaration(node)
	if symbol.Flags&ast.SymbolFlagsAlias != 0 && ast.IsVariableDeclarationInitializedToRequire(node) {
		c.checkAliasSymbol(node)
		return
	}
	if ast.IsBigIntLiteral(name) {
		c.error(name, diagnostics.A_bigint_literal_cannot_be_used_as_a_property_name)
	}
	t := c.convertAutoToAny(c.getTypeOfSymbol(symbol))
	if node == symbol.ValueDeclaration {
		// Node is the primary declaration of the symbol, just validate the initializer
		// Don't validate for-in initializer as it is already an error
		if initializer != nil && !ast.IsForInStatement(node.Parent.Parent) {
			initializerType := c.checkExpressionCached(initializer)
			c.checkTypeAssignableToAndOptionallyElaborate(initializerType, t, node, initializer, nil /*headMessage*/, nil)
			blockScopeKind := c.getCombinedNodeFlagsCached(node) & ast.NodeFlagsBlockScoped
			if blockScopeKind == ast.NodeFlagsAwaitUsing {
				globalAsyncDisposableType := c.getGlobalAsyncDisposableType()
				globalDisposableType := c.getGlobalDisposableType()
				if globalAsyncDisposableType != c.emptyObjectType && globalDisposableType != c.emptyObjectType {
					optionalDisposableType := c.getUnionType([]*Type{globalAsyncDisposableType, globalDisposableType, c.nullType, c.undefinedType})
					c.checkTypeAssignableTo(c.widenTypeForVariableLikeDeclaration(initializerType, node, false), optionalDisposableType, initializer,
						diagnostics.The_initializer_of_an_await_using_declaration_must_be_either_an_object_with_a_Symbol_asyncDispose_or_Symbol_dispose_method_or_be_null_or_undefined)
				}
			} else if blockScopeKind == ast.NodeFlagsUsing {
				globalDisposableType := c.getGlobalDisposableType()
				if globalDisposableType != c.emptyObjectType {
					optionalDisposableType := c.getUnionType([]*Type{globalDisposableType, c.nullType, c.undefinedType})
					c.checkTypeAssignableTo(c.widenTypeForVariableLikeDeclaration(initializerType, node, false), optionalDisposableType, initializer,
						diagnostics.The_initializer_of_a_using_declaration_must_be_either_an_object_with_a_Symbol_dispose_method_or_be_null_or_undefined)
				}
			}
		}
		if len(symbol.Declarations) > 1 {
			if core.Some(symbol.Declarations, func(d *ast.Declaration) bool {
				return d != node && ast.IsVariableLike(d) && !c.areDeclarationFlagsIdentical(d, node)
			}) {
				c.error(name, diagnostics.All_declarations_of_0_must_have_identical_modifiers, scanner.DeclarationNameToString(name))
			}
		}
	} else {
		// Node is a secondary declaration, check that type is identical to primary declaration and check that
		// initializer is consistent with type associated with the node
		declarationType := c.convertAutoToAny(c.getWidenedTypeForVariableLikeDeclaration(node, false))
		if !c.isErrorType(t) && !c.isErrorType(declarationType) && !c.isTypeIdenticalTo(t, declarationType) && symbol.Flags&ast.SymbolFlagsAssignment == 0 {
			c.errorNextVariableOrPropertyDeclarationMustHaveSameType(symbol.ValueDeclaration, t, node, declarationType)
		}
		if initializer != nil {
			c.checkTypeAssignableToAndOptionallyElaborate(c.checkExpressionCached(initializer), declarationType, node, initializer, nil /*headMessage*/, nil)
		}
		if symbol.ValueDeclaration != nil && !c.areDeclarationFlagsIdentical(node, symbol.ValueDeclaration) {
			c.error(name, diagnostics.All_declarations_of_0_must_have_identical_modifiers, scanner.DeclarationNameToString(name))
		}
	}
	if !ast.IsPropertyDeclaration(node) && !ast.IsPropertySignatureDeclaration(node) {
		// We know we don't have a binding pattern or computed name here
		c.checkExportsOnMergedDeclarations(node)
		if ast.IsVariableDeclaration(node) || ast.IsBindingElement(node) {
			c.checkVarDeclaredNamesNotShadowed(node)
		}
		c.checkCollisionsForDeclarationName(node, node.Name())
	}
}

func (c *Checker) errorNextVariableOrPropertyDeclarationMustHaveSameType(firstDeclaration *ast.Declaration, firstType *Type, nextDeclaration *ast.Declaration, nextType *Type) {
	nextDeclarationName := ast.GetNameOfDeclaration(nextDeclaration)
	message := core.IfElse(ast.IsPropertyDeclaration(nextDeclaration) || ast.IsPropertySignatureDeclaration(nextDeclaration),
		diagnostics.Subsequent_property_declarations_must_have_the_same_type_Property_0_must_be_of_type_1_but_here_has_type_2,
		diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2)
	declName := scanner.DeclarationNameToString(nextDeclarationName)
	err := c.error(nextDeclarationName, message, declName, c.TypeToString(firstType), c.TypeToString(nextType))
	if firstDeclaration != nil {
		err.AddRelatedInfo(createDiagnosticForNode(firstDeclaration, diagnostics.X_0_was_also_declared_here, declName))
	}
}

func (c *Checker) checkVarDeclaredNamesNotShadowed(node *ast.Node) {
	// - ScriptBody : StatementList
	// It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList
	// also occurs in the VarDeclaredNames of StatementList.

	// - Block : { StatementList }
	// It is a Syntax Error if any element of the LexicallyDeclaredNames of StatementList
	// also occurs in the VarDeclaredNames of StatementList.

	// Variable declarations are hoisted to the top of their function scope. They can shadow
	// block scoped declarations, which bind tighter. this will not be flagged as duplicate definition
	// by the binder as the declaration scope is different.
	// A non-initialized declaration is a no-op as the block declaration will resolve before the var
	// declaration. the problem is if the declaration has an initializer. this will act as a write to the
	// block declared value. this is fine for let, but not const.
	// Only consider declarations with initializers, uninitialized const declarations will not
	// step on a let/const variable.
	// Do not consider const and const declarations, as duplicate block-scoped declarations
	// are handled by the binder.
	// We are only looking for const declarations that step on let\const declarations from a
	// different scope. e.g.:
	//      {
	//          const x = 0; // localDeclarationSymbol obtained after name resolution will correspond to this declaration
	//          const x = 0; // symbol for this declaration will be 'symbol'
	//      }

	// skip block-scoped variables and parameters
	if (c.getCombinedNodeFlagsCached(node)&ast.NodeFlagsBlockScoped) != 0 || ast.IsPartOfParameterDeclaration(node) {
		return
	}
	// NOTE: in ES6 spec initializer is required in variable declarations where name is binding pattern
	// so we'll always treat binding elements as initialized
	symbol := c.getSymbolOfDeclaration(node)
	name := node.Name()
	if symbol.Flags&ast.SymbolFlagsFunctionScopedVariable != 0 {
		if !ast.IsIdentifier(name) {
			panic("Identifier expected")
		}
		localDeclarationSymbol := c.resolveName(node, name.Text(), ast.SymbolFlagsVariable, nil /*nameNotFoundMessage*/, false /*isUse*/, false)
		if localDeclarationSymbol != nil && localDeclarationSymbol != symbol && localDeclarationSymbol.Flags&ast.SymbolFlagsBlockScopedVariable != 0 {
			if c.getDeclarationNodeFlagsFromSymbol(localDeclarationSymbol)&ast.NodeFlagsBlockScoped != 0 {
				varDeclList := ast.FindAncestorKind(localDeclarationSymbol.ValueDeclaration, ast.KindVariableDeclarationList)
				var container *ast.Node
				if ast.IsVariableStatement(varDeclList.Parent) && varDeclList.Parent.Parent != nil {
					container = varDeclList.Parent.Parent
				}
				// names of block-scoped and function scoped variables can collide only
				// if block scoped variable is defined in the function\module\source file scope (because of variable hoisting)
				namesShareScope := container != nil && (ast.IsBlock(container) && ast.IsFunctionLike(container.Parent) ||
					ast.IsModuleBlock(container) || ast.IsModuleDeclaration(container) || ast.IsSourceFile(container))
				// here we know that function scoped variable is "shadowed" by block scoped one
				// a var declaration can't hoist past a lexical declaration and it results in a SyntaxError at runtime
				if !namesShareScope {
					name := c.symbolToString(localDeclarationSymbol)
					c.error(node, diagnostics.Cannot_initialize_outer_scoped_variable_0_in_the_same_scope_as_block_scoped_declaration_1, name, name)
				}
			}
		}
	}
}

func (c *Checker) checkDecorators(node *ast.Node) {
	// skip this check for nodes that cannot have decorators. These should have already had an error reported by
	// checkGrammarModifiers.
	if !ast.CanHaveDecorators(node) || !ast.HasDecorators(node) || !ast.NodeCanBeDecorated(c.legacyDecorators, node, node.Parent, node.Parent.Parent) {
		return
	}
	firstDecorator := core.Find(node.ModifierNodes(), ast.IsDecorator)
	if firstDecorator == nil {
		return
	}
	if c.legacyDecorators {
		c.checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpersDecorate)
		if ast.IsParameterDeclaration(node) {
			c.checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpersParam)
		}
	} else if c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators {
		c.checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpersESDecorateAndRunInitializers)
		if ast.IsClassDeclaration(node) {
			if node.Name() == nil || c.getFirstTransformableStaticClassElement(node) != nil {
				c.checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpersSetFunctionName)
			}
		} else if !ast.IsClassExpression(node) {
			name := node.Name()
			if ast.IsPrivateIdentifier(name) && (ast.IsMethodDeclaration(node) || ast.IsAccessor(node) || ast.IsAutoAccessorPropertyDeclaration(node)) {
				c.checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpersSetFunctionName)
			}
			if ast.IsComputedPropertyName(name) {
				c.checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpersPropKey)
			}
		}
	}
	c.markLinkedReferences(node, ReferenceHintDecorator, nil, nil)
	for _, modifier := range node.ModifierNodes() {
		if ast.IsDecorator(modifier) {
			c.checkDecorator(modifier)
		}
	}
}

func (c *Checker) checkDecorator(node *ast.Node) {
	c.checkGrammarDecorator(node.AsDecorator())
	signature := c.getResolvedSignature(node, nil, CheckModeNormal)
	c.checkDeprecatedSignature(signature, node)
	returnType := c.getReturnTypeOfSignature(signature)
	if returnType.flags&TypeFlagsAny != 0 {
		return
	}
	// if we fail to get a signature and return type here, we will have already reported a grammar error in `checkDecorators`.
	decoratorSignature := c.getDecoratorCallSignature(node)
	if decoratorSignature == nil || decoratorSignature.resolvedReturnType == nil {
		return
	}
	var headMessage *diagnostics.Message
	expectedReturnType := decoratorSignature.resolvedReturnType
	switch node.Parent.Kind {
	case ast.KindClassDeclaration, ast.KindClassExpression:
		headMessage = diagnostics.Decorator_function_return_type_0_is_not_assignable_to_type_1
	case ast.KindPropertyDeclaration:
		if !c.legacyDecorators {
			headMessage = diagnostics.Decorator_function_return_type_0_is_not_assignable_to_type_1
			break
		}
		fallthrough
	case ast.KindParameter:
		headMessage = diagnostics.Decorator_function_return_type_is_0_but_is_expected_to_be_void_or_any
	case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
		headMessage = diagnostics.Decorator_function_return_type_0_is_not_assignable_to_type_1
	default:
		panic("Unhandled case in checkDecorator")
	}
	c.checkTypeAssignableTo(returnType, expectedReturnType, node.Expression(), headMessage)
}

func (c *Checker) checkIteratedTypeOrElementType(use IterationUse, inputType *Type, sentType *Type, errorNode *ast.Node) *Type {
	if IsTypeAny(inputType) {
		return inputType
	}
	t := c.getIteratedTypeOrElementType(use, inputType, sentType, errorNode, true /*checkAssignability*/)
	if t != nil {
		return t
	}
	return c.anyType
}

func (c *Checker) getIteratedTypeOrElementType(use IterationUse, inputType *Type, sentType *Type, errorNode *ast.Node, checkAssignability bool) *Type {
	allowAsyncIterables := (use & IterationUseAllowsAsyncIterablesFlag) != 0
	if inputType == c.neverType {
		if errorNode != nil {
			c.reportTypeNotIterableError(errorNode, inputType, allowAsyncIterables)
		}
		return nil
	}
	iterableExists := c.getGlobalIterableType() != c.emptyGenericType
	possibleOutOfBounds := c.compilerOptions.NoUncheckedIndexedAccess == core.TSTrue && use&IterationUsePossiblyOutOfBounds != 0
	if iterableExists || allowAsyncIterables {
		iterationTypes := c.getIterationTypesOfIterable(inputType, use, core.IfElse(iterableExists, errorNode, nil))
		if checkAssignability {
			if iterationTypes.nextType != nil {
				var diagnostic *diagnostics.Message
				switch {
				case use&IterationUseForOfFlag != 0:
					diagnostic = diagnostics.Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_for_of_will_always_send_0
				case use&IterationUseSpreadFlag != 0:
					diagnostic = diagnostics.Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_array_spread_will_always_send_0
				case use&IterationUseDestructuringFlag != 0:
					diagnostic = diagnostics.Cannot_iterate_value_because_the_next_method_of_its_iterator_expects_type_1_but_array_destructuring_will_always_send_0
				case use&IterationUseYieldStarFlag != 0:
					diagnostic = diagnostics.Cannot_delegate_iteration_to_value_because_the_next_method_of_its_iterator_expects_type_1_but_the_containing_generator_will_always_send_0
				}
				if diagnostic != nil {
					c.checkTypeAssignableTo(sentType, iterationTypes.nextType, errorNode, diagnostic)
				}
			}
		}
		if iterationTypes.yieldType != nil || iterableExists {
			if iterationTypes.yieldType == nil {
				return nil
			}
			if possibleOutOfBounds {
				return c.includeUndefinedInIndexSignature(iterationTypes.yieldType)
			}
			return iterationTypes.yieldType
		}
	}
	arrayType := inputType
	hasStringConstituent := false
	// If strings are permitted, remove any string-like constituents from the array type.
	// This allows us to find other non-string element types from an array unioned with
	// a string.
	if use&IterationUseAllowsStringInputFlag != 0 {
		if arrayType.flags&TypeFlagsUnion != 0 {
			// After we remove all types that are StringLike, we will know if there was a string constituent
			// based on whether the result of filter is a new array.
			arrayTypes := inputType.Types()
			filteredTypes := core.Filter(arrayTypes, func(t *Type) bool {
				return t.flags&TypeFlagsStringLike == 0
			})
			if !core.Same(filteredTypes, arrayTypes) {
				arrayType = c.getUnionTypeEx(filteredTypes, UnionReductionSubtype, nil, nil)
			}
		} else if arrayType.flags&TypeFlagsStringLike != 0 {
			arrayType = c.neverType
		}
		hasStringConstituent = arrayType != inputType
		if hasStringConstituent {
			// Now that we've removed all the StringLike types, if no constituents remain, then the entire
			// arrayOrStringType was a string.
			if arrayType.flags&TypeFlagsNever != 0 {
				if possibleOutOfBounds {
					return c.includeUndefinedInIndexSignature(c.stringType)
				}
				return c.stringType
			}
		}
	}
	if !c.isArrayLikeType(arrayType) {
		if errorNode != nil {
			// Which error we report depends on whether we allow strings or if there was a
			// string constituent. For example, if the input type is number | string, we
			// want to say that number is not an array type. But if the input was just
			// number and string input is allowed, we want to say that number is not an
			// array type or a string type.
			allowsStrings := use&IterationUseAllowsStringInputFlag != 0 && !hasStringConstituent
			defaultDiagnostic, maybeMissingAwait := c.getIterationDiagnosticDetails(use, inputType, allowsStrings)
			c.errorAndMaybeSuggestAwait(errorNode, maybeMissingAwait && c.getAwaitedTypeOfPromise(arrayType) != nil, defaultDiagnostic, c.TypeToString(arrayType))
		}
		if hasStringConstituent {
			if possibleOutOfBounds {
				return c.includeUndefinedInIndexSignature(c.stringType)
			}
			return c.stringType
		}
		return nil
	}
	arrayElementType := c.getIndexTypeOfType(arrayType, c.numberType)
	if hasStringConstituent && arrayElementType != nil {
		// This is just an optimization for the case where arrayOrStringType is string | string[]
		if arrayElementType.flags&TypeFlagsStringLike != 0 && c.compilerOptions.NoUncheckedIndexedAccess != core.TSTrue {
			return c.stringType
		}
		if possibleOutOfBounds {
			return c.getUnionTypeEx([]*Type{arrayElementType, c.stringType, c.undefinedType}, UnionReductionSubtype, nil, nil)
		}
		return c.getUnionTypeEx([]*Type{arrayElementType, c.stringType}, UnionReductionSubtype, nil, nil)
	}
	if use&IterationUsePossiblyOutOfBounds != 0 {
		return c.includeUndefinedInIndexSignature(arrayElementType)
	}
	return arrayElementType
}

// Gets the requested "iteration type" from a type that is either `Iterable`-like, `Iterator`-like,
// `IterableIterator`-like, or `Generator`-like (for a non-async generator); or `AsyncIterable`-like,
// `AsyncIterator`-like, `AsyncIterableIterator`-like, or `AsyncGenerator`-like (for an async generator).
func (c *Checker) getIterationTypeOfGeneratorFunctionReturnType(typeKind IterationTypeKind, returnType *Type, isAsyncGenerator bool) *Type {
	if IsTypeAny(returnType) {
		return nil
	}
	iterationTypes := c.getIterationTypesOfGeneratorFunctionReturnType(returnType, isAsyncGenerator)
	return iterationTypes.getType(typeKind)
}

func (c *Checker) getIterationTypesOfGeneratorFunctionReturnType(t *Type, isAsyncGenerator bool) IterationTypes {
	if IsTypeAny(t) {
		return IterationTypes{c.anyType, c.anyType, c.anyType}
	}
	use := core.IfElse(isAsyncGenerator, IterationUseAsyncGeneratorReturnType, IterationUseGeneratorReturnType)
	resolver := core.IfElse(isAsyncGenerator, c.asyncIterationTypesResolver, c.syncIterationTypesResolver)
	result := c.getIterationTypesOfIterable(t, use, nil /*errorNode*/)
	if result.hasTypes() {
		return result
	}
	return c.getIterationTypesOfIterator(t, resolver, nil /*errorNode*/, nil /*diagnosticOutput*/)
}

// Gets the requested "iteration type" from an `Iterable`-like or `AsyncIterable`-like type.
func (c *Checker) getIterationTypeOfIterable(use IterationUse, typeKind IterationTypeKind, inputType *Type, errorNode *ast.Node) *Type {
	if IsTypeAny(inputType) {
		return nil
	}
	iterationTypes := c.getIterationTypesOfIterable(inputType, use, errorNode)
	return iterationTypes.getType(typeKind)
}

// Gets the *yield*, *return*, and *next* types from an `Iterable`-like or `AsyncIterable`-like type.
//
// At every level that involves analyzing return types of signatures, we union the return types of all the signatures.
//
// Another thing to note is that at any step of this process, we could run into a dead end,
// meaning either the property is missing, or we run into the anyType. If either of these things
// happens, we return a default `IterationTypes{}` to signal that we could not find the iteration type.
// If a property is missing, and the previous step did not result in `any`, then we also give an error
// if the caller requested it. Then the caller can decide what to do in the case where there is no
// iterated type.
//
// For a **for-of** statement, `yield*` (in a normal generator), spread, array
// destructuring, or normal generator we will only ever look for a `[Symbol.iterator]()`
// method.
//
// For an async generator we will only ever look at the `[Symbol.asyncIterator]()` method.
//
// For a **for-await-of** statement or a `yield*` in an async generator we will look for
// the `[Symbol.asyncIterator]()` method first, and then the `[Symbol.iterator]()` method.
func (c *Checker) getIterationTypesOfIterable(t *Type, use IterationUse, errorNode *ast.Node) IterationTypes {
	t = c.getReducedType(t)
	if IsTypeAny(t) {
		return IterationTypes{c.anyType, c.anyType, c.anyType}
	}
	key := IterationTypesKey{typeId: t.id, use: use & IterationUseCacheFlags}
	// If we are reporting errors and encounter a cached `noIterationTypes`, we should ignore the cached value and continue as if nothing was cached.
	// In addition, we should not cache any new results for this call.
	noCache := false
	if cached, ok := c.iterationTypesCache[key]; ok {
		if errorNode == nil || cached.hasTypes() {
			return cached
		}
		noCache = true
	}
	result := c.getIterationTypesOfIterableWorker(t, use, errorNode, noCache)
	if !noCache {
		c.iterationTypesCache[key] = result
	}
	return result
}

func (c *Checker) getIterationTypesOfIterableWorker(t *Type, use IterationUse, errorNode *ast.Node, noCache bool) IterationTypes {
	if t.flags&TypeFlagsUnion != 0 {
		allIterationTypes := make([]IterationTypes, 0, len(t.Types()))
		for _, constituent := range t.Types() {
			iterationTypes := c.getIterationTypesOfIterableWorker(constituent, use, nil, noCache)
			if !iterationTypes.hasTypes() {
				if errorNode != nil {
					c.addDeferredDiagnostic(func() {
						c.reportTypeNotIterableError(errorNode, t, use&IterationUseAllowsAsyncIterablesFlag != 0)
					})
				}
				return IterationTypes{}
			}
			allIterationTypes = append(allIterationTypes, iterationTypes)
		}
		return c.combineIterationTypes(allIterationTypes)
	}
	var diags []*ast.Diagnostic
	if use&IterationUseAllowsAsyncIterablesFlag != 0 {
		iterationTypes := c.getIterationTypesOfIterableFast(t, c.asyncIterationTypesResolver)
		if iterationTypes.hasTypes() {
			if use&IterationUseForOfFlag != 0 {
				return c.getAsyncFromSyncIterationTypes(iterationTypes, errorNode)
			}
			return iterationTypes
		}
		iterationTypes = c.getIterationTypesOfIterableSlow(t, c.asyncIterationTypesResolver, errorNode, &diags)
		if iterationTypes.hasTypes() {
			if len(diags) != 0 {
				for _, d := range diags {
					c.addDiagnostic(d)
				}
			}
			return iterationTypes
		}
	}
	if use&IterationUseAllowsSyncIterablesFlag != 0 {
		iterationTypes := c.getIterationTypesOfIterableFast(t, c.syncIterationTypesResolver)
		if iterationTypes.hasTypes() {
			if use&IterationUseAllowsAsyncIterablesFlag != 0 {
				return c.getAsyncFromSyncIterationTypes(iterationTypes, errorNode)
			}
			return iterationTypes
		}
		iterationTypes = c.getIterationTypesOfIterableSlow(t, c.syncIterationTypesResolver, errorNode, &diags)
		if iterationTypes.hasTypes() {
			if len(diags) != 0 {
				for _, d := range diags {
					c.addDiagnostic(d)
				}
			}
			if use&IterationUseAllowsAsyncIterablesFlag != 0 {
				return c.getAsyncFromSyncIterationTypes(iterationTypes, errorNode)
			}
			return iterationTypes
		}
	}
	if errorNode != nil {
		// We defer the diagnostic because TypeToString may attempt to resolve symbols that are already being
		// resolved, possibly causing circularities.
		c.addDeferredDiagnostic(func() {
			diagnostic := c.reportTypeNotIterableError(errorNode, t, use&IterationUseAllowsAsyncIterablesFlag != 0)
			for _, d := range diags {
				diagnostic.AddRelatedInfo(d)
			}
		})
	}
	return IterationTypes{}
}

func (c *Checker) getIterationTypesOfIterableFast(t *Type, r *IterationTypesResolver) IterationTypes {
	// As an optimization, if the type is an instantiation of the following global type, then
	// just grab its related type arguments:
	// - `Iterable<T, TReturn, TNext>` or `AsyncIterable<T, TReturn, TNext>`
	// - `IteratorObject<T, TReturn, TNext>` or `AsyncIteratorObject<T, TReturn, TNext>`
	// - `IterableIterator<T, TReturn, TNext>` or `AsyncIterableIterator<T, TReturn, TNext>`
	// - `Generator<T, TReturn, TNext>` or `AsyncGenerator<T, TReturn, TNext>`
	if c.isReferenceToType(t, r.getGlobalIterableType()) ||
		c.isReferenceToType(t, r.getGlobalIteratorObjectType()) ||
		c.isReferenceToType(t, r.getGlobalIterableIteratorType()) ||
		c.isReferenceToType(t, r.getGlobalGeneratorType()) {
		typeArguments := c.getTypeArguments(t)
		return r.getResolvedIterationTypes(typeArguments[0], typeArguments[1], typeArguments[2])
	}
	// As an optimization, if the type is an instantiation of one of the following global types, then
	// just grab the related type argument:
	// - `ArrayIterator<T>`
	// - `MapIterator<T>`
	// - `SetIterator<T>`
	// - `StringIterator<T>`
	// - `ReadableStreamAsyncIterator<T>`
	if c.isReferenceToSomeType(t, r.getGlobalBuiltinIteratorTypes()) {
		return r.getResolvedIterationTypes(c.getTypeArguments(t)[0], c.getBuiltinIteratorReturnType(), c.unknownType)
	}
	return IterationTypes{}
}

func (r *IterationTypesResolver) getResolvedIterationTypes(yieldType *Type, returnType *Type, nextType *Type) IterationTypes {
	return IterationTypes{
		yieldType:  core.OrElse(r.resolveIterationType(yieldType, nil /*errorNode*/), yieldType),
		returnType: core.OrElse(r.resolveIterationType(returnType, nil /*errorNode*/), returnType),
		nextType:   nextType,
	}
}

func (c *Checker) isReferenceToType(t *Type, target *Type) bool {
	return t != nil && t.objectFlags&ObjectFlagsReference != 0 && t.Target() == target
}

func (c *Checker) isReferenceToSomeType(t *Type, targets []*Type) bool {
	return t != nil && t.objectFlags&ObjectFlagsReference != 0 && slices.Contains(targets, t.Target())
}

func (c *Checker) getBuiltinIteratorReturnType() *Type {
	return core.IfElse(c.strictBuiltinIteratorReturn, c.undefinedType, c.anyType)
}

func (iterationTypes *IterationTypes) hasTypes() bool {
	return iterationTypes.yieldType != nil || iterationTypes.returnType != nil || iterationTypes.nextType != nil
}

func (iterationTypes *IterationTypes) getType(typeKind IterationTypeKind) *Type {
	switch typeKind {
	case IterationTypeKindYield:
		return iterationTypes.yieldType
	case IterationTypeKindReturn:
		return iterationTypes.returnType
	case IterationTypeKindNext:
		return iterationTypes.nextType
	}
	panic("Unhandled case in getType(IterationTypeKind)")
}

func (c *Checker) combineIterationTypes(iterationTypes []IterationTypes) IterationTypes {
	return IterationTypes{
		c.getIterationTypeUnion(iterationTypes, func(t IterationTypes) *Type { return t.yieldType }),
		c.getIterationTypeUnion(iterationTypes, func(t IterationTypes) *Type { return t.returnType }),
		c.getIterationTypeUnion(iterationTypes, func(t IterationTypes) *Type { return t.nextType }),
	}
}

func (c *Checker) getIterationTypeUnion(iterationTypes []IterationTypes, f func(IterationTypes) *Type) *Type {
	types := core.MapNonNil(iterationTypes, f)
	if len(types) == 0 {
		return nil
	}
	return c.getUnionType(types)
}

func (c *Checker) getAsyncFromSyncIterationTypes(iterationTypes IterationTypes, errorNode *ast.Node) IterationTypes {
	if !iterationTypes.hasTypes() ||
		iterationTypes.yieldType == c.anyType && iterationTypes.returnType == c.anyType && iterationTypes.nextType == c.anyType {
		return iterationTypes
	}
	// if we're requesting diagnostics, report errors for a missing `Awaited<T>`.
	if errorNode != nil {
		c.getGlobalAwaitedSymbol()
	}
	return IterationTypes{
		yieldType:  core.OrElse(c.getAwaitedTypeEx(iterationTypes.yieldType, errorNode, nil), c.anyType),
		returnType: core.OrElse(c.getAwaitedTypeEx(iterationTypes.returnType, errorNode, nil), c.anyType),
		nextType:   iterationTypes.nextType,
	}
}

// Gets the *yield*, *return*, and *next* types of an `Iterable`-like or `AsyncIterable`-like
// type from its members.
//
// If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` with non-nil
// members is returned. Otherwise, a default `IterationTypes{}` is returned.
//
// NOTE: You probably don't want to call this directly and should be calling
// `getIterationTypesOfIterable` instead.
func (c *Checker) getIterationTypesOfIterableSlow(t *Type, r *IterationTypesResolver, errorNode *ast.Node, diagnosticOutput *[]*ast.Diagnostic) IterationTypes {
	if method := c.getPropertyOfType(t, c.getPropertyNameForKnownSymbolName(r.iteratorSymbolName)); method != nil && method.Flags&ast.SymbolFlagsOptional == 0 {
		methodType := c.getTypeOfSymbol(method)
		if IsTypeAny(methodType) {
			return IterationTypes{c.anyType, c.anyType, c.anyType}
		}
		allSignatures := c.getSignaturesOfType(methodType, SignatureKindCall)
		validSignatures := core.Filter(allSignatures, func(sig *Signature) bool {
			return c.getMinArgumentCount(sig) == 0
		})
		if len(validSignatures) != 0 {
			iteratorType := c.getIntersectionType(core.Map(validSignatures, c.getReturnTypeOfSignature))
			return c.getIterationTypesOfIteratorWorker(iteratorType, r, errorNode, diagnosticOutput)
		}
		if errorNode != nil && len(allSignatures) != 0 {
			c.checkTypeAssignableToEx(t, r.getGlobalIterableTypeChecked(), errorNode, nil, diagnosticOutput)
		}
	}
	return IterationTypes{}
}

// Gets the *yield*, *return*, and *next* types from an `Iterator`-like or `AsyncIterator`-like type.
//
// If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` with non-nil
// members is returned. Otherwise, a default `IterationTypes{}` is returned.
func (c *Checker) getIterationTypesOfIterator(t *Type, r *IterationTypesResolver, errorNode *ast.Node, diagnosticOutput *[]*ast.Diagnostic) IterationTypes {
	return c.getIterationTypesOfIteratorWorker(t, r, errorNode, diagnosticOutput)
}

// Gets the *yield*, *return*, and *next* types from an `Iterator`-like or `AsyncIterator`-like type.
//
// If we successfully found the *yield*, *return*, and *next* types, an `IterationTypes` with non-nil
// members is returned. Otherwise, a default `IterationTypes{}` is returned.
//
// NOTE: You probably don't want to call this directly and should be calling `getIterationTypesOfIterator` instead.
func (c *Checker) getIterationTypesOfIteratorWorker(t *Type, r *IterationTypesResolver, errorNode *ast.Node, diagnosticOutput *[]*ast.Diagnostic) IterationTypes {
	if IsTypeAny(t) {
		return IterationTypes{c.anyType, c.anyType, c.anyType}
	}
	iterationTypes := c.getIterationTypesOfIteratorFast(t, r)
	if iterationTypes.hasTypes() {
		return iterationTypes
	}
	return c.getIterationTypesOfIteratorSlow(t, r, errorNode, diagnosticOutput)
}

func (c *Checker) getIterationTypesOfIteratorFast(t *Type, r *IterationTypesResolver) IterationTypes {
	// As an optimization, if the type is an instantiation of the following global type, then
	// just grab its related type arguments:
	// - `Iterable<T, TReturn, TNext>` or `AsyncIterable<T, TReturn, TNext>`
	// - `IteratorObject<T, TReturn, TNext>` or `AsyncIteratorObject<T, TReturn, TNext>`
	// - `IterableIterator<T, TReturn, TNext>` or `AsyncIterableIterator<T, TReturn, TNext>`
	// - `Generator<T, TReturn, TNext>` or `AsyncGenerator<T, TReturn, TNext>`
	if c.isReferenceToType(t, r.getGlobalIteratorType()) ||
		c.isReferenceToType(t, r.getGlobalIteratorObjectType()) ||
		c.isReferenceToType(t, r.getGlobalIterableIteratorType()) ||
		c.isReferenceToType(t, r.getGlobalGeneratorType()) {
		typeArguments := c.getTypeArguments(t)
		return r.getResolvedIterationTypes(typeArguments[0], typeArguments[1], typeArguments[2])
	}
	// As an optimization, if the type is an instantiation of one of the following global types, then
	// just grab the related type argument:
	// - `ArrayIterator<T>`
	// - `MapIterator<T>`
	// - `SetIterator<T>`
	// - `StringIterator<T>`
	// - `ReadableStreamAsyncIterator<T>`
	if c.isReferenceToSomeType(t, r.getGlobalBuiltinIteratorTypes()) {
		return r.getResolvedIterationTypes(c.getTypeArguments(t)[0], c.getBuiltinIteratorReturnType(), c.unknownType)
	}
	return IterationTypes{}
}

func (c *Checker) getIterationTypesOfIteratorSlow(t *Type, r *IterationTypesResolver, errorNode *ast.Node, diagnosticOutput *[]*ast.Diagnostic) IterationTypes {
	return c.combineIterationTypes([]IterationTypes{
		c.getIterationTypesOfMethod(t, r, "next", errorNode, diagnosticOutput),
		c.getIterationTypesOfMethod(t, r, "return", errorNode, diagnosticOutput),
		c.getIterationTypesOfMethod(t, r, "throw", errorNode, diagnosticOutput),
	})
}

func (c *Checker) getIterationTypesOfMethod(t *Type, resolver *IterationTypesResolver, methodName string, errorNode *ast.Node, diagnosticOutput *[]*ast.Diagnostic) IterationTypes {
	method := c.getPropertyOfType(t, methodName)
	// Ignore 'return' or 'throw' if they are missing.
	if method == nil && methodName != "next" {
		return IterationTypes{}
	}
	var methodType *Type
	if method != nil && !(methodName == "next" && method.Flags&ast.SymbolFlagsOptional != 0) {
		if methodName == "next" {
			methodType = c.getTypeOfSymbol(method)
		} else {
			methodType = c.getTypeWithFacts(c.getTypeOfSymbol(method), TypeFactsNEUndefinedOrNull)
		}
	}
	if IsTypeAny(methodType) {
		return IterationTypes{c.anyType, c.anyType, c.anyType}
	}
	// Both async and non-async iterators *must* have a `next` method.
	var methodSignatures []*Signature
	if methodType != nil {
		methodSignatures = c.getSignaturesOfType(methodType, SignatureKindCall)
	}
	if len(methodSignatures) == 0 {
		if errorNode != nil {
			diagnostic := core.IfElse(methodName == "next", resolver.mustHaveANextMethodDiagnostic, resolver.mustBeAMethodDiagnostic)
			c.reportDiagnostic(NewDiagnosticForNode(errorNode, diagnostic, methodName), diagnosticOutput)
		}
		return IterationTypes{}
	}
	// If the method signature comes exclusively from the global iterator or generator type,
	// create iteration types from its type arguments like `getIterationTypesOfIteratorFast`
	// does (so as to remove `undefined` from the next and return types). We arrive here when
	// a contextual type for a generator was not a direct reference to one of those global types,
	// but looking up `methodType` referred to one of them (and nothing else). E.g., in
	// `interface SpecialIterator extends Iterator<number> {}`, `SpecialIterator` is not a
	// reference to `Iterator`, but its `next` member derives exclusively from `Iterator`.
	if len(methodSignatures) == 1 && methodType.symbol != nil {
		globalGeneratorType := resolver.getGlobalGeneratorType()
		globalIteratorType := resolver.getGlobalIteratorType()
		isGeneratorMethod := globalGeneratorType.symbol != nil && globalGeneratorType.symbol.Members[methodName] == methodType.symbol
		isIteratorMethod := !isGeneratorMethod && globalIteratorType.symbol != nil && globalIteratorType.symbol.Members[methodName] == methodType.symbol
		if isGeneratorMethod || isIteratorMethod {
			typeParameters := core.IfElse(isGeneratorMethod, globalGeneratorType, globalIteratorType).AsInterfaceType().TypeParameters()
			mapper := methodType.Mapper()
			var nextType *Type
			if methodName == "next" {
				nextType = mapper.Map(typeParameters[2])
			}
			return IterationTypes{mapper.Map(typeParameters[0]), mapper.Map(typeParameters[1]), nextType}
		}
	}
	// Extract the first parameter and return type of each signature.
	var methodParameterTypes []*Type
	var methodReturnTypes []*Type
	for _, signature := range methodSignatures {
		if methodName != "throw" && len(signature.parameters) != 0 {
			methodParameterTypes = append(methodParameterTypes, c.getTypeAtPosition(signature, 0))
		}
		methodReturnTypes = append(methodReturnTypes, c.getReturnTypeOfSignature(signature))
	}
	// Resolve the *next* or *return* type from the first parameter of a `next()` or
	// `return()` method, respectively.
	var returnTypes []*Type
	var nextType *Type
	if methodName != "throw" {
		var methodParameterType *Type
		if methodParameterTypes != nil {
			methodParameterType = c.getUnionType(methodParameterTypes)
		} else {
			methodParameterType = c.unknownType
		}
		if methodName == "next" {
			// The value of `next(value)` is *not* awaited by async generators
			nextType = methodParameterType
		} else if methodName == "return" {
			// The value of `return(value)` *is* awaited by async generators
			resolvedMethodParameterType := core.OrElse(resolver.resolveIterationType(methodParameterType, errorNode), c.anyType)
			returnTypes = append(returnTypes, resolvedMethodParameterType)
		}
	}
	// Resolve the *yield* and *return* types from the return type of the method (i.e. `IteratorResult`)
	var yieldType *Type
	var methodReturnType *Type
	if methodReturnTypes != nil {
		methodReturnType = c.getIntersectionType(methodReturnTypes)
	} else {
		methodReturnType = c.neverType
	}
	resolvedMethodReturnType := core.OrElse(resolver.resolveIterationType(methodReturnType, errorNode), c.anyType)
	iterationTypes := c.getIterationTypesOfIteratorResult(resolvedMethodReturnType)
	if !iterationTypes.hasTypes() {
		if errorNode != nil {
			c.reportDiagnostic(NewDiagnosticForNode(errorNode, resolver.mustHaveAValueDiagnostic, methodName), diagnosticOutput)
		}
		yieldType = c.anyType
		returnTypes = append(returnTypes, c.anyType)
	} else {
		yieldType = iterationTypes.yieldType
		returnTypes = append(returnTypes, iterationTypes.returnType)
	}
	return IterationTypes{yieldType, c.getUnionType(returnTypes), nextType}
}

// Gets the *yield* and *return* types of an `IteratorResult`-like type.
//
// If we are unable to determine a *yield* or a *return* type, `noIterationTypes` is
// returned to indicate to the caller that it should handle the error. Otherwise, an
// `IterationTypes` record is returned.
func (c *Checker) getIterationTypesOfIteratorResult(t *Type) IterationTypes {
	if IsTypeAny(t) {
		return IterationTypes{c.anyType, c.anyType, c.anyType}
	}
	// As an optimization, if the type is an instantiation of one of the global `IteratorYieldResult<T>`
	// or `IteratorReturnResult<TReturn>` types, then just grab its type argument.
	if c.isReferenceToType(t, c.getGlobalIteratorYieldResultType()) {
		return IterationTypes{c.getTypeArguments(t)[0], nil, nil}
	}
	if c.isReferenceToType(t, c.getGlobalIteratorReturnResultType()) {
		return IterationTypes{nil, c.getTypeArguments(t)[0], nil}
	}
	// Choose any constituents that can produce the requested iteration type.
	yieldIteratorResult := c.filterType(t, c.isYieldIteratorResult)
	var yieldType *Type
	if yieldIteratorResult != c.neverType {
		yieldType = c.getTypeOfPropertyOfType(yieldIteratorResult, "value" /* as __String */)
	}
	returnIteratorResult := c.filterType(t, c.isReturnIteratorResult)
	var returnType *Type
	if returnIteratorResult != c.neverType {
		returnType = c.getTypeOfPropertyOfType(returnIteratorResult, "value" /* as __String */)
	}
	if yieldType == nil && returnType == nil {
		return IterationTypes{}
	}
	// From https://tc39.github.io/ecma262/#sec-iteratorresult-interface
	// > ... If the iterator does not have a return value, `value` is `undefined`. In that case, the
	// > `value` property may be absent from the conforming object if it does not inherit an explicit
	// > `value` property.
	return IterationTypes{yieldType, core.OrElse(returnType, c.voidType), nil}
}

func (c *Checker) isYieldIteratorResult(t *Type) bool {
	return c.isIteratorResult(t, IterationTypeKindYield)
}

func (c *Checker) isReturnIteratorResult(t *Type) bool {
	return c.isIteratorResult(t, IterationTypeKindReturn)
}

func (c *Checker) isIteratorResult(t *Type, kind IterationTypeKind) bool {
	// From https://tc39.github.io/ecma262/#sec-iteratorresult-interface:
	// > [done] is the result status of an iterator `next` method call. If the end of the iterator was reached `done` is `true`.
	// > If the end was not reached `done` is `false` and a value is available.
	// > If a `done` property (either own or inherited) does not exist, it is consider to have the value `false`.
	doneType := core.OrElse(c.getTypeOfPropertyOfType(t, "done"), c.falseType)
	return c.isTypeAssignableTo(core.IfElse(kind == IterationTypeKindYield, c.falseType, c.trueType), doneType)
}

func (c *Checker) reportTypeNotIterableError(errorNode *ast.Node, t *Type, allowAsyncIterables bool) *ast.Diagnostic {
	var message *diagnostics.Message
	if allowAsyncIterables {
		message = diagnostics.Type_0_must_have_a_Symbol_asyncIterator_method_that_returns_an_async_iterator
	} else {
		message = diagnostics.Type_0_must_have_a_Symbol_iterator_method_that_returns_an_iterator
	}
	suggestAwait := c.getAwaitedTypeOfPromise(t) != nil || (!allowAsyncIterables &&
		ast.IsForOfStatement(errorNode.Parent) &&
		errorNode.Parent.Expression() == errorNode &&
		c.getGlobalAsyncIterableType() != c.emptyGenericType &&
		c.isTypeAssignableTo(t, c.createTypeFromGenericGlobalType(c.getGlobalAsyncIterableType(), []*Type{c.anyType, c.anyType, c.anyType})))
	return c.errorAndMaybeSuggestAwait(errorNode, suggestAwait, message, c.TypeToString(t))
}

func (c *Checker) getIterationDiagnosticDetails(use IterationUse, inputType *Type, allowsStrings bool) (*diagnostics.Message, bool) {
	yieldType := c.getIterationTypeOfIterable(use, IterationTypeKindYield, inputType, nil /*errorNode*/)
	if yieldType != nil {
		return diagnostics.Type_0_can_only_be_iterated_through_when_using_the_downlevelIteration_flag_or_with_a_target_of_es2015_or_higher, false
	}
	if inputType.symbol != nil && isES2015OrLaterIterable(inputType.symbol.Name) {
		return diagnostics.Type_0_can_only_be_iterated_through_when_using_the_downlevelIteration_flag_or_with_a_target_of_es2015_or_higher, true
	}
	if allowsStrings {
		return diagnostics.Type_0_is_not_an_array_type_or_a_string_type, true
	}
	return diagnostics.Type_0_is_not_an_array_type, true
}

func isES2015OrLaterIterable(n string) bool {
	switch n {
	case "Float32Array", "Float64Array", "Int16Array", "Int32Array", "Int8Array", "NodeList", "Uint16Array", "Uint32Array", "Uint8Array", "Uint8ClampedArray":
		return true
	}
	return false
}

func (c *Checker) checkAliasSymbol(node *ast.Node) {
	symbol := c.getSymbolOfDeclaration(node)
	target := c.resolveAlias(symbol)
	if target == c.unknownSymbol {
		return
	}
	// For external modules, `symbol` represents the local symbol for an alias.
	// This local symbol will merge any other local declarations (excluding other aliases)
	// and symbol.flags will contains combined representation for all merged declaration.
	// Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have,
	// otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export*
	// in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names).
	symbol = c.getMergedSymbol(core.OrElse(symbol.ExportSymbol, symbol))
	targetFlags := c.getSymbolFlags(target)
	// A type-only import/export will already have a grammar error in a JS file, so no need to issue more errors within
	if ast.IsInJSFile(node) && targetFlags&ast.SymbolFlagsValue == 0 && !ast.IsTypeOnlyImportOrExportDeclaration(node) {
		errorNode := core.OrElse(node.PropertyNameOrName(), node)
		debug.Assert(node.Kind != ast.KindNamespaceExport)
		if ast.IsExportSpecifier(node) {
			diag := c.error(errorNode, diagnostics.Types_cannot_appear_in_export_declarations_in_JavaScript_files)
			if sourceSymbol := ast.GetSourceFileOfNode(node).AsNode().Symbol(); sourceSymbol != nil {
				if alreadyExportedSymbol := sourceSymbol.Exports[node.PropertyNameOrName().Text()]; alreadyExportedSymbol == target {
					if exportingDeclaration := core.Find(alreadyExportedSymbol.Declarations, ast.IsJSTypeAliasDeclaration); exportingDeclaration != nil {
						diag.AddRelatedInfo(NewDiagnosticForNode(exportingDeclaration, diagnostics.X_0_is_automatically_exported_here, alreadyExportedSymbol.Name))
					}
				}
			}
		} else {
			identifierText := symbol.Name
			if ast.IsIdentifier(errorNode) {
				identifierText = errorNode.Text()
			}
			specifierText := "..."
			if importDeclaration := ast.FindAncestor(node, func(n *ast.Node) bool {
				return ast.IsImportOrImportEqualsDeclaration(n) || ast.IsVariableDeclaration(n)
			}); importDeclaration != nil {
				if moduleSpecifier := TryGetModuleSpecifierFromDeclaration(importDeclaration); moduleSpecifier != nil {
					specifierText = moduleSpecifier.Text()
				}
			}
			importText := "import(\"" + specifierText + "\")"
			if ast.IsImportSpecifier(node) {
				importText = importText + "." + identifierText
			}
			c.error(errorNode, diagnostics.X_0_is_a_type_and_cannot_be_imported_in_JavaScript_files_Use_1_in_a_JSDoc_type_annotation, identifierText, importText)
		}
		return
	}
	excludedMeanings := core.IfElse(symbol.Flags&(ast.SymbolFlagsValue|ast.SymbolFlagsExportValue) != 0, ast.SymbolFlagsValue, 0) |
		core.IfElse(symbol.Flags&ast.SymbolFlagsType != 0, ast.SymbolFlagsType, 0) |
		core.IfElse(symbol.Flags&ast.SymbolFlagsNamespace != 0, ast.SymbolFlagsNamespace, 0)
	if targetFlags&excludedMeanings != 0 {
		message := core.IfElse(ast.IsExportSpecifier(node),
			diagnostics.Export_declaration_conflicts_with_exported_declaration_of_0,
			diagnostics.Import_declaration_conflicts_with_local_declaration_of_0)
		c.error(node, message, c.symbolToString(symbol))
	} else if !ast.IsExportSpecifier(node) {
		// Look at 'compilerOptions.isolatedModules' and not 'getIsolatedModules(...)' (which considers 'verbatimModuleSyntax')
		// here because 'verbatimModuleSyntax' will already have an error for importing a type without 'import type'.
		appearsValueyToTranspiler := c.compilerOptions.IsolatedModules.IsTrue() && ast.FindAncestor(node, ast.IsTypeOnlyImportOrExportDeclaration) == nil
		if appearsValueyToTranspiler && symbol.Flags&(ast.SymbolFlagsValue|ast.SymbolFlagsExportValue) != 0 {
			c.error(node, diagnostics.Import_0_conflicts_with_local_value_so_must_be_declared_with_a_type_only_import_when_isolatedModules_is_enabled, c.symbolToString(symbol), c.getIsolatedModulesLikeFlagName())
		}
	}
	if c.compilerOptions.GetIsolatedModules() && !ast.IsTypeOnlyImportOrExportDeclaration(node) && node.Flags&ast.NodeFlagsAmbient == 0 {
		typeOnlyAlias := c.getTypeOnlyAliasDeclaration(symbol)
		isType := targetFlags&ast.SymbolFlagsValue == 0
		if isType || typeOnlyAlias != nil {
			switch node.Kind {
			case ast.KindImportClause, ast.KindImportSpecifier, ast.KindImportEqualsDeclaration:
				if c.compilerOptions.VerbatimModuleSyntax.IsTrue() {
					debug.Assert(node.Name() != nil, "An ImportClause with a symbol should have a name")
					var message *diagnostics.Message
					switch {
					case c.compilerOptions.VerbatimModuleSyntax.IsTrue() && ast.IsInternalModuleImportEqualsDeclaration(node):
						message = diagnostics.An_import_alias_cannot_resolve_to_a_type_or_type_only_declaration_when_verbatimModuleSyntax_is_enabled
					case isType:
						message = diagnostics.X_0_is_a_type_and_must_be_imported_using_a_type_only_import_when_verbatimModuleSyntax_is_enabled
					default:
						message = diagnostics.X_0_resolves_to_a_type_only_declaration_and_must_be_imported_using_a_type_only_import_when_verbatimModuleSyntax_is_enabled
					}
					name := node.PropertyNameOrName().Text()
					c.addTypeOnlyDeclarationRelatedInfo(c.error(node, message, name), core.IfElse(isType, nil, typeOnlyAlias), name)
				}
				if isType && node.Kind == ast.KindImportEqualsDeclaration && ast.HasModifier(node, ast.ModifierFlagsExport) {
					c.error(node, diagnostics.Cannot_use_export_import_on_a_type_or_type_only_namespace_when_0_is_enabled, c.getIsolatedModulesLikeFlagName())
				}
			case ast.KindExportSpecifier:
				// Don't allow re-exporting an export that will be elided when `--isolatedModules` is set.
				// The exception is that `import type { A } from './a'; export { A }` is allowed
				// because single-file analysis can determine that the export should be dropped.
				if c.compilerOptions.VerbatimModuleSyntax.IsTrue() || ast.GetSourceFileOfNode(typeOnlyAlias) != ast.GetSourceFileOfNode(node) {
					name := node.PropertyNameOrName().Text()
					var diagnostic *ast.Diagnostic
					if isType {
						diagnostic = c.error(node, diagnostics.Re_exporting_a_type_when_0_is_enabled_requires_using_export_type, c.getIsolatedModulesLikeFlagName())
					} else {
						diagnostic = c.error(node, diagnostics.X_0_resolves_to_a_type_only_declaration_and_must_be_re_exported_using_a_type_only_re_export_when_1_is_enabled, name, c.getIsolatedModulesLikeFlagName())
					}
					c.addTypeOnlyDeclarationRelatedInfo(diagnostic, core.IfElse(isType, nil, typeOnlyAlias), name)
				}
			}
		}
		if c.compilerOptions.VerbatimModuleSyntax.IsTrue() && !ast.IsImportEqualsDeclaration(node) && !ast.IsInJSFile(node) && c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) == core.ModuleKindCommonJS {
			c.error(node, getVerbatimModuleSyntaxErrorMessage(node))
		} else if c.moduleKind == core.ModuleKindPreserve && !ast.IsImportEqualsDeclaration(node) && !ast.IsVariableDeclaration(node) && c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) == core.ModuleKindCommonJS {
			// In `--module preserve`, ESM input syntax emits ESM output syntax, but there will be times
			// when we look at the `impliedNodeFormat` of this file and decide it's CommonJS (i.e., currently,
			// only if the file extension is .cjs/.cts). To avoid that inconsistency, we disallow ESM syntax
			// in files that are unambiguously CommonJS in this mode.
			c.error(node, diagnostics.ECMAScript_module_syntax_is_not_allowed_in_a_CommonJS_module_when_module_is_set_to_preserve)
		}
		if c.compilerOptions.VerbatimModuleSyntax.IsTrue() && !ast.IsTypeOnlyImportOrExportDeclaration(node) && node.Flags&ast.NodeFlagsAmbient == 0 && targetFlags&ast.SymbolFlagsConstEnum != 0 {
			constEnumDeclaration := target.ValueDeclaration
			redirect := c.program.GetProjectReferenceFromOutputDts(ast.GetSourceFileOfNode(constEnumDeclaration).Path())
			if constEnumDeclaration.Flags&ast.NodeFlagsAmbient != 0 && (redirect == nil || !redirect.Resolved.CompilerOptions().ShouldPreserveConstEnums()) {
				c.error(node, diagnostics.Cannot_access_ambient_const_enums_when_0_is_enabled, c.getIsolatedModulesLikeFlagName())
			}
		}
	}
	if ast.IsImportSpecifier(node) {
		targetSymbol := c.resolveAliasWithDeprecationCheck(symbol, node)
		if c.isDeprecatedSymbol(targetSymbol) && targetSymbol.Declarations != nil {
			c.addDeprecatedSuggestion(node, targetSymbol.Declarations, targetSymbol.Name)
		}
	}
}

func (c *Checker) areDeclarationFlagsIdentical(left *ast.Declaration, right *ast.Declaration) bool {
	if ast.IsParameterDeclaration(left) && ast.IsVariableDeclaration(right) || ast.IsVariableDeclaration(left) && ast.IsParameterDeclaration(right) {
		// Differences in optionality between parameters and variables are allowed.
		return true
	}
	if isOptionalDeclaration(left) != isOptionalDeclaration(right) {
		return false
	}
	interestingFlags := ast.ModifierFlagsPrivate | ast.ModifierFlagsProtected | ast.ModifierFlagsAsync | ast.ModifierFlagsAbstract | ast.ModifierFlagsReadonly | ast.ModifierFlagsStatic
	return getSelectedModifierFlags(left, interestingFlags) == getSelectedModifierFlags(right, interestingFlags)
}

func (c *Checker) checkTypeAliasDeclaration(node *ast.Node) {
	// Grammar checking
	c.checkGrammarModifiers(node)
	c.checkTypeNameIsReserved(node.Name(), diagnostics.Type_alias_name_cannot_be_0)
	if !c.containerAllowsBlockScopedVariable(node.Parent) {
		c.grammarErrorOnNode(node, diagnostics.X_0_declarations_can_only_be_declared_inside_a_block, "type")
	}
	c.checkExportsOnMergedDeclarations(node)

	typeNode := node.Type()
	typeParameters := node.TypeParameters()
	c.checkTypeParameters(typeParameters)
	if typeNode != nil && typeNode.Kind == ast.KindIntrinsicKeyword {
		if !(len(typeParameters) == 0 && node.Name().Text() == "BuiltinIteratorReturn" ||
			len(typeParameters) == 1 && intrinsicTypeKinds[node.Name().Text()] != IntrinsicTypeKindUnknown) {
			c.error(typeNode, diagnostics.The_intrinsic_keyword_can_only_be_used_to_declare_compiler_provided_intrinsic_types)
		}
		return
	}
	c.checkSourceElement(typeNode)
	c.registerForUnusedIdentifiersCheck(node)
}

func (c *Checker) checkTypeNameIsReserved(name *ast.Node, message *diagnostics.Message) {
	// TS 1.0 spec (April 2014): 3.6.1
	// The predefined type keywords are reserved and cannot be used as names of user defined types.
	switch name.Text() {
	case "any", "unknown", "never", "number", "bigint", "boolean", "string", "symbol", "void", "object", "undefined":
		c.error(name, message, name.Text())
	}
}

func (c *Checker) checkExportsOnMergedDeclarations(node *ast.Node) {
	// If localSymbol is defined on node then node itself is exported - check is required.
	symbol := node.LocalSymbol()
	if symbol == nil {
		// Local symbol is undefined => this declaration is non-exported.
		// However, symbol might contain other declarations that are exported.
		symbol = c.getSymbolOfDeclaration(node)
		if symbol.ExportSymbol == nil {
			// This is a pure local symbol (all declarations are non-exported) - no need to check anything.
			return
		}
	}
	// Run the check only for the first declaration in the list.
	if ast.GetDeclarationOfKind(symbol, node.Kind) != node {
		return
	}
	exportedDeclarationSpaces := DeclarationSpacesNone
	nonExportedDeclarationSpaces := DeclarationSpacesNone
	defaultExportedDeclarationSpaces := DeclarationSpacesNone
	for _, d := range symbol.Declarations {
		declarationSpaces := c.getDeclarationSpaces(d)
		effectiveDeclarationFlags := c.getEffectiveDeclarationFlags(d, ast.ModifierFlagsExport|ast.ModifierFlagsDefault)
		if effectiveDeclarationFlags&ast.ModifierFlagsExport != 0 {
			if effectiveDeclarationFlags&ast.ModifierFlagsDefault != 0 {
				defaultExportedDeclarationSpaces |= declarationSpaces
			} else {
				exportedDeclarationSpaces |= declarationSpaces
			}
		} else {
			nonExportedDeclarationSpaces |= declarationSpaces
		}
	}
	// Spaces for anything not declared a 'default export'.
	nonDefaultExportedDeclarationSpaces := exportedDeclarationSpaces | nonExportedDeclarationSpaces
	commonDeclarationSpacesForExportsAndLocals := exportedDeclarationSpaces & nonExportedDeclarationSpaces
	commonDeclarationSpacesForDefaultAndNonDefault := defaultExportedDeclarationSpaces & nonDefaultExportedDeclarationSpaces
	if commonDeclarationSpacesForExportsAndLocals != 0 || commonDeclarationSpacesForDefaultAndNonDefault != 0 {
		// declaration spaces for exported and non-exported declarations intersect
		for _, d := range symbol.Declarations {
			declarationSpaces := c.getDeclarationSpaces(d)
			name := ast.GetNameOfDeclaration(d)
			// Only error on the declarations that contributed to the intersecting spaces.
			if declarationSpaces&commonDeclarationSpacesForDefaultAndNonDefault != 0 {
				c.error(name, diagnostics.Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead, scanner.DeclarationNameToString(name))
			} else if declarationSpaces&commonDeclarationSpacesForExportsAndLocals != 0 {
				c.error(name, diagnostics.Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local, scanner.DeclarationNameToString(name))
			}
		}
	}
}

func (c *Checker) getDeclarationSpaces(node *ast.Declaration) DeclarationSpaces {
	switch node.Kind {
	case ast.KindInterfaceDeclaration, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindJSDocTypedefTag, ast.KindJSDocCallbackTag:
		return DeclarationSpacesExportType
	case ast.KindModuleDeclaration:
		if ast.IsAmbientModule(node) || ast.GetModuleInstanceState(node) != ast.ModuleInstanceStateNonInstantiated {
			return DeclarationSpacesExportNamespace | DeclarationSpacesExportValue
		}
		return DeclarationSpacesExportNamespace
	case ast.KindClassDeclaration, ast.KindEnumDeclaration, ast.KindEnumMember:
		return DeclarationSpacesExportType | DeclarationSpacesExportValue
	case ast.KindSourceFile:
		return DeclarationSpacesExportType | DeclarationSpacesExportValue | DeclarationSpacesExportNamespace
	case ast.KindExportAssignment, ast.KindBinaryExpression:
		var expression *ast.Node
		if ast.IsExportAssignment(node) {
			expression = node.Expression()
		} else {
			expression = node.AsBinaryExpression().Right
		}
		// Export assigned entity name expressions act as aliases and should fall through, otherwise they export values.
		if !ast.IsEntityNameExpression(expression) || c.getSymbolOfDeclaration(node).Flags&ast.SymbolFlagsAlias == 0 {
			return DeclarationSpacesExportValue
		}
		// The below options all declare an Alias, which is allowed to merge with other values within the importing module.
		fallthrough
	case ast.KindImportEqualsDeclaration, ast.KindNamespaceImport, ast.KindImportClause:
		result := DeclarationSpacesNone
		target := c.resolveAlias(c.getSymbolOfDeclaration(node))
		for _, d := range target.Declarations {
			result |= c.getDeclarationSpaces(d)
		}
		return result
	case ast.KindVariableDeclaration, ast.KindBindingElement, ast.KindFunctionDeclaration, ast.KindImportSpecifier:
		return DeclarationSpacesExportValue
	case ast.KindMethodSignature, ast.KindPropertySignature:
		return DeclarationSpacesExportType
	}
	panic("Unhandled case in getDeclarationSpaces: " + node.Kind.String())
}

func (c *Checker) checkTypeParameters(typeParameterDeclarations []*ast.Node) {
	seenDefault := false
	for i, node := range typeParameterDeclarations {
		c.checkTypeParameter(node)
		defaultTypeNode := node.AsTypeParameterDeclaration().DefaultType
		if defaultTypeNode != nil {
			seenDefault = true
			c.checkTypeParametersNotReferenced(defaultTypeNode, typeParameterDeclarations, i)
		} else if seenDefault {
			c.error(node, diagnostics.Required_type_parameters_may_not_follow_optional_type_parameters)
		}
		for j := range i {
			if typeParameterDeclarations[j].Symbol() == node.Symbol() {
				c.error(node.Name(), diagnostics.Duplicate_identifier_0, scanner.DeclarationNameToString(node.Name()))
			}
		}
	}
}

// Check that type parameter defaults only reference previously declared type parameters */
func (c *Checker) checkTypeParametersNotReferenced(root *ast.Node, typeParameters []*ast.Node, index int) {
	var visit func(*ast.Node) bool
	visit = func(node *ast.Node) bool {
		if ast.IsTypeReferenceNode(node) {
			t := c.getTypeFromTypeReference(node)
			if t.flags&TypeFlagsTypeParameter != 0 {
				for i := index; i < len(typeParameters); i++ {
					if t.symbol == c.getSymbolOfDeclaration(typeParameters[i]) {
						c.error(node, diagnostics.Type_parameter_defaults_can_only_reference_previously_declared_type_parameters)
					}
				}
			}
		}
		return node.ForEachChild(visit)
	}
	visit(root)
}

func (c *Checker) registerForUnusedIdentifiersCheck(node *ast.Node) {
	sourceFile := ast.GetSourceFileOfNode(node)
	links := c.sourceFileLinks.Get(sourceFile)
	links.identifierCheckNodes = append(links.identifierCheckNodes, node)
}

func (c *Checker) checkUnusedIdentifiers(potentiallyUnusedIdentifiers []*ast.Node) {
	for _, node := range potentiallyUnusedIdentifiers {
		switch node.Kind {
		case ast.KindClassDeclaration, ast.KindClassExpression:
			c.checkUnusedClassMembers(node)
			c.checkUnusedTypeParameters(node)
		case ast.KindSourceFile, ast.KindModuleDeclaration, ast.KindBlock, ast.KindCaseBlock, ast.KindForStatement, ast.KindForInStatement,
			ast.KindForOfStatement:
			c.checkUnusedLocalsAndParameters(node)
		case ast.KindConstructor, ast.KindFunctionExpression, ast.KindFunctionDeclaration, ast.KindArrowFunction, ast.KindMethodDeclaration,
			ast.KindGetAccessor, ast.KindSetAccessor:
			// Only report unused parameters on the implementation, not overloads.
			if node.Body() != nil {
				c.checkUnusedLocalsAndParameters(node)
			}
			c.checkUnusedTypeParameters(node)
		case ast.KindMethodSignature, ast.KindCallSignature, ast.KindConstructSignature, ast.KindFunctionType, ast.KindConstructorType,
			ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindInterfaceDeclaration:
			c.checkUnusedTypeParameters(node)
		case ast.KindInferType:
			c.checkUnusedInferTypeParameter(node)
		default:
			panic("Unhandled case in checkUnusedIdentifiers")
		}
	}
}

func (c *Checker) isReferenced(symbol *ast.Symbol) bool {
	return c.symbolReferenceLinks.Get(symbol).referenceKinds != 0
}

type UnusedKind int32

const (
	UnusedKindLocal UnusedKind = iota
	UnusedKindParameter
)

func (c *Checker) reportUnusedVariable(location *ast.Node, diagnostic *ast.Diagnostic) {
	for ast.IsBindingElement(location) || ast.IsBindingPattern(location) {
		location = location.Parent
	}
	c.reportUnused(location, core.IfElse(ast.IsParameterDeclaration(location), UnusedKindParameter, UnusedKindLocal), diagnostic)
}

func (c *Checker) reportUnused(location *ast.Node, kind UnusedKind, diagnostic *ast.Diagnostic) {
	if location.Flags&(ast.NodeFlagsAmbient|ast.NodeFlagsThisNodeOrAnySubNodesHasError) == 0 {
		isError := c.unusedIsError(kind)
		if isError {
			c.addDiagnostic(diagnostic)
		} else {
			suggestion := *diagnostic
			suggestion.SetCategory(diagnostics.CategorySuggestion)
			c.addSuggestionDiagnostic(&suggestion)
		}
	}
}

func (c *Checker) unusedIsError(kind UnusedKind) bool {
	switch kind {
	case UnusedKindLocal:
		return c.compilerOptions.NoUnusedLocals.IsTrue()
	case UnusedKindParameter:
		return c.compilerOptions.NoUnusedParameters.IsTrue()
	default:
		panic("Unhandled case in unusedIsError")
	}
}

func (c *Checker) checkUnusedClassMembers(node *ast.Node) {
	for _, member := range node.Members() {
		switch member.Kind {
		case ast.KindMethodDeclaration, ast.KindPropertyDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
			if ast.IsSetAccessorDeclaration(member) && member.Symbol().Flags&ast.SymbolFlagsGetAccessor != 0 {
				break // Already would have reported an error on the getter.
			}
			symbol := c.getSymbolOfDeclaration(member)
			if !c.isReferenced(symbol) && (ast.HasModifier(member, ast.ModifierFlagsPrivate) || member.Name() != nil && ast.IsPrivateIdentifier(member.Name())) && member.Flags&ast.NodeFlagsAmbient == 0 {
				c.reportUnused(member, UnusedKindLocal, NewDiagnosticForNode(member.Name(), diagnostics.X_0_is_declared_but_its_value_is_never_read, c.symbolToString(symbol)))
			}
		case ast.KindConstructor:
			for _, parameter := range member.AsConstructorDeclaration().Parameters.Nodes {
				if !c.isReferenced(parameter.Symbol()) && ast.HasSyntacticModifier(parameter, ast.ModifierFlagsPrivate) {
					c.reportUnused(parameter, UnusedKindLocal, NewDiagnosticForNode(parameter.Name(), diagnostics.Property_0_is_declared_but_its_value_is_never_read, ast.SymbolName(parameter.Symbol())))
				}
			}
		case ast.KindIndexSignature, ast.KindSemicolonClassElement, ast.KindClassStaticBlockDeclaration, ast.KindJSTypeAliasDeclaration:
			// Can't be private
		default:
			panic("Unhandled case in checkUnusedClassMembers")
		}
	}
}

func (c *Checker) checkUnusedLocalsAndParameters(node *ast.Node) {
	var variableParents collections.Set[*ast.Node]
	var importClauses map[*ast.Node][]*ast.Node
	for _, local := range node.Locals() {
		referenceKinds := c.symbolReferenceLinks.Get(local).referenceKinds
		if local.Flags&ast.SymbolFlagsTypeParameter != 0 && (local.Flags&ast.SymbolFlagsVariable == 0 || referenceKinds&ast.SymbolFlagsVariable != 0) ||
			local.Flags&ast.SymbolFlagsTypeParameter == 0 && (referenceKinds != 0 || local.ExportSymbol != nil ||
				local.Flags&ast.SymbolFlagsModuleExports != 0) {
			continue
		}
		for _, declaration := range local.Declarations {
			switch {
			case ast.IsVariableDeclaration(declaration) || ast.IsParameterDeclaration(declaration) || ast.IsBindingElement(declaration):
				variableParents.Add(ast.GetRootDeclaration(declaration).Parent)
			case ast.IsImportClause(declaration) || ast.IsImportSpecifier(declaration) || ast.IsNamespaceImport(declaration):
				if !isIdentifierThatStartsWithUnderscore(declaration.Name()) {
					if importClauses == nil {
						importClauses = make(map[*ast.Node][]*ast.Node)
					}
					importClause := importClauseFromImported(declaration)
					importClauses[importClause] = append(importClauses[importClause], declaration)
				}
			default:
				if !ast.IsTypeParameterDeclaration(declaration) && !ast.IsAmbientModule(declaration) {
					c.reportUnusedLocal(declaration, ast.SymbolName(local))
				}
			}
		}
	}
	for declaration := range variableParents.Keys() {
		if ast.IsVariableDeclarationList(declaration) {
			c.reportUnusedVariables(declaration)
		} else {
			c.reportUnusedParameters(declaration)
		}
	}
	for declaration, unuseds := range importClauses {
		c.reportUnusedImports(declaration, unuseds)
	}
}

func (c *Checker) reportUnusedLocal(node *ast.Node, name string) {
	message := core.IfElse(ast.IsTypeDeclaration(node), diagnostics.X_0_is_declared_but_never_used, diagnostics.X_0_is_declared_but_its_value_is_never_read)
	c.reportUnused(node, UnusedKindLocal, NewDiagnosticForNode(core.OrElse(node.Name(), node), message, name))
}

func (c *Checker) reportUnusedVariables(node *ast.Node) {
	declarations := node.AsVariableDeclarationList().Declarations.Nodes
	if len(declarations) > 1 && core.Every(declarations, c.isUnreferencedVariableDeclaration) {
		c.reportUnusedVariable(node, NewDiagnosticForNode(node, diagnostics.All_variables_are_unused))
	} else {
		c.reportUnusedVariableDeclarations(declarations)
	}
}

func (c *Checker) reportUnusedParameters(node *ast.Node) {
	c.reportUnusedVariableDeclarations(node.Parameters())
}

func (c *Checker) reportUnusedBindingElements(node *ast.Node) {
	declarations := node.Elements()
	if len(declarations) > 1 && core.Every(declarations, c.isUnreferencedVariableDeclaration) {
		c.reportUnusedVariable(node, NewDiagnosticForNode(node, diagnostics.All_destructured_elements_are_unused))
	} else {
		c.reportUnusedVariableDeclarations(declarations)
	}
}

func (c *Checker) reportUnusedVariableDeclarations(declarations []*ast.Node) {
	for _, declaration := range declarations {
		name := declaration.Name()
		if name != nil && !ast.IsParameterPropertyDeclaration(declaration, declaration.Parent) && !ast.IsThisParameter(declaration) {
			if ast.IsBindingPattern(name) {
				c.reportUnusedBindingElements(name)
			} else if c.isUnreferencedVariableDeclaration(declaration) {
				c.reportUnusedVariable(declaration, NewDiagnosticForNode(name, diagnostics.X_0_is_declared_but_its_value_is_never_read, name.Text()))
			}
		}
	}
}

func (c *Checker) isUnreferencedVariableDeclaration(node *ast.Node) bool {
	name := node.Name()
	if name == nil {
		return true
	}
	if ast.IsBindingPattern(name) {
		return core.Every(node.Name().Elements(), c.isUnreferencedVariableDeclaration)
	}
	if c.symbolReferenceLinks.Get(c.getSymbolOfDeclaration(node)).referenceKinds&ast.SymbolFlagsVariable != 0 {
		return false
	}
	if ast.IsBindingElement(node) && ast.IsObjectBindingPattern(node.Parent) {
		// In `{ a, ...b }, `a` is considered used since it removes a property from `b`. `b` may still be unused though.
		lastElement := core.LastOrNil(node.Parent.Elements())
		if node != lastElement && hasDotDotDotToken(lastElement) {
			return false
		}
	}
	if (ast.IsParameterDeclaration(node) ||
		ast.IsVariableDeclaration(node) && (ast.IsForInOrOfStatement(node.Parent.Parent) || c.getCombinedNodeFlagsCached(node)&ast.NodeFlagsUsing != 0) ||
		ast.IsBindingElement(node) && !(ast.IsObjectBindingPattern(node.Parent) && node.PropertyName() == nil)) &&
		isIdentifierThatStartsWithUnderscore(name) {
		return false
	}
	return true
}

func (c *Checker) reportUnusedImports(node *ast.Node, unuseds []*ast.Node) {
	declarationCount := core.IfElse(node.Name() != nil, 1, 0)
	namedBindings := node.AsImportClause().NamedBindings
	if namedBindings != nil {
		if ast.IsNamespaceImport(namedBindings) {
			declarationCount++
		} else {
			declarationCount += len(namedBindings.Elements())
		}
	}
	if declarationCount > 1 && declarationCount == len(unuseds) {
		c.reportUnused(node, UnusedKindLocal, NewDiagnosticForNode(node.Parent, diagnostics.All_imports_in_import_declaration_are_unused))
	} else {
		for _, unused := range unuseds {
			c.reportUnusedLocal(unused, unused.Name().Text())
		}
	}
}

func isIdentifierThatStartsWithUnderscore(node *ast.Node) bool {
	return ast.IsIdentifier(node) && node.Text() != "" && node.Text()[0] == '_'
}

func importClauseFromImported(node *ast.Node) *ast.Node {
	switch node.Kind {
	case ast.KindImportClause:
		return node
	case ast.KindNamespaceImport:
		return node.Parent
	default:
		return node.Parent.Parent
	}
}

func (c *Checker) checkUnusedInferTypeParameter(node *ast.Node) {
	typeParameter := node.AsInferTypeNode().TypeParameter
	if c.isUnreferencedTypeParameter(typeParameter) {
		c.reportUnused(node, UnusedKindParameter, NewDiagnosticForNode(typeParameter.Name(), diagnostics.X_0_is_declared_but_never_used, typeParameter.Name().Text()))
	}
}

func (c *Checker) checkUnusedTypeParameters(node *ast.Node) {
	if !allDeclarationsInSameSourceFile(c.getSymbolOfDeclaration(node)) {
		return
	}
	typeParameterList := node.TypeParameterList()
	if typeParameterList == nil {
		return
	}
	if len(typeParameterList.Nodes) > 1 && core.Every(typeParameterList.Nodes, c.isUnreferencedTypeParameter) {
		file := ast.GetSourceFileOfNode(node)
		loc := rangeOfTypeParameters(file, typeParameterList)
		c.reportUnused(node, UnusedKindParameter, ast.NewDiagnostic(file, loc, diagnostics.All_type_parameters_are_unused))
	} else {
		for _, typeParameter := range typeParameterList.Nodes {
			if c.isUnreferencedTypeParameter(typeParameter) {
				c.reportUnused(node, UnusedKindParameter, NewDiagnosticForNode(typeParameter, diagnostics.X_0_is_declared_but_never_used, typeParameter.Name().Text()))
			}
		}
	}
}

func (c *Checker) isUnreferencedTypeParameter(typeParameter *ast.Node) bool {
	return c.symbolReferenceLinks.Get(c.getMergedSymbol(typeParameter.Symbol())).referenceKinds&ast.SymbolFlagsTypeParameter == 0 && !isIdentifierThatStartsWithUnderscore(typeParameter.Name())
}

func (c *Checker) checkUnusedRenamedBindingElements() {
	for _, node := range c.renamedBindingElementsInTypes {
		if c.symbolReferenceLinks.Get(c.getSymbolOfDeclaration(node)).referenceKinds == 0 {
			wrappingDeclaration := ast.WalkUpBindingElementsAndPatterns(node)
			debug.Assert(ast.IsPartOfParameterDeclaration(wrappingDeclaration), "Only parameter declaration should be checked here")
			diagnostic := NewDiagnosticForNode(node.Name(), diagnostics.X_0_is_an_unused_renaming_of_1_Did_you_intend_to_use_it_as_a_type_annotation, scanner.DeclarationNameToString(node.Name()), scanner.DeclarationNameToString(node.PropertyName()))
			if wrappingDeclaration.Type() == nil {
				// entire parameter does not have type annotation, suggest adding an annotation
				diagnostic.AddRelatedInfo(ast.NewDiagnostic(ast.GetSourceFileOfNode(wrappingDeclaration), core.NewTextRange(wrappingDeclaration.End(), wrappingDeclaration.End()), diagnostics.We_can_only_write_a_type_for_0_by_adding_a_type_for_the_entire_parameter_here, scanner.DeclarationNameToString(node.PropertyName())))
			}
			c.addDiagnostic(diagnostic)
		}
	}
}

func (c *Checker) checkExpressionStatement(node *ast.Node) {
	// Grammar checking
	c.checkGrammarStatementInAmbientContext(node)
	c.checkExpression(node.Expression())
}

// Returns the type of an expression. Unlike checkExpression, this function is simply concerned
// with computing the type and may not fully check all contained sub-expressions for errors.
func (c *Checker) getTypeOfExpression(node *ast.Node) *Type {
	// Don't bother caching types that require no flow analysis and are quick to compute.
	quickType := c.getQuickTypeOfExpression(node)
	if quickType != nil {
		return quickType
	}
	// If a type has been cached for the node, return it.
	if cachedType := c.flowTypeCache[node]; cachedType != nil {
		return cachedType
	}
	startInvocationCount := c.flowInvocationCount
	t := c.checkExpressionEx(node, CheckModeTypeOnly)
	// If control flow analysis was required to determine the type, it is worth caching.
	if c.flowInvocationCount != startInvocationCount {
		if c.flowTypeCache == nil {
			c.flowTypeCache = make(map[*ast.Node]*Type)
		}
		c.flowTypeCache[node] = t
	}
	return t
}

// Returns the type of an expression. Unlike checkExpression, this function is simply concerned
// with computing the type and may not fully check all contained sub-expressions for errors.
func (c *Checker) getQuickTypeOfExpression(node *ast.Node) *Type {
	expr := ast.SkipParentheses(node)
	switch {
	case ast.IsAwaitExpression(expr):
		t := c.getQuickTypeOfExpression(expr.Expression())
		if t != nil {
			return c.getAwaitedType(t)
		}
		return nil
	// Optimize for the common case of a call to a function with a single non-generic call
	// signature where we can just fetch the return type without checking the arguments.
	case ast.IsCallExpression(expr) && expr.Expression().Kind != ast.KindSuperKeyword && !ast.IsRequireCall(expr, true /*requireStringLiteralLikeArgument*/) && !c.isSymbolOrSymbolForCall(expr) && !ast.IsImportCall(expr):
		if isCallChain(expr) {
			return c.getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr)
		}
		return c.getReturnTypeOfSingleNonGenericSignature(c.checkNonNullExpression(expr.Expression()), SignatureKindCall)
	case ast.IsNewExpression(expr):
		return c.getReturnTypeOfSingleNonGenericSignature(c.checkNonNullExpression(expr.Expression()), SignatureKindConstruct)
	case ast.IsAssertionExpression(expr) && !ast.IsConstTypeReference(expr.Type()):
		return c.getTypeFromTypeNode(expr.Type())
	case ast.IsLiteralExpression(node) || ast.IsBooleanLiteral(node):
		return c.checkExpression(node)
	}
	return nil
}

func (c *Checker) getReturnTypeOfSingleNonGenericSignature(funcType *Type, kind SignatureKind) *Type {
	signature := c.getSingleSignature(funcType, kind, true /*allowMembers*/)
	if signature != nil && len(signature.typeParameters) == 0 {
		return c.getReturnTypeOfSignature(signature)
	}
	return nil
}

func (c *Checker) getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr *ast.Node) *Type {
	funcType := c.checkExpression(expr.Expression())
	nonOptionalType := c.getOptionalExpressionType(funcType, expr.Expression())
	returnType := c.getReturnTypeOfSingleNonGenericSignature(funcType, SignatureKindCall)
	if returnType != nil {
		return c.propagateOptionalTypeMarker(returnType, expr, nonOptionalType != funcType)
	}
	return nil
}

func (c *Checker) checkNonNullExpression(node *ast.Node) *Type {
	return c.checkNonNullType(c.checkExpression(node), node)
}

func (c *Checker) checkNonNullType(t *Type, node *ast.Node) *Type {
	return c.checkNonNullTypeWithReporter(t, node, (*Checker).reportObjectPossiblyNullOrUndefinedError)
}

func (c *Checker) checkNonNullTypeWithReporter(t *Type, node *ast.Node, reportError func(c *Checker, node *ast.Node, facts TypeFacts)) *Type {
	if c.strictNullChecks && t.flags&TypeFlagsUnknown != 0 {
		if ast.IsEntityNameExpression(node) {
			nodeText := entityNameToString(node)
			if len(nodeText) < 100 {
				c.error(node, diagnostics.X_0_is_of_type_unknown, nodeText)
				return c.errorType
			}
		}
		c.error(node, diagnostics.Object_is_of_type_unknown)
		return c.errorType
	}
	facts := c.getTypeFacts(t, TypeFactsIsUndefinedOrNull)
	if facts&TypeFactsIsUndefinedOrNull != 0 {
		reportError(c, node, facts)
		nonNullable := c.GetNonNullableType(t)
		if nonNullable.flags&(TypeFlagsNullable|TypeFlagsNever) != 0 {
			return c.errorType
		}
		return nonNullable
	}
	return t
}

func (c *Checker) checkNonNullNonVoidType(t *Type, node *ast.Node) *Type {
	nonNullType := c.checkNonNullType(t, node)
	if nonNullType.flags&TypeFlagsVoid != 0 {
		if ast.IsEntityNameExpression(node) {
			nodeText := entityNameToString(node)
			if ast.IsIdentifier(node) && nodeText == "undefined" {
				c.error(node, diagnostics.The_value_0_cannot_be_used_here, nodeText)
				return nonNullType
			}
			if len(nodeText) < 100 {
				c.error(node, diagnostics.X_0_is_possibly_undefined, nodeText)
				return nonNullType
			}
		}
		c.error(node, diagnostics.Object_is_possibly_undefined)
	}
	return nonNullType
}

func (c *Checker) reportObjectPossiblyNullOrUndefinedError(node *ast.Node, facts TypeFacts) {
	var nodeText string
	if ast.IsEntityNameExpression(node) {
		nodeText = entityNameToString(node)
	}
	if node.Kind == ast.KindNullKeyword {
		c.error(node, diagnostics.The_value_0_cannot_be_used_here, "null")
		return
	}
	if nodeText != "" && len(nodeText) < 100 {
		if ast.IsIdentifier(node) && nodeText == "undefined" {
			c.error(node, diagnostics.The_value_0_cannot_be_used_here, "undefined")
			return
		}
		c.error(node, core.IfElse(facts&TypeFactsIsUndefined != 0,
			core.IfElse(facts&TypeFactsIsNull != 0,
				diagnostics.X_0_is_possibly_null_or_undefined,
				diagnostics.X_0_is_possibly_undefined),
			diagnostics.X_0_is_possibly_null), nodeText)
	} else {
		c.error(node, core.IfElse(facts&TypeFactsIsUndefined != 0,
			core.IfElse(facts&TypeFactsIsNull != 0,
				diagnostics.Object_is_possibly_null_or_undefined,
				diagnostics.Object_is_possibly_undefined),
			diagnostics.Object_is_possibly_null))
	}
}

func (c *Checker) checkExpressionWithContextualType(node *ast.Node, contextualType *Type, inferenceContext *InferenceContext, checkMode CheckMode) *Type {
	contextNode := c.getContextNode(node)
	c.pushContextualType(contextNode, contextualType, false /*isCache*/)
	c.pushInferenceContext(contextNode, inferenceContext)
	t := c.checkExpressionEx(node, checkMode|CheckModeContextual|core.IfElse(inferenceContext != nil, CheckModeInferential, 0))
	// In CheckMode.Inferential we collect intra-expression inference sites to process before fixing any type
	// parameters. This information is no longer needed after the call to checkExpression.
	if inferenceContext != nil && inferenceContext.intraExpressionInferenceSites != nil {
		inferenceContext.intraExpressionInferenceSites = nil
	}
	// We strip literal freshness when an appropriate contextual type is present such that contextually typed
	// literals always preserve their literal types (otherwise they might widen during type inference). An alternative
	// here would be to not mark contextually typed literals as fresh in the first place.
	if c.maybeTypeOfKind(t, TypeFlagsLiteral) && c.isLiteralOfContextualType(t, c.instantiateContextualType(contextualType, node, ContextFlagsNone)) {
		t = c.getRegularTypeOfLiteralType(t)
	}
	c.popInferenceContext()
	c.popContextualType()
	return t
}

func (c *Checker) getContextNode(node *ast.Node) *ast.Node {
	if ast.IsJsxAttributes(node) && !ast.IsJsxSelfClosingElement(node.Parent) {
		// Needs to be the root JsxElement, so it encompasses the attributes _and_ the children (which are essentially part of the attributes)
		return node.Parent.Parent
	}
	return node
}

func (c *Checker) checkExpressionCached(node *ast.Node) *Type {
	return c.checkExpressionCachedEx(node, CheckModeNormal)
}

func (c *Checker) checkExpressionCachedEx(node *ast.Node, checkMode CheckMode) *Type {
	if checkMode != CheckModeNormal {
		return c.checkExpressionEx(node, checkMode)
	}
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		// When computing a type that we're going to cache, we need to ignore any ongoing control flow
		// analysis because variables may have transient types in indeterminable states. Moving flowLoopStart
		// to the top of the stack ensures all transient types are computed from a known point.
		saveFlowLoopStack := c.flowLoopStack
		saveFlowTypeCache := c.flowTypeCache
		c.flowLoopStack = nil
		c.flowTypeCache = nil
		links.resolvedType = c.checkExpressionEx(node, checkMode)
		c.flowTypeCache = saveFlowTypeCache
		c.flowLoopStack = saveFlowLoopStack
	}
	return links.resolvedType
}

// Returns the type of an expression. Unlike checkExpression, this function is simply concerned
// with computing the type and may not fully check all contained sub-expressions for errors.
// It is intended for uses where you know there is no contextual type,
// and requesting the contextual type might cause a circularity or other bad behaviour.
// It sets the contextual type of the node to any before calling getTypeOfExpression.
func (c *Checker) getContextFreeTypeOfExpression(node *ast.Node) *Type {
	if cached := c.contextFreeTypes[node]; cached != nil {
		return cached
	}
	c.pushContextualType(node, c.anyType, false /*isCache*/)
	t := c.checkExpressionEx(node, CheckModeSkipContextSensitive)
	c.contextFreeTypes[node] = t
	c.popContextualType()
	return t
}

func (c *Checker) checkExpression(node *ast.Node) *Type {
	return c.checkExpressionEx(node, CheckModeNormal)
}

func (c *Checker) checkExpressionEx(node *ast.Node, checkMode CheckMode) *Type {
	if tr := c.tracer; tr != nil {
		defer tr.Push(tracing.PhaseCheck, "checkExpression", map[string]any{"kind": node.Kind, "pos": node.Pos(), "end": node.End(), "path": ast.GetSourceFileOfNode(node).FileName()}, false)()
	}
	saveCurrentNode := c.currentNode
	c.currentNode = node
	c.instantiationCount = 0
	uninstantiatedType := c.checkExpressionWorker(node, checkMode)
	t := c.instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode)
	if isConstEnumObjectType(t) {
		c.checkConstEnumAccess(node, t)
	}
	c.currentNode = saveCurrentNode
	return t
}

func (c *Checker) checkConstEnumAccess(node *ast.Node, t *Type) {
	// enum object type for const enums are only permitted in:
	// - 'left' in property access
	// - 'object' in indexed access
	// - target in rhs of import statement
	ok := ast.IsPropertyAccessExpression(node.Parent) && node.Parent.Expression() == node ||
		ast.IsElementAccessExpression(node.Parent) && node.Parent.Expression() == node ||
		((ast.IsIdentifier(node) || ast.IsQualifiedName(node)) && isInRightSideOfImportOrExportAssignment(node) ||
			ast.IsTypeQueryNode(node.Parent) && node.Parent.AsTypeQueryNode().ExprName == node) ||
		ast.IsExportSpecifier(node.Parent) // We allow reexporting const enums
	if !ok {
		c.error(node, diagnostics.X_const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment_or_type_query)
	}
	// --verbatimModuleSyntax only gets checked here when the enum usage does not
	// resolve to an import, because imports of ambient const enums get checked
	// separately in `checkAliasSymbol`.
	if c.compilerOptions.IsolatedModules.IsTrue() || c.compilerOptions.VerbatimModuleSyntax.IsTrue() && ok && c.resolveName(node, ast.GetFirstIdentifier(node).Text(), ast.SymbolFlagsAlias, nil, false, true) == nil {
		debug.Assert(t.symbol.Flags&ast.SymbolFlagsConstEnum != 0)
		constEnumDeclaration := t.symbol.ValueDeclaration
		redirect := c.program.GetProjectReferenceFromOutputDts(ast.GetSourceFileOfNode(constEnumDeclaration).Path())
		if constEnumDeclaration.Flags&ast.NodeFlagsAmbient != 0 && !ast.IsValidTypeOnlyAliasUseSite(node) && (redirect == nil || !redirect.Resolved.CompilerOptions().ShouldPreserveConstEnums()) {
			c.error(node, diagnostics.Cannot_access_ambient_const_enums_when_0_is_enabled, c.getIsolatedModulesLikeFlagName())
		}
	}
}

func (c *Checker) instantiateTypeWithSingleGenericCallSignature(node *ast.Node, t *Type, checkMode CheckMode) *Type {
	if checkMode&(CheckModeInferential|CheckModeSkipGenericFunctions) == 0 {
		return t
	}
	callSignature := c.getSingleSignature(t, SignatureKindCall, true /*allowMembers*/)
	constructSignature := c.getSingleSignature(t, SignatureKindConstruct, true /*allowMembers*/)
	signature := core.OrElse(callSignature, constructSignature)
	if signature == nil || len(signature.typeParameters) == 0 {
		return t
	}
	contextualType := c.getApparentTypeOfContextualType(node, ContextFlagsNoConstraints)
	if contextualType == nil {
		return t
	}
	contextualSignature := c.getSingleSignature(c.GetNonNullableType(contextualType), core.IfElse(callSignature != nil, SignatureKindCall, SignatureKindConstruct), false /*allowMembers*/)
	if contextualSignature == nil || len(contextualSignature.typeParameters) != 0 {
		return t
	}
	if checkMode&CheckModeSkipGenericFunctions != 0 {
		c.skippedGenericFunction(node, checkMode)
		return c.anyFunctionType
	}
	context := c.getInferenceContext(node)
	// We have an expression that is an argument of a generic function for which we are performing
	// type argument inference. The expression is of a function type with a single generic call
	// signature and a contextual function type with a single non-generic call signature. Now check
	// if the outer function returns a function type with a single non-generic call signature and
	// if some of the outer function type parameters have no inferences so far. If so, we can
	// potentially add inferred type parameters to the outer function return type.
	var returnSignature *Signature
	if context.signature != nil {
		returnType := c.getReturnTypeOfSignature(context.signature)
		if returnType != nil {
			returnSignature = c.getSingleCallOrConstructSignature(returnType)
		}
	}
	if returnSignature != nil && len(returnSignature.typeParameters) == 0 && !core.Every(context.inferences, hasInferenceCandidates) {
		// Instantiate the signature with its own type parameters as type arguments, possibly
		// renaming the type parameters to ensure they have unique names.
		uniqueTypeParameters := c.getUniqueTypeParameters(context, signature.typeParameters)
		instantiatedSignature := c.getSignatureInstantiationWithoutFillingInTypeArguments(signature, uniqueTypeParameters)
		// Infer from the parameters of the instantiated signature to the parameters of the
		// contextual signature starting with an empty set of inference candidates.
		inferences := core.Map(context.inferences, func(info *InferenceInfo) *InferenceInfo {
			return newInferenceInfo(info.typeParameter)
		})
		c.applyToParameterTypes(instantiatedSignature, contextualSignature, func(source *Type, target *Type) {
			c.inferTypes(inferences, source, target, InferencePriorityNone, true /*contravariant*/)
		})
		if core.Some(inferences, hasInferenceCandidates) {
			// We have inference candidates, indicating that one or more type parameters are referenced
			// in the parameter types of the contextual signature. Now also infer from the return type.
			c.applyToReturnTypes(instantiatedSignature, contextualSignature, func(source *Type, target *Type) {
				c.inferTypes(inferences, source, target, InferencePriorityNone, false)
			})
			// If the type parameters for which we produced candidates do not have any inferences yet,
			// we adopt the new inference candidates and add the type parameters of the expression type
			// to the set of inferred type parameters for the outer function return type.
			if !hasOverlappingInferences(context.inferences, inferences) {
				c.mergeInferences(context.inferences, inferences)
				context.inferredTypeParameters = core.Concatenate(context.inferredTypeParameters, uniqueTypeParameters)
				return c.getOrCreateTypeFromSignature(instantiatedSignature)
			}
		}
	}
	// TODO: The signature may reference any outer inference contexts, but we map pop off and then apply new inference contexts,
	// and thus get different inferred types. That this is cached on the *first* such attempt is not currently an issue, since expression
	// types *also* get cached on the first pass. If we ever properly speculate, though, the cached "isolatedSignatureType" signature
	// field absolutely needs to be included in the list of speculative caches.
	return c.getOrCreateTypeFromSignature(c.instantiateSignatureInContextOf(signature, contextualSignature, context, nil))
}

func (c *Checker) getOuterInferenceTypeParameters() []*Type {
	var result []*Type
	for i := range c.inferenceContextInfos {
		context := c.inferenceContextInfos[i].context
		if context != nil {
			for _, info := range context.inferences {
				result = append(result, info.typeParameter)
			}
		}
	}
	return result
}

func (c *Checker) getUniqueTypeParameters(context *InferenceContext, typeParameters []*Type) []*Type {
	var oldTypeParameters []*Type
	var newTypeParameters []*Type
	result := make([]*Type, 0, len(typeParameters))
	for _, tp := range typeParameters {
		name := tp.symbol.Name
		if hasTypeParameterByName(context.inferredTypeParameters, name) || hasTypeParameterByName(result, name) {
			newName := getUniqueTypeParameterName(core.Concatenate(context.inferredTypeParameters, result), name)
			symbol := c.newSymbol(ast.SymbolFlagsTypeParameter, newName)
			newTypeParameter := c.newTypeParameter(symbol)
			newTypeParameter.AsTypeParameter().target = tp
			oldTypeParameters = append(oldTypeParameters, tp)
			newTypeParameters = append(newTypeParameters, newTypeParameter)
			result = append(result, newTypeParameter)
		} else {
			result = append(result, tp)
		}
	}
	if len(newTypeParameters) != 0 {
		mapper := newTypeMapper(oldTypeParameters, newTypeParameters)
		for _, tp := range newTypeParameters {
			tp.AsTypeParameter().mapper = mapper
		}
	}
	return result
}

func hasTypeParameterByName(typeParameters []*Type, name string) bool {
	return core.Some(typeParameters, func(tp *Type) bool {
		return tp.symbol.Name == name
	})
}

func getUniqueTypeParameterName(typeParameters []*Type, baseName string) string {
	for len(baseName) > 1 && baseName[len(baseName)-1] >= '0' && baseName[len(baseName)-1] <= '9' {
		baseName = baseName[:len(baseName)-1]
	}
	index := 1
	for {
		augmentedName := baseName + strconv.Itoa(index)
		if !hasTypeParameterByName(typeParameters, augmentedName) {
			return augmentedName
		}
		index++
	}
}

func (c *Checker) checkExpressionWorker(node *ast.Node, checkMode CheckMode) *Type {
	switch node.Kind {
	case ast.KindIdentifier:
		return c.checkIdentifier(node, checkMode)
	case ast.KindPrivateIdentifier:
		return c.checkPrivateIdentifierExpression(node)
	case ast.KindThisKeyword:
		return c.checkThisExpression(node)
	case ast.KindSuperKeyword:
		return c.checkSuperExpression(node)
	case ast.KindNullKeyword:
		return c.nullWideningType
	case ast.KindStringLiteral, ast.KindNoSubstitutionTemplateLiteral:
		if c.isSkipDirectInferenceNode(node) {
			return c.blockedStringType
		}
		return c.getFreshTypeOfLiteralType(c.getStringLiteralType(node.Text()))
	case ast.KindNumericLiteral:
		c.checkGrammarNumericLiteral(node.AsNumericLiteral())
		return c.getFreshTypeOfLiteralType(c.getNumberLiteralType(jsnum.FromString(node.Text())))
	case ast.KindBigIntLiteral:
		c.checkGrammarBigIntLiteral(node.AsBigIntLiteral())
		return c.getFreshTypeOfLiteralType(c.getBigIntLiteralType(jsnum.NewPseudoBigInt(jsnum.ParsePseudoBigInt(node.Text()), false /*negative*/)))
	case ast.KindTrueKeyword:
		return c.trueType
	case ast.KindFalseKeyword:
		return c.falseType
	case ast.KindTemplateExpression:
		return c.checkTemplateExpression(node)
	case ast.KindRegularExpressionLiteral:
		return c.checkRegularExpressionLiteral(node)
	case ast.KindArrayLiteralExpression:
		return c.checkArrayLiteral(node, checkMode)
	case ast.KindObjectLiteralExpression:
		return c.checkObjectLiteral(node, checkMode)
	case ast.KindPropertyAccessExpression:
		return c.checkPropertyAccessExpression(node, checkMode, false /*writeOnly*/)
	case ast.KindQualifiedName:
		return c.checkQualifiedName(node, checkMode)
	case ast.KindElementAccessExpression:
		return c.checkIndexedAccess(node, checkMode)
	case ast.KindCallExpression:
		if ast.IsImportCall(node) {
			return c.checkImportCallExpression(node)
		}
		return c.checkCallExpression(node, checkMode)
	case ast.KindNewExpression:
		return c.checkCallExpression(node, checkMode)
	case ast.KindTaggedTemplateExpression:
		return c.checkTaggedTemplateExpression(node)
	case ast.KindParenthesizedExpression:
		return c.checkParenthesizedExpression(node, checkMode)
	case ast.KindClassExpression:
		return c.checkClassExpression(node)
	case ast.KindFunctionExpression, ast.KindArrowFunction:
		return c.checkFunctionExpressionOrObjectLiteralMethod(node, checkMode)
	case ast.KindTypeAssertionExpression, ast.KindAsExpression:
		return c.checkAssertion(node, checkMode)
	case ast.KindTypeOfExpression:
		return c.checkTypeOfExpression(node)
	case ast.KindNonNullExpression:
		return c.checkNonNullAssertion(node)
	case ast.KindExpressionWithTypeArguments:
		return c.checkExpressionWithTypeArguments(node)
	case ast.KindSatisfiesExpression:
		return c.checkSatisfiesExpression(node)
	case ast.KindMetaProperty:
		return c.checkMetaProperty(node)
	case ast.KindDeleteExpression:
		return c.checkDeleteExpression(node)
	case ast.KindVoidExpression:
		return c.checkVoidExpression(node)
	case ast.KindAwaitExpression:
		return c.checkAwaitExpression(node)
	case ast.KindPrefixUnaryExpression:
		return c.checkPrefixUnaryExpression(node)
	case ast.KindPostfixUnaryExpression:
		return c.checkPostfixUnaryExpression(node)
	case ast.KindBinaryExpression:
		return c.checkBinaryExpression(node, checkMode)
	case ast.KindConditionalExpression:
		return c.checkConditionalExpression(node, checkMode)
	case ast.KindSpreadElement:
		return c.checkSpreadExpression(node, checkMode)
	case ast.KindOmittedExpression:
		return c.undefinedWideningType
	case ast.KindYieldExpression:
		return c.checkYieldExpression(node)
	case ast.KindSyntheticExpression:
		return c.checkSyntheticExpression(node)
	case ast.KindJsxExpression:
		return c.checkJsxExpression(node, checkMode)
	case ast.KindJsxElement:
		return c.checkJsxElement(node, checkMode)
	case ast.KindJsxSelfClosingElement:
		return c.checkJsxSelfClosingElement(node, checkMode)
	case ast.KindJsxFragment:
		return c.checkJsxFragment(node)
	case ast.KindJsxAttributes:
		return c.checkJsxAttributes(node, checkMode)
	case ast.KindJsxOpeningElement:
		panic("Should never directly check a JsxOpeningElement")
	}
	return c.errorType
}

func (c *Checker) checkPrivateIdentifierExpression(node *ast.Node) *Type {
	c.checkGrammarPrivateIdentifierExpression(node.AsPrivateIdentifier())
	symbol := c.getSymbolForPrivateIdentifierExpression(node)
	if symbol != nil {
		c.markPropertyAsReferenced(symbol, nil /*nodeForCheckWriteOnly*/, false /*isSelfTypeAccess*/)
	}
	return c.anyType
}

func (c *Checker) getSymbolForPrivateIdentifierExpression(node *ast.Node) *ast.Symbol {
	links := c.symbolNodeLinks.Get(node)
	if links.resolvedSymbol == nil {
		links.resolvedSymbol = c.lookupSymbolForPrivateIdentifierDeclaration(node.Text(), node)
	}
	return links.resolvedSymbol
}

func (c *Checker) checkSuperExpression(node *ast.Node) *Type {
	isCallExpression := ast.IsCallExpression(node.Parent) && node.Parent.Expression() == node
	immediateContainer := getSuperContainer(node, true /*stopOnFunctions*/)
	container := immediateContainer

	// adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting
	if !isCallExpression {
		for container != nil && ast.IsArrowFunction(container) {
			container = getSuperContainer(container, true /*stopOnFunctions*/)
		}
	}

	isLegalUsageOfSuperExpression := func() bool {
		if isCallExpression {
			// TS 1.0 SPEC (April 2014): 4.8.1
			// Super calls are only permitted in constructors of derived classes
			return ast.IsConstructorDeclaration(container)
		}
		// TS 1.0 SPEC (April 2014)
		// 'super' property access is allowed
		// - In a constructor, instance member function, instance member accessor, or instance member variable initializer where this references a derived class instance
		// - In a static member function or static member accessor

		// topmost container must be something that is directly nested in the class declaration\object literal expression
		if ast.IsClassLike(container.Parent) || ast.IsObjectLiteralExpression(container.Parent) {
			if ast.IsStatic(container) {
				return ast.NodeKindIs(container, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyDeclaration, ast.KindClassStaticBlockDeclaration)
			}
			return ast.NodeKindIs(container, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindConstructor)
		}
		return false
	}

	if container == nil || !isLegalUsageOfSuperExpression() {
		// issue more specific error if super is used in computed property name
		// class A { foo() { return "1" }}
		// class B {
		//     [super.foo()]() {}
		// }
		current := ast.FindAncestorOrQuit(node, func(n *ast.Node) ast.FindAncestorResult {
			if n == container {
				return ast.FindAncestorQuit
			}
			if ast.IsComputedPropertyName(n) {
				return ast.FindAncestorTrue
			}
			return ast.FindAncestorFalse
		})
		switch {
		case current != nil && ast.IsComputedPropertyName(current):
			c.error(node, diagnostics.X_super_cannot_be_referenced_in_a_computed_property_name)
		case isCallExpression:
			c.error(node, diagnostics.Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors)
		case container == nil || container.Parent == nil || !(ast.IsClassLike(container.Parent) || ast.IsObjectLiteralExpression(container.Parent)):
			c.error(node, diagnostics.X_super_can_only_be_referenced_in_members_of_derived_classes_or_object_literal_expressions)
		default:
			c.error(node, diagnostics.X_super_property_access_is_permitted_only_in_a_constructor_member_function_or_member_accessor_of_a_derived_class)
		}
		return c.errorType
	}
	if !isCallExpression && ast.IsConstructorDeclaration(immediateContainer) {
		c.checkThisBeforeSuper(node, container, diagnostics.X_super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class)
	}
	if container.Parent.Kind == ast.KindObjectLiteralExpression {
		// for object literal assume that type of 'super' is 'any'
		return c.anyType
	}
	// at this point the only legal case for parent is ClassLikeDeclaration
	classLikeDeclaration := container.Parent
	if ast.GetExtendsHeritageClauseElement(classLikeDeclaration) == nil {
		c.error(node, diagnostics.X_super_can_only_be_referenced_in_a_derived_class)
		return c.errorType
	}
	if c.classDeclarationExtendsNull(classLikeDeclaration) {
		if isCallExpression {
			return c.errorType
		}
		return c.nullWideningType
	}
	classType := c.getDeclaredTypeOfSymbol(c.getSymbolOfDeclaration(classLikeDeclaration))
	var baseClassType *Type
	if classType != nil {
		baseClassType = core.FirstOrNil(c.getBaseTypes(classType))
	}
	if baseClassType == nil {
		return c.errorType
	}
	if ast.IsConstructorDeclaration(container) && c.isInConstructorArgumentInitializer(node, container) {
		// issue custom error message for super property access in constructor arguments (to be aligned with old compiler)
		c.error(node, diagnostics.X_super_cannot_be_referenced_in_constructor_arguments)
		return c.errorType
	}
	if ast.IsStatic(container) || isCallExpression {
		if !isCallExpression &&
			c.languageVersion <= core.ScriptTargetES2021 &&
			(ast.IsPropertyDeclaration(container) || ast.IsClassStaticBlockDeclaration(container)) {
			// for `super.x` or `super[x]` in a static initializer, mark all enclosing
			// block scope containers so that we can report potential collisions with
			// `Reflect`.
			for current := ast.GetEnclosingBlockScopeContainer(node.Parent); current != nil; current = ast.GetEnclosingBlockScopeContainer(current) {
				if !ast.IsSourceFile(current) || ast.IsExternalOrCommonJSModule(current.AsSourceFile()) {
					c.nodeLinks.Get(current).flags |= NodeCheckFlagsContainsSuperPropertyInStaticInitializer
				}
			}
		}
		return c.getBaseConstructorTypeOfClass(classType)
	}
	return c.getTypeWithThisArgument(baseClassType, classType.AsInterfaceType().thisType, false)
}

func (c *Checker) isInConstructorArgumentInitializer(node *ast.Node, constructorDecl *ast.Node) bool {
	return ast.FindAncestorOrQuit(node, func(n *ast.Node) ast.FindAncestorResult {
		if ast.IsFunctionLikeDeclaration(n) {
			return ast.FindAncestorQuit
		}
		if ast.IsParameterDeclaration(n) && n.Parent == constructorDecl {
			return ast.FindAncestorTrue
		}
		return ast.FindAncestorFalse
	}) != nil
}

func (c *Checker) checkTemplateExpression(node *ast.Node) *Type {
	expr := node.AsTemplateExpression()
	length := len(expr.TemplateSpans.Nodes)
	texts := make([]string, length+1)
	types := make([]*Type, length)
	texts[0] = expr.Head.Text()
	for i, span := range expr.TemplateSpans.Nodes {
		t := c.checkExpression(span.Expression())
		if c.maybeTypeOfKindConsideringBaseConstraint(t, TypeFlagsESSymbolLike) {
			c.error(span.Expression(), diagnostics.Implicit_conversion_of_a_symbol_to_a_string_will_fail_at_runtime_Consider_wrapping_this_expression_in_String)
		}
		texts[i+1] = span.AsTemplateSpan().Literal.Text()
		types[i] = core.IfElse(c.isTypeAssignableTo(t, c.templateConstraintType), t, c.stringType)
	}
	var evaluated any
	if !ast.IsTaggedTemplateExpression(node.Parent) {
		evaluated = c.evaluate(node, node).Value
	}
	if evaluated != nil {
		return c.getFreshTypeOfLiteralType(c.getStringLiteralType(evaluated.(string)))
	}
	if c.isConstContext(node) || c.isTemplateLiteralContext(node) || someType(core.OrElse(c.getContextualType(node, ContextFlagsNone), c.unknownType), c.isTemplateLiteralContextualType) {
		return c.getTemplateLiteralType(texts, types)
	}
	return c.stringType
}

func (c *Checker) isTemplateLiteralContext(node *ast.Node) bool {
	parent := node.Parent
	return ast.IsParenthesizedExpression(parent) && c.isTemplateLiteralContext(parent) || ast.IsElementAccessExpression(parent) && parent.AsElementAccessExpression().ArgumentExpression == node
}

func (c *Checker) isTemplateLiteralContextualType(t *Type) bool {
	return t.flags&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral) != 0 || t.flags&TypeFlagsInstantiableNonPrimitive != 0 && c.maybeTypeOfKind(core.OrElse(c.getBaseConstraintOfType(t), c.unknownType), TypeFlagsStringLike)
}

func (c *Checker) checkRegularExpressionLiteral(node *ast.Node) *Type {
	nodeLinks := c.nodeLinks.Get(node)
	if nodeLinks.flags&NodeCheckFlagsTypeChecked == 0 {
		nodeLinks.flags |= NodeCheckFlagsTypeChecked
		c.checkGrammarRegularExpressionLiteral(node.AsRegularExpressionLiteral())
	}
	return c.globalRegExpType
}

func (c *Checker) checkArrayLiteral(node *ast.Node, checkMode CheckMode) *Type {
	elements := node.Elements()
	elementTypes := make([]*Type, len(elements))
	elementInfos := make([]TupleElementInfo, len(elements))
	c.pushCachedContextualType(node)
	inDestructuringPattern := ast.IsAssignmentTarget(node)
	inConstContext := c.isConstContext(node)
	contextualType := c.getApparentTypeOfContextualType(node, ContextFlagsNone)
	inTupleContext := isSpreadIntoCallOrNew(node) || contextualType != nil && someType(contextualType, func(t *Type) bool {
		return c.isTupleLikeType(t) || c.isGenericMappedType(t) && t.AsMappedType().nameType == nil && c.getHomomorphicTypeVariable(core.OrElse(t.AsMappedType().target, t)) != nil
	})
	hasOmittedExpression := false
	for i, e := range elements {
		switch {
		case ast.IsSpreadElement(e):
			spreadType := c.checkExpressionEx(e.Expression(), checkMode)
			switch {
			case c.isArrayLikeType(spreadType):
				elementTypes[i] = spreadType
				elementInfos[i] = TupleElementInfo{flags: ElementFlagsVariadic}
			case inDestructuringPattern:
				// Given the following situation:
				//    var c: {};
				//    [...c] = ["", 0];
				//
				// c is represented in the tree as a spread element in an array literal.
				// But c really functions as a rest element, and its purpose is to provide
				// a contextual type for the right hand side of the assignment. Therefore,
				// instead of calling checkExpression on "...c", which will give an error
				// if c is not iterable/array-like, we need to act as if we are trying to
				// get the contextual element type from it. So we do something similar to
				// getContextualTypeForElementExpression, which will crucially not error
				// if there is no index type / iterated type.
				restElementType := c.getIndexTypeOfType(spreadType, c.numberType)
				if restElementType == nil {
					restElementType = c.getIteratedTypeOrElementType(IterationUseDestructuring, spreadType, c.undefinedType, nil /*errorNode*/, false /*checkAssignability*/)
					if restElementType == nil {
						restElementType = c.unknownType
					}
				}
				elementTypes[i] = restElementType
				elementInfos[i] = TupleElementInfo{flags: ElementFlagsRest}
			default:
				elementTypes[i] = c.checkIteratedTypeOrElementType(IterationUseSpread, spreadType, c.undefinedType, e.Expression())
				elementInfos[i] = TupleElementInfo{flags: ElementFlagsRest}
			}
		case c.exactOptionalPropertyTypes && ast.IsOmittedExpression(e):
			hasOmittedExpression = true
			elementTypes[i] = c.undefinedOrMissingType
			elementInfos[i] = TupleElementInfo{flags: ElementFlagsOptional}
		default:
			t := c.checkExpressionForMutableLocation(e, checkMode)
			elementTypes[i] = c.addOptionalityEx(t, true /*isProperty*/, hasOmittedExpression)
			elementInfos[i] = TupleElementInfo{flags: core.IfElse(hasOmittedExpression, ElementFlagsOptional, ElementFlagsRequired)}
			if inTupleContext && checkMode&CheckModeInferential != 0 && checkMode&CheckModeSkipContextSensitive == 0 && c.isContextSensitive(e) {
				inferenceContext := c.getInferenceContext(node)
				// In CheckMode.Inferential we should always have an inference context
				c.addIntraExpressionInferenceSite(inferenceContext, e, t)
			}
		}
	}
	c.popContextualType()
	if inDestructuringPattern {
		return c.createTupleTypeEx(elementTypes, elementInfos, false)
	}
	if checkMode&CheckModeForceTuple != 0 || inConstContext || inTupleContext {
		return c.createArrayLiteralType(c.createTupleTypeEx(elementTypes, elementInfos, inConstContext && !(contextualType != nil && someType(contextualType, c.isMutableArrayLikeType)) /*readonly*/))
	}
	var elementType *Type
	if len(elementTypes) != 0 {
		for i, e := range elementTypes {
			if elementInfos[i].flags&ElementFlagsVariadic != 0 {
				elementTypes[i] = core.OrElse(c.getIndexedAccessTypeOrUndefined(e, c.numberType, AccessFlagsNone, nil, nil), c.anyType)
			}
		}
		elementType = c.getUnionTypeEx(elementTypes, UnionReductionSubtype, nil, nil)
	} else {
		elementType = core.IfElse(c.strictNullChecks, c.implicitNeverType, c.undefinedWideningType)
	}
	return c.createArrayLiteralType(c.createArrayTypeEx(elementType, inConstContext))
}

func (c *Checker) createArrayLiteralType(t *Type) *Type {
	if t.objectFlags&ObjectFlagsReference == 0 {
		return t
	}
	key := CachedTypeKey{kind: CachedTypeKindArrayLiteralType, typeId: t.id}
	if cached, ok := c.cachedTypes[key]; ok {
		return cached
	}
	literalType := c.cloneTypeReference(t)
	literalType.objectFlags |= ObjectFlagsArrayLiteral | ObjectFlagsContainsObjectOrArrayLiteral
	c.cachedTypes[key] = literalType
	return literalType
}

func isSpreadIntoCallOrNew(node *ast.Node) bool {
	parent := ast.WalkUpParenthesizedExpressions(node.Parent)
	return ast.IsSpreadElement(parent) && ast.IsCallOrNewExpression(parent.Parent)
}

func (c *Checker) checkQualifiedName(node *ast.Node, checkMode CheckMode) *Type {
	left := node.AsQualifiedName().Left
	var leftType *Type
	if ast.IsPartOfTypeQuery(node) && ast.IsThisIdentifier(left) {
		leftType = c.checkNonNullType(c.checkThisExpression(left), left)
	} else {
		leftType = c.checkNonNullExpression(left)
	}
	return c.checkPropertyAccessExpressionOrQualifiedName(node, left, leftType, node.AsQualifiedName().Right, checkMode, false)
}

func (c *Checker) checkIndexedAccess(node *ast.Node, checkMode CheckMode) *Type {
	if node.Flags&ast.NodeFlagsOptionalChain != 0 {
		return c.checkElementAccessChain(node, checkMode)
	}
	return c.checkElementAccessExpression(node, c.checkNonNullExpression(node.Expression()), checkMode)
}

func (c *Checker) checkElementAccessChain(node *ast.Node, checkMode CheckMode) *Type {
	exprType := c.checkExpression(node.Expression())
	nonOptionalType := c.getOptionalExpressionType(exprType, node.Expression())
	return c.propagateOptionalTypeMarker(c.checkElementAccessExpression(node, c.checkNonNullType(nonOptionalType, node.Expression()), checkMode), node, nonOptionalType != exprType)
}

func (c *Checker) checkElementAccessExpression(node *ast.Node, exprType *Type, checkMode CheckMode) *Type {
	objectType := exprType
	if getAssignmentTargetKind(node) != AssignmentKindNone || c.isMethodAccessForCall(node) {
		objectType = c.getWidenedType(objectType)
	}
	indexExpression := node.AsElementAccessExpression().ArgumentExpression
	indexType := c.checkExpression(indexExpression)
	if c.isErrorType(objectType) || objectType == c.silentNeverType {
		return objectType
	}
	if isConstEnumObjectType(objectType) && !ast.IsStringLiteralLike(indexExpression) {
		c.error(indexExpression, diagnostics.A_const_enum_member_can_only_be_accessed_using_a_string_literal)
		return c.errorType
	}
	effectiveIndexType := indexType
	if c.isForInVariableForNumericPropertyNames(indexExpression) {
		effectiveIndexType = c.numberType
	}
	assignmentTargetKind := getAssignmentTargetKind(node)
	var accessFlags AccessFlags
	if assignmentTargetKind == AssignmentKindNone {
		accessFlags = AccessFlagsExpressionPosition
	} else {
		accessFlags = AccessFlagsWriting |
			core.IfElse(assignmentTargetKind == AssignmentKindCompound, AccessFlagsExpressionPosition, 0) |
			core.IfElse(c.isGenericObjectType(objectType) && !isThisTypeParameter(objectType), AccessFlagsNoIndexSignatures, 0)
	}
	indexedAccessType := core.OrElse(c.getIndexedAccessTypeOrUndefined(objectType, effectiveIndexType, accessFlags, node, nil), c.errorType)
	return c.checkIndexedAccessIndexType(c.getFlowTypeOfAccessExpression(node, c.getResolvedSymbolOrNil(node), indexedAccessType, indexExpression, checkMode), node)
}

// Return true if given node is an expression consisting of an identifier (possibly parenthesized)
// that references a for-in variable for an object with numeric property names.
func (c *Checker) isForInVariableForNumericPropertyNames(expr *ast.Node) bool {
	e := ast.SkipParentheses(expr)
	if ast.IsIdentifier(e) {
		symbol := c.getResolvedSymbol(e)
		if symbol.Flags&ast.SymbolFlagsVariable != 0 {
			child := expr
			node := expr.Parent
			for node != nil {
				if ast.IsForInStatement(node) && child == node.AsForInOrOfStatement().Statement && c.getForInVariableSymbol(node) == symbol && c.hasNumericPropertyNames(c.getTypeOfExpression(node.Expression())) {
					return true
				}
				child = node
				node = node.Parent
			}
		}
	}
	return false
}

// Return the symbol of the for-in variable declared or referenced by the given for-in statement.
func (c *Checker) getForInVariableSymbol(node *ast.Node) *ast.Symbol {
	initializer := node.Initializer()
	if ast.IsVariableDeclarationList(initializer) {
		declarations := initializer.AsVariableDeclarationList().Declarations.Nodes
		if len(declarations) > 0 {
			variable := declarations[0]
			if variable != nil && !ast.IsBindingPattern(variable.Name()) {
				return c.getSymbolOfDeclaration(variable)
			}
		}
	} else if ast.IsIdentifier(initializer) {
		return c.getResolvedSymbol(initializer)
	}
	return nil
}

// Return true if the given type is considered to have numeric property names.
func (c *Checker) hasNumericPropertyNames(t *Type) bool {
	return len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.numberType) != nil
}

func (c *Checker) checkIndexedAccessIndexType(t *Type, accessNode *ast.Node) *Type {
	if t.flags&TypeFlagsIndexedAccess == 0 {
		return t
	}
	// Check if the index type is assignable to 'keyof T' for the object type.
	objectType := t.AsIndexedAccessType().objectType
	indexType := t.AsIndexedAccessType().indexType
	// skip index type deferral on remapping mapped types
	var objectIndexType *Type
	if c.isGenericMappedType(objectType) && c.getMappedTypeNameTypeKind(objectType) == MappedTypeNameTypeKindRemapping {
		objectIndexType = c.getIndexTypeForMappedType(objectType, IndexFlagsNone)
	} else {
		objectIndexType = c.getIndexTypeEx(objectType, IndexFlagsNone)
	}
	hasNumberIndexInfo := c.getIndexInfoOfType(objectType, c.numberType) != nil
	if everyType(indexType, func(t *Type) bool {
		return c.isTypeAssignableTo(t, objectIndexType) || hasNumberIndexInfo && c.isApplicableIndexType(t, c.numberType)
	}) {
		if accessNode.Kind == ast.KindElementAccessExpression && ast.IsAssignmentTarget(accessNode) && objectType.objectFlags&ObjectFlagsMapped != 0 && getMappedTypeModifiers(objectType)&MappedTypeModifiersIncludeReadonly != 0 {
			c.error(accessNode, diagnostics.Index_signature_in_type_0_only_permits_reading, c.TypeToString(objectType))
		}
		return t
	}
	if c.isGenericObjectType(objectType) {
		propertyName := c.getPropertyNameFromIndex(indexType, accessNode)
		if propertyName != ast.InternalSymbolNameMissing {
			propertySymbol := c.getConstituentProperty(objectType, propertyName)
			if propertySymbol != nil && getDeclarationModifierFlagsFromSymbol(propertySymbol)&ast.ModifierFlagsNonPublicAccessibilityModifier != 0 {
				c.error(accessNode, diagnostics.Private_or_protected_member_0_cannot_be_accessed_on_a_type_parameter, propertyName)
				return c.errorType
			}
		}
	}
	c.error(accessNode, diagnostics.Type_0_cannot_be_used_to_index_type_1, c.TypeToString(indexType), c.TypeToString(objectType))
	return c.errorType
}

func (c *Checker) getConstituentProperty(objectType *Type, propertyName string) *ast.Symbol {
	for _, t := range c.getApparentType(objectType).Distributed() {
		prop := c.getPropertyOfType(t, propertyName)
		if prop != nil {
			return prop
		}
	}
	return nil
}

func (c *Checker) checkImportCallExpression(node *ast.Node) *Type {
	// Check grammar of dynamic import
	c.checkGrammarImportCallExpression(node)
	args := node.Arguments()
	if len(args) == 0 {
		return c.createPromiseReturnType(node, c.anyType)
	}
	specifier := args[0]
	specifierType := c.checkExpressionCached(specifier)
	var optionsType *Type
	if len(args) > 1 {
		optionsType = c.checkExpressionCached(args[1])
	}
	// Even though multiple arguments is grammatically incorrect, type-check extra arguments for completion
	for i := 2; i < len(args); i++ {
		c.checkExpressionCached(args[i])
	}
	if specifierType.flags&TypeFlagsNullable != 0 || !c.isTypeAssignableTo(specifierType, c.stringType) {
		c.error(specifier, diagnostics.Dynamic_import_s_specifier_must_be_of_type_string_but_here_has_type_0, c.TypeToString(specifierType))
	}
	if optionsType != nil {
		importCallOptionsType := c.getGlobalImportCallOptionsTypeChecked()
		if importCallOptionsType != c.emptyObjectType {
			c.checkTypeAssignableTo(optionsType, c.getNullableType(importCallOptionsType, TypeFlagsUndefined), args[1], nil)
		}
		if ast.IsObjectLiteralExpression(args[1]) {
			for _, prop := range args[1].AsObjectLiteralExpression().Properties.Nodes {
				if ast.IsPropertyAssignment(prop) && ast.IsIdentifier(prop.Name()) && prop.Name().Text() == "assert" {
					c.error(prop.Name(), diagnostics.Import_assertions_have_been_replaced_by_import_attributes_Use_with_instead_of_assert)
					break
				}
			}
		}
	}
	// resolveExternalModuleName will return undefined if the moduleReferenceExpression is not a string literal
	moduleSymbol := c.resolveExternalModuleName(node, specifier, false /*ignoreErrors*/)
	if moduleSymbol != nil {
		esModuleSymbol := c.resolveExternalModuleSymbol(moduleSymbol, true /*dontResolveAlias*/)
		if esModuleSymbol != nil {
			syntheticType := c.getTypeWithSyntheticDefaultOnly(c.getTypeOfSymbol(esModuleSymbol), esModuleSymbol, moduleSymbol, specifier)
			if syntheticType == nil {
				syntheticType = c.getTypeWithSyntheticDefaultImportType(c.getTypeOfSymbol(esModuleSymbol), esModuleSymbol, moduleSymbol, specifier)
			}
			return c.createPromiseReturnType(node, syntheticType)
		}
	}
	return c.createPromiseReturnType(node, c.anyType)
}

/**
 * Syntactically and semantically checks a call or new expression.
 * @param node The call/new expression to be checked.
 * @returns On success, the expression's signature's return type. On failure, anyType.
 */
func (c *Checker) checkCallExpression(node *ast.Node, checkMode CheckMode) *Type {
	c.checkGrammarTypeArguments(node, node.TypeArgumentList())
	signature := c.getResolvedSignature(node, nil /*candidatesOutArray*/, checkMode)
	if signature == c.resolvingSignature {
		// CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that
		// returns a function type. We defer checking and return silentNeverType.
		return c.silentNeverType
	}
	c.checkDeprecatedSignature(signature, node)
	if node.Expression().Kind == ast.KindSuperKeyword {
		return c.voidType
	}
	if ast.IsNewExpression(node) {
		declaration := signature.declaration
		if declaration != nil && !ast.IsConstructorDeclaration(declaration) && !ast.IsConstructSignatureDeclaration(declaration) && !ast.IsConstructorTypeNode(declaration) {
			// When resolved signature is a call signature (and not a construct signature) the result type is any
			if c.noImplicitAny {
				c.error(node, diagnostics.X_new_expression_whose_target_lacks_a_construct_signature_implicitly_has_an_any_type)
			}
			return c.anyType
		}
	}
	if ast.IsInJSFile(node) && c.isCommonJSRequire(node) {
		return c.resolveExternalModuleTypeByLiteral(node.Arguments()[0])
	}
	returnType := c.getReturnTypeOfSignature(signature)
	// Treat any call to the global 'Symbol' function that is part of a const variable or readonly property
	// as a fresh unique symbol literal type.
	if returnType.flags&TypeFlagsESSymbolLike != 0 && c.isSymbolOrSymbolForCall(node) {
		return c.getESSymbolLikeTypeForNode(ast.WalkUpParenthesizedExpressions(node.Parent))
	}
	if ast.IsCallExpression(node) && node.QuestionDotToken() == nil && ast.IsExpressionStatement(node.Parent) && returnType.flags&TypeFlagsVoid != 0 && c.getTypePredicateOfSignature(signature) != nil {
		if !ast.IsDottedName(node.Expression()) {
			c.error(node.Expression(), diagnostics.Assertions_require_the_call_target_to_be_an_identifier_or_qualified_name)
		} else if c.getEffectsSignature(node) == nil {
			diagnostic := c.error(node.Expression(), diagnostics.Assertions_require_every_name_in_the_call_target_to_be_declared_with_an_explicit_type_annotation)
			c.getTypeOfDottedName(node.Expression(), diagnostic)
		}
	}
	return returnType
}

func (c *Checker) checkDeprecatedSignature(sig *Signature, node *ast.Node) {
	if sig.flags&SignatureFlagsIsSignatureCandidateForOverloadFailure != 0 {
		return
	}
	if sig.declaration != nil && c.IsDeprecatedDeclaration(sig.declaration) {
		suggestionNode := c.getDeprecatedSuggestionNode(node)
		name := tryGetPropertyAccessOrIdentifierToString(ast.GetInvokedExpression(node))
		c.addDeprecatedSuggestionWithSignature(suggestionNode, sig.declaration, name, c.signatureToString(sig))
	}
}

func (c *Checker) addDeprecatedSuggestionWithSignature(location *ast.Node, declaration *ast.Node, deprecatedEntity string, signatureString string) *ast.Diagnostic {
	message := core.IfElse(deprecatedEntity != "", diagnostics.The_signature_0_of_1_is_deprecated, diagnostics.X_0_is_deprecated)
	diagnostic := NewDiagnosticForNode(location, message, signatureString, deprecatedEntity)
	return c.addDeprecatedSuggestionWorker([]*ast.Node{declaration}, diagnostic)
}

func (c *Checker) isSymbolOrSymbolForCall(node *ast.Node) bool {
	if !ast.IsCallExpression(node) {
		return false
	}
	left := node.Expression()
	if ast.IsPropertyAccessExpression(left) && left.Name().Text() == "for" {
		left = left.Expression()
	}
	if !ast.IsIdentifier(left) || left.Text() != "Symbol" {
		return false
	}
	// make sure `Symbol` is the global symbol
	globalESSymbol := c.getGlobalESSymbolConstructorSymbolOrNil()
	if globalESSymbol == nil {
		return false
	}
	return globalESSymbol == c.resolveName(left, "Symbol", ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, false /*isUse*/, false)
}

/**
 * Resolve a signature of a given call-like expression.
 * @param node a call-like expression to try resolve a signature for
 * @param candidatesOutArray an array of signature to be filled in by the function. It is passed by signature help in the language service;
 *    the function will fill it up with appropriate candidate signatures
 * @return a signature of the call-like expression or undefined if one can't be found
 */
func (c *Checker) getResolvedSignature(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature {
	links := c.signatureLinks.Get(node)
	// If getResolvedSignature has already been called, we will have cached the resolvedSignature.
	// However, it is possible that either candidatesOutArray was not passed in the first time,
	// or that a different candidatesOutArray was passed in. Therefore, we need to redo the work
	// to correctly fill the candidatesOutArray.
	cached := links.resolvedSignature
	if cached != nil && cached != c.resolvingSignature && candidatesOutArray == nil {
		return cached
	}
	saveResolutionStart := c.resolutionStart
	if cached == nil {
		// If we haven't already done so, temporarily reset the resolution stack. This allows us to
		// handle "inverted" situations where, for example, an API client asks for the type of a symbol
		// containined in a function call argument whose contextual type depends on the symbol itself
		// through resolution of the containing function call. By resetting the resolution stack we'll
		// retry the symbol type resolution with the resolvingSignature marker in place to suppress
		// the contextual type circularity.
		c.resolutionStart = len(c.typeResolutions)
	}
	links.resolvedSignature = c.resolvingSignature
	result := c.resolveSignature(node, candidatesOutArray, checkMode)
	c.resolutionStart = saveResolutionStart
	// When CheckMode.SkipGenericFunctions is set we use resolvingSignature to indicate that call
	// resolution should be deferred.
	if result != c.resolvingSignature {
		// if the signature resolution originated on a node that itself depends on the contextual type
		// then it's possible that the resolved signature might not be the same as the one that would be computed in source order
		// since resolving such signature leads to resolving the potential outer signature, its arguments and thus the very same signature
		// it's possible that this inner resolution sets the resolvedSignature first.
		// In such a case we ignore the local result and reuse the correct one that was cached.
		if links.resolvedSignature != c.resolvingSignature {
			result = links.resolvedSignature
		}
		// If signature resolution originated in control flow type analysis (for example to compute the
		// assigned type in a flow assignment) we don't cache the result as it may be based on temporary
		// types from the control flow analysis.
		if len(c.flowLoopStack) == 0 {
			links.resolvedSignature = result
		} else {
			links.resolvedSignature = cached
		}
	}
	return result
}

func (c *Checker) resolveSignature(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature {
	switch node.Kind {
	case ast.KindCallExpression:
		return c.resolveCallExpression(node, candidatesOutArray, checkMode)
	case ast.KindNewExpression:
		return c.resolveNewExpression(node, candidatesOutArray, checkMode)
	case ast.KindTaggedTemplateExpression:
		return c.resolveTaggedTemplateExpression(node, candidatesOutArray, checkMode)
	case ast.KindDecorator:
		return c.resolveDecorator(node, candidatesOutArray, checkMode)
	case ast.KindJsxOpeningFragment, ast.KindJsxOpeningElement, ast.KindJsxSelfClosingElement:
		return c.resolveJsxOpeningLikeElement(node, candidatesOutArray, checkMode)
	case ast.KindBinaryExpression:
		return c.resolveInstanceofExpression(node, candidatesOutArray, checkMode)
	}
	panic("Unhandled case in resolveSignature")
}

func (c *Checker) resolveCallExpression(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature {
	if node.Expression().Kind == ast.KindSuperKeyword {
		superType := c.checkSuperExpression(node.Expression())
		if IsTypeAny(superType) {
			for _, arg := range node.Arguments() {
				// Still visit arguments so they get marked for visibility, etc
				c.checkExpression(arg)
			}
			return c.anySignature
		}
		if !c.isErrorType(superType) {
			// In super call, the candidate signatures are the matching arity signatures of the base constructor function instantiated
			// with the type arguments specified in the extends clause.
			baseTypeNode := ast.GetExtendsHeritageClauseElement(ast.GetContainingClass(node))
			if baseTypeNode != nil {
				baseConstructors := c.getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.TypeArguments(), baseTypeNode)
				return c.resolveCall(node, baseConstructors, candidatesOutArray, checkMode, SignatureFlagsNone, nil)
			}
		}
		return c.resolveUntypedCall(node)
	}
	if ast.IsImportCall(node) {
		return c.resolveUntypedCall(node)
	}
	var callChainFlags SignatureFlags
	funcType := c.checkExpression(node.Expression())
	if isCallChain(node) {
		nonOptionalType := c.getOptionalExpressionType(funcType, node.Expression())
		switch {
		case nonOptionalType == funcType:
			callChainFlags = SignatureFlagsNone
		case ast.IsOutermostOptionalChain(node):
			callChainFlags = SignatureFlagsIsOuterCallChain
		default:
			callChainFlags = SignatureFlagsIsInnerCallChain
		}
		funcType = nonOptionalType
	} else {
		callChainFlags = SignatureFlagsNone
	}
	funcType = c.checkNonNullTypeWithReporter(funcType, node.Expression(), (*Checker).reportCannotInvokePossiblyNullOrUndefinedError)
	if funcType == c.silentNeverType {
		return c.silentNeverSignature
	}
	apparentType := c.getApparentType(funcType)
	if c.isErrorType(apparentType) {
		// Another error has already been reported
		return c.resolveErrorCall(node)
	}
	// Technically, this signatures list may be incomplete. We are taking the apparent type,
	// but we are not including call signatures that may have been added to the Object or
	// Function interface, since they have none by default. This is a bit of a leap of faith
	// that the user will not add any.
	callSignatures := c.getSignaturesOfType(apparentType, SignatureKindCall)
	numConstructSignatures := len(c.getSignaturesOfType(apparentType, SignatureKindConstruct))
	// TS 1.0 Spec: 4.12
	// In an untyped function call no TypeArgs are permitted, Args can be any argument list, no contextual
	// types are provided for the argument expressions, and the result is always of type Any.
	if c.isUntypedFunctionCall(funcType, apparentType, len(callSignatures), numConstructSignatures) {
		// The unknownType indicates that an error already occurred (and was reported).  No
		// need to report another error in this case.
		if !c.isErrorType(funcType) && node.TypeArguments() != nil {
			c.error(node, diagnostics.Untyped_function_calls_may_not_accept_type_arguments)
		}
		return c.resolveUntypedCall(node)
	}
	// If FuncExpr's apparent type(section 3.8.1) is a function type, the call is a typed function call.
	// TypeScript employs overload resolution in typed function calls in order to support functions
	// with multiple call signatures.
	if len(callSignatures) == 0 {
		if numConstructSignatures != 0 {
			c.error(node, diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, c.TypeToString(funcType))
		} else {
			var relatedInformation *ast.Diagnostic
			if len(node.Arguments()) == 1 {
				text := ast.GetSourceFileOfNode(node).Text()
				options := scanner.SkipTriviaOptions{StopAfterLineBreak: true}
				if stringutil.IsLineBreak(rune(text[scanner.SkipTriviaEx(text, node.Expression().End(), &options)-1])) {
					relatedInformation = createDiagnosticForNode(node.Expression(), diagnostics.Are_you_missing_a_semicolon)
				}
			}
			c.invocationError(node.Expression(), apparentType, SignatureKindCall, relatedInformation)
		}
		return c.resolveErrorCall(node)
	}
	// When a call to a generic function is an argument to an outer call to a generic function for which
	// inference is in process, we have a choice to make. If the inner call relies on inferences made from
	// its contextual type to its return type, deferring the inner call processing allows the best possible
	// contextual type to accumulate. But if the outer call relies on inferences made from the return type of
	// the inner call, the inner call should be processed early. There's no sure way to know which choice is
	// right (only a full unification algorithm can determine that), so we resort to the following heuristic:
	// If no type arguments are specified in the inner call and at least one call signature is generic and
	// returns a function type, we choose to defer processing. This narrowly permits function composition
	// operators to flow inferences through return types, but otherwise processes calls right away. We
	// use the resolvingSignature singleton to indicate that we deferred processing. This result will be
	// propagated out and eventually turned into silentNeverType (a type that is assignable to anything and
	// from which we never make inferences).
	if checkMode&CheckModeSkipGenericFunctions != 0 && len(node.TypeArguments()) == 0 && core.Some(callSignatures, c.isGenericFunctionReturningFunction) {
		c.skippedGenericFunction(node, checkMode)
		return c.resolvingSignature
	}
	return c.resolveCall(node, callSignatures, candidatesOutArray, checkMode, callChainFlags, nil)
}

func (c *Checker) resolveNewExpression(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature {
	expressionType := c.checkNonNullExpression(node.Expression())
	if expressionType == c.silentNeverType {
		return c.silentNeverSignature
	}
	// If expressionType's apparent type(section 3.8.1) is an object type with one or
	// more construct signatures, the expression is processed in the same manner as a
	// function call, but using the construct signatures as the initial set of candidate
	// signatures for overload resolution. The result type of the function call becomes
	// the result type of the operation.
	expressionType = c.getApparentType(expressionType)
	if c.isErrorType(expressionType) {
		// Another error has already been reported
		return c.resolveErrorCall(node)
	}
	// TS 1.0 spec: 4.11
	// If expressionType is of type Any, Args can be any argument
	// list and the result of the operation is of type Any.
	if IsTypeAny(expressionType) {
		if len(node.TypeArguments()) != 0 {
			c.error(node, diagnostics.Untyped_function_calls_may_not_accept_type_arguments)
		}
		return c.resolveUntypedCall(node)
	}
	// Technically, this signatures list may be incomplete. We are taking the apparent type,
	// but we are not including construct signatures that may have been added to the Object or
	// Function interface, since they have none by default. This is a bit of a leap of faith
	// that the user will not add any.
	constructSignatures := c.getSignaturesOfType(expressionType, SignatureKindConstruct)
	if len(constructSignatures) != 0 {
		if !c.isConstructorAccessible(node, constructSignatures[0]) {
			return c.resolveErrorCall(node)
		}
		// If the expression is a class of abstract type, or an abstract construct signature,
		// then it cannot be instantiated.
		// In the case of a merged class-module or class-interface declaration,
		// only the class declaration node will have the Abstract flag set.
		if someSignature(constructSignatures, func(sig *Signature) bool {
			return sig.flags&SignatureFlagsAbstract != 0
		}) {
			c.error(node, diagnostics.Cannot_create_an_instance_of_an_abstract_class)
			return c.resolveErrorCall(node)
		}
		if expressionType.symbol != nil {
			valueDecl := ast.GetClassLikeDeclarationOfSymbol(expressionType.symbol)
			if valueDecl != nil && ast.HasModifier(valueDecl, ast.ModifierFlagsAbstract) {
				c.error(node, diagnostics.Cannot_create_an_instance_of_an_abstract_class)
				return c.resolveErrorCall(node)
			}
		}
		return c.resolveCall(node, constructSignatures, candidatesOutArray, checkMode, SignatureFlagsNone, nil)
	}
	// If expressionType's apparent type is an object type with no construct signatures but
	// one or more call signatures, the expression is processed as a function call. A compile-time
	// error occurs if the result of the function call is not Void. The type of the result of the
	// operation is Any. It is an error to have a Void this type.
	callSignatures := c.getSignaturesOfType(expressionType, SignatureKindCall)
	if len(callSignatures) != 0 {
		signature := c.resolveCall(node, callSignatures, candidatesOutArray, checkMode, SignatureFlagsNone, nil)
		if !c.noImplicitAny {
			if signature.declaration != nil && c.getReturnTypeOfSignature(signature) != c.voidType {
				c.error(node, diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword)
			}
			if c.getThisTypeOfSignature(signature) == c.voidType {
				c.error(node, diagnostics.A_function_that_is_called_with_the_new_keyword_cannot_have_a_this_type_that_is_void)
			}
		}
		return signature
	}
	c.invocationError(node.Expression(), expressionType, SignatureKindConstruct, nil)
	return c.resolveErrorCall(node)
}

func (c *Checker) isConstructorAccessible(node *ast.Node, signature *Signature) bool {
	if signature == nil || signature.declaration == nil {
		return true
	}
	declaration := signature.declaration
	modifiers := getSelectedModifierFlags(declaration, ast.ModifierFlagsNonPublicAccessibilityModifier)
	// (1) Public constructors and (2) constructor functions are always accessible.
	if modifiers == 0 || !ast.IsConstructorDeclaration(declaration) {
		return true
	}
	declaringClassDeclaration := ast.GetClassLikeDeclarationOfSymbol(declaration.Parent.Symbol())
	declaringClass := c.getDeclaredTypeOfSymbol(declaration.Parent.Symbol())
	// A private or protected constructor can only be instantiated within its own class (or a subclass, for protected)
	if !c.isNodeWithinClass(node, declaringClassDeclaration) {
		containingClass := ast.GetContainingClass(node)
		if containingClass != nil && modifiers&ast.ModifierFlagsProtected != 0 {
			containingType := c.getDeclaredTypeOfSymbol(containingClass.Symbol())
			if c.typeHasProtectedAccessibleBase(declaration.Parent.Symbol(), containingType) {
				return true
			}
		}
		if modifiers&ast.ModifierFlagsPrivate != 0 {
			c.error(node, diagnostics.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration, c.TypeToString(declaringClass))
		}
		if modifiers&ast.ModifierFlagsProtected != 0 {
			c.error(node, diagnostics.Constructor_of_class_0_is_protected_and_only_accessible_within_the_class_declaration, c.TypeToString(declaringClass))
		}
		return false
	}
	return true
}

func (c *Checker) typeHasProtectedAccessibleBase(target *ast.Symbol, t *Type) bool {
	baseTypes := c.getBaseTypes(c.getTargetType(t))
	if len(baseTypes) == 0 {
		return false
	}
	firstBase := baseTypes[0]
	if firstBase.flags&TypeFlagsIntersection != 0 {
		types := firstBase.AsIntersectionType().types
		mixinFlags, _ := c.findMixins(types)
		for i, intersectionMember := range firstBase.Types() {
			// We want to ignore mixin ctors
			if !mixinFlags[i] {
				if intersectionMember.objectFlags&(ObjectFlagsClass|ObjectFlagsInterface) != 0 {
					if intersectionMember.symbol == target {
						return true
					}
					if c.typeHasProtectedAccessibleBase(target, intersectionMember) {
						return true
					}
				}
			}
		}
		return false
	}
	if firstBase.symbol == target {
		return true
	}
	return c.typeHasProtectedAccessibleBase(target, firstBase)
}

func someSignature(signatures []*Signature, f func(s *Signature) bool) bool {
	for _, sig := range signatures {
		if sig.composite != nil && sig.composite.isUnion && core.Some(sig.composite.signatures, f) || sig.composite == nil && f(sig) {
			return true
		}
	}
	return false
}

func (c *Checker) resolveTaggedTemplateExpression(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature {
	tag := node.AsTaggedTemplateExpression().Tag
	tagType := c.checkExpression(tag)
	apparentType := c.getApparentType(tagType)
	if c.isErrorType(apparentType) {
		// Another error has already been reported
		return c.resolveErrorCall(node)
	}
	callSignatures := c.getSignaturesOfType(apparentType, SignatureKindCall)
	numConstructSignatures := len(c.getSignaturesOfType(apparentType, SignatureKindConstruct))
	if c.isUntypedFunctionCall(tagType, apparentType, len(callSignatures), numConstructSignatures) {
		return c.resolveUntypedCall(node)
	}
	if len(callSignatures) == 0 {
		if ast.IsArrayLiteralExpression(node.Parent) {
			c.error(tag, diagnostics.It_is_likely_that_you_are_missing_a_comma_to_separate_these_two_template_expressions_They_form_a_tagged_template_expression_which_cannot_be_invoked)
			return c.resolveErrorCall(node)
		}
		c.invocationError(tag, apparentType, SignatureKindCall, nil)
		return c.resolveErrorCall(node)
	}
	return c.resolveCall(node, callSignatures, candidatesOutArray, checkMode, SignatureFlagsNone, nil)
}

func (c *Checker) resolveDecorator(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature {
	if !ast.CanHaveDecorators(node.Parent) {
		return c.resolveErrorCall(node)
	}
	funcType := c.checkExpression(node.Expression())
	apparentType := c.getApparentType(funcType)
	if c.isErrorType(apparentType) {
		return c.resolveErrorCall(node)
	}
	callSignatures := c.getSignaturesOfType(apparentType, SignatureKindCall)
	numConstructSignatures := len(c.getSignaturesOfType(apparentType, SignatureKindConstruct))
	if c.isUntypedFunctionCall(funcType, apparentType, len(callSignatures), numConstructSignatures) {
		return c.resolveUntypedCall(node)
	}
	if c.isPotentiallyUncalledDecorator(node, callSignatures) && !ast.IsParenthesizedExpression(node.Expression()) {
		nodeStr := scanner.GetTextOfNode(node.Expression())
		c.error(node, diagnostics.X_0_accepts_too_few_arguments_to_be_used_as_a_decorator_here_Did_you_mean_to_call_it_first_and_write_0, nodeStr)
		return c.resolveErrorCall(node)
	}
	headMessage := c.getDiagnosticHeadMessageForDecoratorResolution(node)
	if len(callSignatures) == 0 {
		diag := ast.NewDiagnosticChain(c.invocationErrorDetails(node.Expression(), apparentType, SignatureKindCall), headMessage)
		c.addDiagnostic(diag)
		c.invocationErrorRecovery(apparentType, SignatureKindCall, diag)
		return c.resolveErrorCall(node)
	}
	decoratorSignature := c.getDecoratorCallSignature(node)
	if decoratorSignature == nil {
		return c.resolveErrorCall(node)
	}
	return c.resolveCall(node, callSignatures, candidatesOutArray, checkMode, SignatureFlagsNone, headMessage)
}

// Sometimes, we have a decorator that could accept zero arguments,
// but is receiving too many arguments as part of the decorator invocation.
// In those cases, a user may have meant to *call* the expression before using it as a decorator.
func (c *Checker) isPotentiallyUncalledDecorator(decorator *ast.Node, signatures []*Signature) bool {
	return len(signatures) != 0 && core.Every(signatures, func(sig *Signature) bool {
		return sig.minArgumentCount == 0 && !signatureHasRestParameter(sig) && len(sig.parameters) < c.getDecoratorArgumentCount(decorator, sig)
	})
}

// Gets the localized diagnostic head message to use for errors when resolving a decorator as a call expression.
func (c *Checker) getDiagnosticHeadMessageForDecoratorResolution(node *ast.Node) *diagnostics.Message {
	switch node.Parent.Kind {
	case ast.KindClassDeclaration, ast.KindClassExpression:
		return diagnostics.Unable_to_resolve_signature_of_class_decorator_when_called_as_an_expression
	case ast.KindParameter:
		return diagnostics.Unable_to_resolve_signature_of_parameter_decorator_when_called_as_an_expression
	case ast.KindPropertyDeclaration:
		return diagnostics.Unable_to_resolve_signature_of_property_decorator_when_called_as_an_expression
	case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
		return diagnostics.Unable_to_resolve_signature_of_method_decorator_when_called_as_an_expression
	}
	panic("Unhandled case in getDiagnosticHeadMessageForDecoratorResolution")
}

func (c *Checker) resolveInstanceofExpression(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature {
	// if rightType is an object type with a custom `[Symbol.hasInstance]` method, then it is potentially
	// valid on the right-hand side of the `instanceof` operator. This allows normal `object` types to
	// participate in `instanceof`, as per Step 2 of https://tc39.es/ecma262/#sec-instanceofoperator.
	right := node.AsBinaryExpression().Right
	rightType := c.checkExpression(right)
	if !IsTypeAny(rightType) {
		hasInstanceMethodType := c.getSymbolHasInstanceMethodOfObjectType(rightType)
		if hasInstanceMethodType != nil {
			apparentType := c.getApparentType(hasInstanceMethodType)
			if c.isErrorType(apparentType) {
				return c.resolveErrorCall(node)
			}
			callSignatures := c.getSignaturesOfType(apparentType, SignatureKindCall)
			constructSignatures := c.getSignaturesOfType(apparentType, SignatureKindConstruct)
			if c.isUntypedFunctionCall(hasInstanceMethodType, apparentType, len(callSignatures), len(constructSignatures)) {
				return c.resolveUntypedCall(node)
			}
			if len(callSignatures) != 0 {
				return c.resolveCall(node, callSignatures, candidatesOutArray, checkMode, SignatureFlagsNone, nil)
			}
		} else if !(c.typeHasCallOrConstructSignatures(rightType) || c.isTypeSubtypeOf(rightType, c.globalFunctionType)) {
			c.error(right, diagnostics.The_right_hand_side_of_an_instanceof_expression_must_be_either_of_type_any_a_class_function_or_other_type_assignable_to_the_Function_interface_type_or_an_object_type_with_a_Symbol_hasInstance_method)
			return c.resolveErrorCall(node)
		}
	}
	// fall back to a default signature
	return c.anySignature
}

type CallState struct {
	node                           *ast.Node
	typeArguments                  []*ast.Node
	args                           []*ast.Node
	candidates                     []*Signature
	argCheckMode                   CheckMode
	isSingleNonGenericCandidate    bool
	signatureHelpTrailingComma     bool
	candidatesForArgumentError     []*Signature
	candidateForArgumentArityError *Signature
	candidateForTypeArgumentError  *Signature
}

func (c *Checker) resolveCall(node *ast.Node, signatures []*Signature, candidatesOutArray *[]*Signature, checkMode CheckMode, callChainFlags SignatureFlags, headMessage *diagnostics.Message) *Signature {
	isTaggedTemplate := node.Kind == ast.KindTaggedTemplateExpression
	isDecorator := node.Kind == ast.KindDecorator
	isJsxOpeningOrSelfClosingElement := ast.IsJsxOpeningLikeElement(node)
	isInstanceof := node.Kind == ast.KindBinaryExpression
	reportErrors := !c.isInferencePartiallyBlocked && candidatesOutArray == nil
	var s CallState
	s.node = node
	if !isDecorator && !isInstanceof && !isSuperCall(node) && !ast.IsJsxOpeningFragment(node) {
		s.typeArguments = node.TypeArguments()
		// We already perform checking on the type arguments on the class declaration itself.
		if isTaggedTemplate || isJsxOpeningOrSelfClosingElement || node.Expression().Kind != ast.KindSuperKeyword {
			c.checkSourceElements(s.typeArguments)
		}
	}
	s.candidates = c.reorderCandidates(signatures, callChainFlags)
	if candidatesOutArray != nil {
		*candidatesOutArray = s.candidates
	}

	if len(s.candidates) == 0 {
		// In Strada we would error here, but no known repro doesn't have at least
		// one other error in this codepath. Just return instead. See #54442
		return c.unknownSignature
	}

	s.args = c.getEffectiveCallArguments(node)
	// The excludeArgument array contains true for each context sensitive argument (an argument
	// is context sensitive it is susceptible to a one-time permanent contextual typing).
	//
	// The idea is that we will perform type argument inference & assignability checking once
	// without using the susceptible parameters that are functions, and once more for those
	// parameters, contextually typing each as we go along.
	//
	// For a tagged template, then the first argument be 'undefined' if necessary because it
	// represents a TemplateStringsArray.
	//
	// For a decorator, no arguments are susceptible to contextual typing due to the fact
	// decorators are applied to a declaration by the emitter, and not to an expression.
	s.isSingleNonGenericCandidate = len(s.candidates) == 1 && len(s.candidates[0].typeParameters) == 0
	if !isDecorator && !s.isSingleNonGenericCandidate && core.Some(s.args, c.isContextSensitive) {
		s.argCheckMode = CheckModeSkipContextSensitive
	} else {
		s.argCheckMode = CheckModeNormal
	}
	// The following variables are captured and modified by calls to chooseOverload.
	// If overload resolution or type argument inference fails, we want to report the
	// best error possible. The best error is one which says that an argument was not
	// assignable to a parameter. This implies that everything else about the overload
	// was fine. So if there is any overload that is only incorrect because of an
	// argument, we will report an error on that one.
	//
	//     function foo(s: string): void;
	//     function foo(n: number): void; // Report argument error on this overload
	//     function foo(): void;
	//     foo(true);
	//
	// If none of the overloads even made it that far, there are two possibilities.
	// There was a problem with type arguments for some overload, in which case
	// report an error on that. Or none of the overloads even had correct arity,
	// in which case give an arity error.
	//
	//     function foo<T extends string>(x: T): void; // Report type argument error
	//     function foo(): void;
	//     foo<number>(0);
	//
	// If we are in signature help, a trailing comma indicates that we intend to provide another argument,
	// so we will only accept overloads with arity at least 1 higher than the current number of provided arguments.
	s.signatureHelpTrailingComma = checkMode&CheckModeIsForSignatureHelp != 0 && ast.IsCallExpression(node) && node.ArgumentList().HasTrailingComma()
	// Section 4.12.1:
	// if the candidate list contains one or more signatures for which the type of each argument
	// expression is a subtype of each corresponding parameter type, the return type of the first
	// of those signatures becomes the return type of the function call.
	// Otherwise, the return type of the first signature in the candidate list becomes the return
	// type of the function call.
	//
	// Whether the call is an error is determined by assignability of the arguments. The subtype pass
	// is just important for choosing the best signature. So in the case where there is only one
	// signature, the subtype pass is useless. So skipping it is an optimization.
	var result *Signature
	if len(s.candidates) > 1 {
		result = c.chooseOverload(&s, c.subtypeRelation)
	}
	if result == nil {
		result = c.chooseOverload(&s, c.assignableRelation)
	}
	if result != nil {
		return result
	}
	result = c.getCandidateForOverloadFailure(s.node, s.candidates, s.args, candidatesOutArray != nil, checkMode)
	// Preemptively cache the result; getResolvedSignature will do this after we return, but
	// we need to ensure that the result is present for the error checks below so that if
	// this signature is encountered again, we handle the circularity (rather than producing a
	// different result which may produce no errors and assert). Callers of getResolvedSignature
	// don't hit this issue because they only observe this result after it's had a chance to
	// be cached, but the error reporting code below executes before getResolvedSignature sets
	// resolvedSignature.
	c.signatureLinks.Get(node).resolvedSignature = result
	// No signatures were applicable. Now report errors based on the last applicable signature with
	// no arguments excluded from assignability checks.
	// If candidate is undefined, it means that no candidates had a suitable arity. In that case,
	// skip the checkApplicableSignature check.
	if reportErrors {
		// If the call expression is a synthetic call to a `[Symbol.hasInstance]` method then we will produce a head
		// message when reporting diagnostics that explains how we got to `right[Symbol.hasInstance](left)` from
		// `left instanceof right`, as it pertains to "Argument" related messages reported for the call.
		if headMessage == nil && isInstanceof {
			headMessage = diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_assignable_to_the_first_argument_of_the_right_hand_side_s_Symbol_hasInstance_method
		}
		c.reportCallResolutionErrors(node, &s, signatures, headMessage)
	}
	return result
}

func (c *Checker) reorderCandidates(signatures []*Signature, callChainFlags SignatureFlags) []*Signature {
	var lastParent *ast.Node
	var lastSymbol *ast.Symbol
	var index int
	var cutoffIndex int
	var spliceIndex int
	specializedIndex := -1
	result := make([]*Signature, 0, len(signatures))
	for _, signature := range signatures {
		var symbol *ast.Symbol
		var parent *ast.Node
		if signature.declaration != nil {
			symbol = c.getSymbolOfDeclaration(signature.declaration)
			parent = signature.declaration.Parent
		}
		if lastSymbol == nil || symbol == lastSymbol {
			if lastParent != nil && parent == lastParent {
				index = index + 1
			} else {
				lastParent = parent
				index = cutoffIndex
			}
		} else {
			// current declaration belongs to a different symbol
			// set cutoffIndex so re-orderings in the future won't change result set from 0 to cutoffIndex
			index = len(result)
			cutoffIndex = len(result)
			lastParent = parent
		}
		lastSymbol = symbol
		// specialized signatures always need to be placed before non-specialized signatures regardless
		// of the cutoff position; see GH#1133
		if signatureHasLiteralTypes(signature) {
			specializedIndex++
			spliceIndex = specializedIndex
			// The cutoff index always needs to be greater than or equal to the specialized signature index
			// in order to prevent non-specialized signatures from being added before a specialized
			// signature.
			cutoffIndex++
		} else {
			spliceIndex = index
		}
		if callChainFlags != 0 {
			signature = c.getOptionalCallSignature(signature, callChainFlags)
		}
		result = slices.Insert(result, spliceIndex, signature)
	}
	return result
}

func signatureHasLiteralTypes(s *Signature) bool {
	return s.flags&SignatureFlagsHasLiteralTypes != 0
}

func (c *Checker) getOptionalCallSignature(signature *Signature, callChainFlags SignatureFlags) *Signature {
	if signature.flags&SignatureFlagsCallChainFlags == callChainFlags {
		return signature
	}
	key := CachedSignatureKey{sig: signature, key: core.IfElse(callChainFlags == SignatureFlagsIsInnerCallChain, SignatureKeyInner, SignatureKeyOuter)}
	if cached := c.cachedSignatures[key]; cached != nil {
		return cached
	}
	result := c.cloneSignature(signature)
	result.flags |= callChainFlags
	c.cachedSignatures[key] = result
	return result
}

func (c *Checker) chooseOverload(s *CallState, relation *Relation) *Signature {
	s.candidatesForArgumentError = nil
	s.candidateForArgumentArityError = nil
	s.candidateForTypeArgumentError = nil
	if s.isSingleNonGenericCandidate {
		candidate := s.candidates[0]
		if len(s.typeArguments) != 0 || !c.hasCorrectArity(s.node, s.args, candidate, s.signatureHelpTrailingComma) {
			return nil
		}
		if !c.isSignatureApplicable(s.node, s.args, candidate, relation, CheckModeNormal, false /*reportErrors*/, nil /*diagnosticOutput*/) {
			s.candidatesForArgumentError = []*Signature{candidate}
			return nil
		}
		return candidate
	}
	for candidateIndex, candidate := range s.candidates {
		if !c.hasCorrectTypeArgumentArity(candidate, s.typeArguments) || !c.hasCorrectArity(s.node, s.args, candidate, s.signatureHelpTrailingComma) {
			continue
		}
		var checkCandidate *Signature
		var inferenceContext *InferenceContext
		if len(candidate.typeParameters) != 0 {
			var typeArgumentTypes []*Type
			if len(s.typeArguments) != 0 {
				typeArgumentTypes = c.checkTypeArguments(candidate, s.typeArguments, false /*reportErrors*/, nil)
				if typeArgumentTypes == nil {
					s.candidateForTypeArgumentError = candidate
					continue
				}
			} else {
				inferenceContext = c.newInferenceContext(candidate.typeParameters, candidate, core.IfElse(ast.IsInJSFile(s.node), InferenceFlagsAnyDefault, InferenceFlagsNone) /*flags*/, nil)
				typeArgumentTypes = c.inferTypeArguments(s.node, candidate, s.args, s.argCheckMode|CheckModeSkipGenericFunctions, inferenceContext)
				if inferenceContext.flags&InferenceFlagsSkippedGenericFunction != 0 {
					s.argCheckMode |= CheckModeSkipGenericFunctions
				}
			}
			var inferredTypeParameters []*Type
			if inferenceContext != nil {
				inferredTypeParameters = inferenceContext.inferredTypeParameters
			}
			checkCandidate = c.getSignatureInstantiation(candidate, typeArgumentTypes, ast.IsInJSFile(candidate.declaration), inferredTypeParameters)
			// If the original signature has a generic rest type, instantiation may produce a
			// signature with different arity and we need to perform another arity check.
			if c.getNonArrayRestType(candidate) != nil && !c.hasCorrectArity(s.node, s.args, checkCandidate, s.signatureHelpTrailingComma) {
				s.candidateForArgumentArityError = checkCandidate
				continue
			}
		} else {
			checkCandidate = candidate
		}
		if !c.isSignatureApplicable(s.node, s.args, checkCandidate, relation, s.argCheckMode, false /*reportErrors*/, nil /*diagnosticOutput*/) {
			// Give preference to error candidates that have no rest parameters (as they are more specific)
			s.candidatesForArgumentError = append(s.candidatesForArgumentError, checkCandidate)
			continue
		}
		if s.argCheckMode != 0 {
			// If one or more context sensitive arguments were excluded, we start including
			// them now (and keeping do so for any subsequent candidates) and perform a second
			// round of type inference and applicability checking for this particular candidate.
			s.argCheckMode = CheckModeNormal
			if inferenceContext != nil {
				typeArgumentTypes := c.inferTypeArguments(s.node, candidate, s.args, s.argCheckMode, inferenceContext)
				checkCandidate = c.getSignatureInstantiation(candidate, typeArgumentTypes, ast.IsInJSFile(candidate.declaration), inferenceContext.inferredTypeParameters)
				// If the original signature has a generic rest type, instantiation may produce a
				// signature with different arity and we need to perform another arity check.
				if c.getNonArrayRestType(candidate) != nil && !c.hasCorrectArity(s.node, s.args, checkCandidate, s.signatureHelpTrailingComma) {
					s.candidateForArgumentArityError = checkCandidate
					continue
				}
			}
			if !c.isSignatureApplicable(s.node, s.args, checkCandidate, relation, s.argCheckMode, false /*reportErrors*/, nil /*diagnosticOutput*/) {
				// Give preference to error candidates that have no rest parameters (as they are more specific)
				s.candidatesForArgumentError = append(s.candidatesForArgumentError, checkCandidate)
				continue
			}
		}
		s.candidates[candidateIndex] = checkCandidate
		return checkCandidate
	}
	return nil
}

func (c *Checker) hasCorrectArity(node *ast.Node, args []*ast.Node, signature *Signature, signatureHelpTrailingComma bool) bool {
	if ast.IsJsxOpeningFragment(node) {
		return true
	}
	var argCount int
	callIsIncomplete := false
	// In incomplete call we want to be lenient when we have too few arguments
	effectiveParameterCount := c.getParameterCount(signature)
	effectiveMinimumArguments := c.getMinArgumentCount(signature)
	switch {
	case ast.IsTaggedTemplateExpression(node):
		argCount = len(args)
		template := node.AsTaggedTemplateExpression().Template
		if ast.IsTemplateExpression(template) {
			// If a tagged template expression lacks a tail literal, the call is incomplete.
			// Specifically, a template only can end in a TemplateTail or a Missing literal.
			lastSpan := core.LastOrNil(template.AsTemplateExpression().TemplateSpans.Nodes)
			// we should always have at least one span.
			callIsIncomplete = ast.NodeIsMissing(lastSpan.AsTemplateSpan().Literal) || ast.IsUnterminatedLiteral(lastSpan.AsTemplateSpan().Literal)
		} else {
			// If the template didn't end in a backtick, or its beginning occurred right prior to EOF,
			// then this might actually turn out to be a TemplateHead in the future;
			// so we consider the call to be incomplete.
			callIsIncomplete = ast.IsUnterminatedLiteral(template)
		}
	case ast.IsDecorator(node):
		argCount = c.getDecoratorArgumentCount(node, signature)
	case ast.IsBinaryExpression(node):
		argCount = 1
	case ast.IsJsxOpeningLikeElement(node):
		callIsIncomplete = node.Attributes().End() == node.End()
		if callIsIncomplete {
			return true
		}
		argCount = core.IfElse(effectiveMinimumArguments == 0, len(args), 1)
		effectiveParameterCount = core.IfElse(len(args) == 0, effectiveParameterCount, 1) // class may have argumentless ctor functions - still resolve ctor and compare vs props member type
		effectiveMinimumArguments = min(effectiveMinimumArguments, 1)                     // sfc may specify context argument - handled by framework and not typechecked
	case ast.IsNewExpression(node) && node.ArgumentList() == nil:
		// This only happens when we have something of the form: 'new C'
		return c.getMinArgumentCount(signature) == 0
	default:
		if signatureHelpTrailingComma {
			argCount = len(args) + 1
		} else {
			argCount = len(args)
		}
		// If we are missing the close parenthesis, the call is incomplete.
		callIsIncomplete = node.ArgumentList().End() == node.End()
		// If a spread argument is present, check that it corresponds to a rest parameter or at least that it's in the valid range.
		spreadArgIndex := c.getSpreadArgumentIndex(args)
		if spreadArgIndex >= 0 {
			return spreadArgIndex >= c.getMinArgumentCount(signature) && (c.hasEffectiveRestParameter(signature) || spreadArgIndex < c.getParameterCount(signature))
		}
	}
	// Too many arguments implies incorrect arity.
	if !c.hasEffectiveRestParameter(signature) && argCount > effectiveParameterCount {
		return false
	}
	// If the call is incomplete, we should skip the lower bound check.
	// JSX signatures can have extra parameters provided by the library which we don't check
	if callIsIncomplete || argCount >= effectiveMinimumArguments {
		return true
	}
	for i := argCount; i < effectiveMinimumArguments; i++ {
		t := c.getTypeAtPosition(signature, i)
		if c.filterType(t, acceptsVoid).flags&TypeFlagsNever != 0 {
			return false
		}
	}
	return true
}

func acceptsVoid(t *Type) bool {
	return t.flags&TypeFlagsVoid != 0
}

func (c *Checker) getDecoratorArgumentCount(node *ast.Node, signature *Signature) int {
	if c.compilerOptions.ExperimentalDecorators.IsTrue() {
		return c.getLegacyDecoratorArgumentCount(node, signature)
	}
	return min(max(c.getParameterCount(signature), 1), 2)
}

/**
 * Returns the argument count for a decorator node that works like a function invocation.
 */
func (c *Checker) getLegacyDecoratorArgumentCount(node *ast.Node, signature *Signature) int {
	switch node.Parent.Kind {
	case ast.KindClassDeclaration, ast.KindClassExpression:
		return 1
	case ast.KindPropertyDeclaration:
		if ast.HasAccessorModifier(node.Parent) {
			return 3
		}
		return 2
	case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
		// For decorators with only two parameters we supply only two arguments
		if len(signature.parameters) <= 2 {
			return 2
		}
		return 3
	case ast.KindParameter:
		return 3
	}
	panic("Unhandled case in getLegacyDecoratorArgumentCount")
}

func (c *Checker) hasCorrectTypeArgumentArity(signature *Signature, typeArguments []*ast.Node) bool {
	// If the user supplied type arguments, but the number of type arguments does not match
	// the declared number of type parameters, the call has an incorrect arity.
	numTypeParameters := len(signature.typeParameters)
	minTypeArgumentCount := c.getMinTypeArgumentCount(signature.typeParameters)
	return len(typeArguments) == 0 || len(typeArguments) >= minTypeArgumentCount && len(typeArguments) <= numTypeParameters
}

func (c *Checker) checkTypeArguments(signature *Signature, typeArgumentNodes []*ast.Node, reportErrors bool, headMessage *diagnostics.Message) []*Type {
	isJavaScript := ast.IsInJSFile(signature.declaration)
	typeParameters := signature.typeParameters
	typeArgumentTypes := c.fillMissingTypeArguments(core.Map(typeArgumentNodes, c.getTypeFromTypeNode), typeParameters, c.getMinTypeArgumentCount(typeParameters), isJavaScript)
	var mapper *TypeMapper
	for i := range typeArgumentNodes {
		debug.Assert(typeParameters[i] != nil, "Should not call checkTypeArguments with too many type arguments")
		constraint := c.getConstraintOfTypeParameter(typeParameters[i])
		if constraint != nil {
			typeArgumentHeadMessage := core.OrElse(headMessage, diagnostics.Type_0_does_not_satisfy_the_constraint_1)
			if mapper == nil {
				mapper = newTypeMapper(typeParameters, typeArgumentTypes)
			}
			typeArgument := typeArgumentTypes[i]
			var errorNode *ast.Node
			if reportErrors {
				errorNode = typeArgumentNodes[i]
			}
			var diags []*ast.Diagnostic
			if !c.checkTypeAssignableToEx(typeArgument, c.getTypeWithThisArgument(c.instantiateType(constraint, mapper), typeArgument, false), errorNode, typeArgumentHeadMessage, &diags) {
				if len(diags) != 0 {
					diagnostic := diags[0]
					if headMessage != nil {
						diagnostic = ast.NewDiagnosticChain(diagnostic, diagnostics.Type_0_does_not_satisfy_the_constraint_1)
					}
					c.addDiagnostic(diagnostic)
				}
				return nil
			}
		}
	}
	return typeArgumentTypes
}

func (c *Checker) isSignatureApplicable(node *ast.Node, args []*ast.Node, signature *Signature, relation *Relation, checkMode CheckMode, reportErrors bool, diagnosticOutput *[]*ast.Diagnostic) bool {
	if ast.IsJsxCallLike(node) {
		return c.checkApplicableSignatureForJsxCallLikeElement(node, signature, relation, checkMode, reportErrors, diagnosticOutput)
	}
	thisType := c.getThisTypeOfSignature(signature)
	if thisType != nil && thisType != c.voidType && !(ast.IsNewExpression(node) || ast.IsCallExpression(node) && ast.IsSuperProperty(node.Expression())) {
		// If the called expression is not of the form `x.f` or `x["f"]`, then sourceType = voidType
		// If the signature's 'this' type is voidType, then the check is skipped -- anything is compatible.
		// If the expression is a new expression or super call expression, then the check is skipped.
		thisArgumentNode := c.getThisArgumentOfCall(node)
		thisArgumentType := c.getThisArgumentType(thisArgumentNode)
		var errorNode *ast.Node
		if reportErrors {
			errorNode = thisArgumentNode
			if errorNode == nil {
				errorNode = node
			}
		}
		headMessage := diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1
		if !c.checkTypeRelatedToEx(thisArgumentType, thisType, relation, errorNode, headMessage, diagnosticOutput) {
			return false
		}
	}
	headMessage := diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1
	restType := c.getNonArrayRestType(signature)
	var argCount int
	if restType != nil {
		argCount = min(c.getParameterCount(signature)-1, len(args))
	} else {
		argCount = len(args)
	}
	for i := range argCount {
		arg := args[i]
		if !ast.IsOmittedExpression(arg) {
			paramType := c.getTypeAtPosition(signature, i)
			argType := c.checkExpressionWithContextualType(arg, paramType, nil /*inferenceContext*/, checkMode)
			// If one or more arguments are still excluded (as indicated by CheckMode.SkipContextSensitive),
			// we obtain the regular type of any object literal arguments because we may not have inferred complete
			// parameter types yet and therefore excess property checks may yield false positives (see #17041).
			var checkArgType *Type
			if checkMode&CheckModeSkipContextSensitive != 0 {
				checkArgType = c.getRegularTypeOfObjectLiteral(argType)
			} else {
				checkArgType = argType
			}
			effectiveCheckArgumentNode := c.getEffectiveCheckNode(arg)
			if !c.checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, core.IfElse(reportErrors, effectiveCheckArgumentNode, nil), effectiveCheckArgumentNode, headMessage, diagnosticOutput) {
				c.maybeAddMissingAwaitInfo(arg, checkArgType, paramType, relation, reportErrors, diagnosticOutput)
				return false
			}
		}
	}
	if restType != nil {
		spreadType := c.getSpreadArgumentType(args, argCount, len(args), restType, nil /*context*/, checkMode)
		restArgCount := len(args) - argCount
		var errorNode *ast.Node
		if reportErrors {
			switch restArgCount {
			case 0:
				errorNode = node
			case 1:
				errorNode = c.getEffectiveCheckNode(args[argCount])
			default:
				errorNode = c.createSyntheticExpression(node, spreadType, false, nil)
				errorNode.Loc = core.NewTextRange(args[argCount].Pos(), args[len(args)-1].End())
			}
		}
		if !c.checkTypeRelatedToEx(spreadType, restType, relation, errorNode, headMessage, diagnosticOutput) {
			c.maybeAddMissingAwaitInfo(errorNode, spreadType, restType, relation, reportErrors, diagnosticOutput)
			return false
		}
	}
	return true
}

func (c *Checker) maybeAddMissingAwaitInfo(errorNode *ast.Node, source *Type, target *Type, relation *Relation, reportErrors bool, diagnosticOutput *[]*ast.Diagnostic) {
	if errorNode != nil && reportErrors && diagnosticOutput != nil && len(*diagnosticOutput) != 0 {
		// Bail if target is Promise-like---something else is wrong
		if c.getAwaitedTypeOfPromise(target) != nil {
			return
		}
		awaitedTypeOfSource := c.getAwaitedTypeOfPromise(source)
		if awaitedTypeOfSource != nil && c.isTypeRelatedTo(awaitedTypeOfSource, target, relation) {
			(*diagnosticOutput)[0].AddRelatedInfo(NewDiagnosticForNode(errorNode, diagnostics.Did_you_forget_to_use_await))
		}
	}
}

// Returns the `this` argument node in calls like `x.f(...)` and `x[f](...)`. `nil` otherwise.
func (c *Checker) getThisArgumentOfCall(node *ast.Node) *ast.Node {
	if ast.IsBinaryExpression(node) {
		return node.AsBinaryExpression().Right
	}
	var expression *ast.Node
	switch {
	case ast.IsCallExpression(node):
		expression = node.Expression()
	case ast.IsTaggedTemplateExpression(node):
		expression = node.AsTaggedTemplateExpression().Tag
	case ast.IsDecorator(node) && !c.legacyDecorators:
		expression = node.Expression()
	}
	if expression != nil {
		callee := ast.SkipOuterExpressions(expression, ast.OEKAll)
		if ast.IsAccessExpression(callee) {
			return callee.Expression()
		}
	}
	return nil
}

func (c *Checker) getThisArgumentType(node *ast.Node) *Type {
	if node == nil {
		return c.voidType
	}
	thisArgumentType := c.checkExpression(node)
	switch {
	case ast.IsOptionalChainRoot(node.Parent):
		return c.GetNonNullableType(thisArgumentType)
	case ast.IsOptionalChain(node.Parent):
		return c.removeOptionalTypeMarker(thisArgumentType)
	}
	return thisArgumentType
}

func (c *Checker) getEffectiveCheckNode(argument *ast.Node) *ast.Node {
	flags := core.IfElse(
		ast.IsInJSFile(argument),
		ast.OEKParentheses|ast.OEKSatisfies|ast.OEKExcludeJSDocTypeAssertion,
		ast.OEKParentheses|ast.OEKSatisfies,
	)
	return ast.SkipOuterExpressions(argument, flags)
}

func (c *Checker) inferTypeArguments(node *ast.Node, signature *Signature, args []*ast.Node, checkMode CheckMode, context *InferenceContext) []*Type {
	if ast.IsJsxOpeningLikeElement(node) {
		return c.inferJsxTypeArguments(node, signature, checkMode, context)
	}
	// If a contextual type is available, infer from that type to the return type of the call expression. For
	// example, given a 'function wrap<T, U>(cb: (x: T) => U): (x: T) => U' and a call expression
	// 'let f: (x: string) => number = wrap(s => s.length)', we infer from the declared type of 'f' to the
	// return type of 'wrap'.
	if !ast.IsDecorator(node) && !ast.IsBinaryExpression(node) {
		skipBindingPatterns := core.Every(signature.typeParameters, func(p *Type) bool { return c.getDefaultFromTypeParameter(p) != nil })
		contextualType := c.getContextualType(node, core.IfElse(skipBindingPatterns, ContextFlagsSkipBindingPatterns, ContextFlagsNone))
		if contextualType != nil {
			inferenceTargetType := c.getReturnTypeOfSignature(signature)
			if c.couldContainTypeVariables(inferenceTargetType) {
				outerContext := c.getInferenceContext(node)
				isFromBindingPattern := !skipBindingPatterns && c.getContextualType(node, ContextFlagsSkipBindingPatterns) != contextualType
				// A return type inference from a binding pattern can be used in instantiating the contextual
				// type of an argument later in inference, but cannot stand on its own as the final return type.
				// It is incorporated into `context.returnMapper` which is used in `instantiateContextualType`,
				// but doesn't need to go into `context.inferences`. This allows a an array binding pattern to
				// produce a tuple for `T` in
				//   declare function f<T>(cb: () => T): T;
				//   const [e1, e2, e3] = f(() => [1, "hi", true]);
				// but does not produce any inference for `T` in
				//   declare function f<T>(): T;
				//   const [e1, e2, e3] = f();
				if !isFromBindingPattern {
					// We clone the inference context to avoid disturbing a resolution in progress for an
					// outer call expression. Effectively we just want a snapshot of whatever has been
					// inferred for any outer call expression so far.
					outerMapper := c.getMapperFromContext(c.cloneInferenceContext(outerContext, InferenceFlagsNoDefault))
					instantiatedType := c.instantiateType(contextualType, outerMapper)
					// If the contextual type is a generic function type with a single call signature, we
					// instantiate the type with its own type parameters and type arguments. This ensures that
					// the type parameters are not erased to type any during type inference such that they can
					// be inferred as actual types from the contextual type. For example:
					//   declare function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[];
					//   const boxElements: <A>(a: A[]) => { value: A }[] = arrayMap(value => ({ value }));
					// Above, the type of the 'value' parameter is inferred to be 'A'.
					contextualSignature := c.getSingleCallSignature(instantiatedType)
					var inferenceSourceType *Type
					if contextualSignature != nil && len(contextualSignature.typeParameters) != 0 {
						inferenceSourceType = c.getOrCreateTypeFromSignature(c.getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters))
					} else {
						inferenceSourceType = instantiatedType
					}
					// Inferences made from return types have lower priority than all other inferences.
					c.inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriorityReturnType, false)
				}
				// Create a type mapper for instantiating generic contextual types using the inferences made
				// from the return type. We need a separate inference pass here because (a) instantiation of
				// the source type uses the outer context's return mapper (which excludes inferences made from
				// outer arguments), and (b) we don't want any further inferences going into this context.
				// We use `createOuterReturnMapper` to ensure that all occurrences of outer type parameters are
				// replaced with inferences produced from the outer return type or preceding outer arguments.
				// This protects against circular inferences, i.e. avoiding situations where inferences reference
				// type parameters for which the inferences are being made.
				returnContext := c.newInferenceContext(signature.typeParameters, signature, context.flags, nil)
				var outerReturnMapper *TypeMapper
				if outerContext != nil {
					outerReturnMapper = c.createOuterReturnMapper(outerContext)
				}
				returnSourceType := c.instantiateType(contextualType, outerReturnMapper)
				c.inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType, InferencePriorityNone, false)
				if core.Some(returnContext.inferences, hasInferenceCandidates) {
					context.returnMapper = c.getMapperFromContext(c.cloneInferredPartOfContext(returnContext))
				} else {
					context.returnMapper = nil
				}
			}
		}
	}
	restType := c.getNonArrayRestType(signature)
	argCount := len(args)
	if restType != nil {
		argCount = min(c.getParameterCount(signature)-1, argCount)
	}
	if restType != nil && restType.flags&TypeFlagsTypeParameter != 0 {
		info := core.Find(context.inferences, func(info *InferenceInfo) bool { return info.typeParameter == restType })
		if info != nil {
			if core.FindIndex(args[argCount:], isSpreadArgument) < 0 {
				info.impliedArity = len(args) - argCount
			}
		}
	}
	thisType := c.getThisTypeOfSignature(signature)
	if thisType != nil && c.couldContainTypeVariables(thisType) {
		thisArgumentNode := c.getThisArgumentOfCall(node)
		c.inferTypes(context.inferences, c.getThisArgumentType(thisArgumentNode), thisType, InferencePriorityNone, false)
	}
	for i := range argCount {
		arg := args[i]
		if arg.Kind != ast.KindOmittedExpression {
			paramType := c.getTypeAtPosition(signature, i)
			if c.couldContainTypeVariables(paramType) {
				argType := c.checkExpressionWithContextualType(arg, paramType, context, checkMode)
				c.inferTypes(context.inferences, argType, paramType, InferencePriorityNone, false)
			}
		}
	}
	if restType != nil && c.couldContainTypeVariables(restType) {
		spreadType := c.getSpreadArgumentType(args, argCount, len(args), restType, context, checkMode)
		c.inferTypes(context.inferences, spreadType, restType, InferencePriorityNone, false)
	}
	return c.getInferredTypes(context)
}

// No signature was applicable. We have already reported the errors for the invalid signature.
func (c *Checker) getCandidateForOverloadFailure(node *ast.Node, candidates []*Signature, args []*ast.Node, hasCandidatesOutArray bool, checkMode CheckMode) *Signature {
	// Else should not have called this.
	c.checkNodeDeferred(node)
	// Normally we will combine overloads. Skip this if they have type parameters since that's hard to combine.
	// Don't do this if there is a `candidatesOutArray`,
	// because then we want the chosen best candidate to be one of the overloads, not a combination.
	if hasCandidatesOutArray || len(candidates) == 1 || core.Some(candidates, func(s *Signature) bool { return len(s.typeParameters) != 0 }) {
		return c.pickLongestCandidateSignature(node, candidates, args, checkMode)
	}
	return c.createUnionOfSignaturesForOverloadFailure(candidates)
}

func (c *Checker) pickLongestCandidateSignature(node *ast.Node, candidates []*Signature, args []*ast.Node, checkMode CheckMode) *Signature {
	// Pick the longest signature. This way we can get a contextual type for cases like:
	//     declare function f(a: { xa: number; xb: number; }, b: number);
	//     f({ |
	// Also, use explicitly-supplied type arguments if they are provided, so we can get a contextual signature in cases like:
	//     declare function f<T>(k: keyof T);
	//     f<Foo>("
	argCount := len(args)
	if c.apparentArgumentCount != nil {
		argCount = *c.apparentArgumentCount
	}
	bestIndex := c.getLongestCandidateIndex(candidates, argCount)
	candidate := candidates[bestIndex]
	typeParameters := candidate.typeParameters
	if len(typeParameters) == 0 {
		return candidate
	}
	var typeArgumentNodes []*ast.Node
	if c.callLikeExpressionMayHaveTypeArguments(node) {
		typeArgumentNodes = node.TypeArguments()
	}
	var instantiated *Signature
	if len(typeArgumentNodes) != 0 {
		instantiated = c.createSignatureInstantiation(candidate, c.getTypeArgumentsFromNodes(typeArgumentNodes, typeParameters))
	} else {
		instantiated = c.inferSignatureInstantiationForOverloadFailure(node, typeParameters, candidate, args, checkMode)
	}
	candidates[bestIndex] = instantiated
	return instantiated
}

func (c *Checker) getLongestCandidateIndex(candidates []*Signature, argsCount int) int {
	maxParamsIndex := -1
	maxParams := -1
	for i, candidate := range candidates {
		paramCount := c.getParameterCount(candidate)
		if c.hasEffectiveRestParameter(candidate) || paramCount >= argsCount {
			return i
		}
		if paramCount > maxParams {
			maxParams = paramCount
			maxParamsIndex = i
		}
	}
	return maxParamsIndex
}

func (c *Checker) getTypeArgumentsFromNodes(typeArgumentNodes []*ast.Node, typeParameters []*Type) []*Type {
	if len(typeArgumentNodes) > len(typeParameters) {
		typeArgumentNodes = typeArgumentNodes[:len(typeParameters)]
	}
	typeArguments := core.Map(typeArgumentNodes, c.getTypeFromTypeNode)
	for len(typeArguments) < len(typeParameters) {
		t := c.getDefaultFromTypeParameter(typeParameters[len(typeArguments)])
		if t == nil {
			t = c.getConstraintOfTypeParameter(typeParameters[len(typeArguments)])
			if t == nil {
				t = c.unknownType
			}
		}
		typeArguments = append(typeArguments, t)
	}
	return typeArguments
}

func (c *Checker) inferSignatureInstantiationForOverloadFailure(node *ast.Node, typeParameters []*Type, candidate *Signature, args []*ast.Node, checkMode CheckMode) *Signature {
	inferenceContext := c.newInferenceContext(typeParameters, candidate, core.IfElse(ast.IsInJSFile(node), InferenceFlagsAnyDefault, InferenceFlagsNone), nil)
	typeArgumentTypes := c.inferTypeArguments(node, candidate, args, checkMode|CheckModeSkipContextSensitive|CheckModeSkipGenericFunctions, inferenceContext)
	return c.createSignatureInstantiation(candidate, typeArgumentTypes)
}

func (c *Checker) createUnionOfSignaturesForOverloadFailure(candidates []*Signature) *Signature {
	thisParameters := core.MapNonNil(candidates, func(c *Signature) *ast.Symbol { return c.thisParameter })
	var thisParameter *ast.Symbol
	if len(thisParameters) != 0 {
		thisParameter = c.createCombinedSymbolFromTypes(thisParameters, core.Map(thisParameters, c.getTypeOfParameter))
	}
	minArgumentCount, maxNonRestParam := minAndMax(candidates, getNonRestParameterCount)
	parameters := make([]*ast.Symbol, 0, maxNonRestParam)
	for i := range maxNonRestParam {
		symbols := core.MapNonNil(candidates, func(s *Signature) *ast.Symbol {
			if signatureHasRestParameter(s) {
				if i < len(s.parameters)-1 {
					return s.parameters[i]
				}
				return core.LastOrNil(s.parameters)
			}
			if i < len(s.parameters) {
				return s.parameters[i]
			}
			return nil
		})
		parameters = append(parameters, c.createCombinedSymbolFromTypes(symbols, core.MapNonNil(candidates, func(s *Signature) *Type { return c.tryGetTypeAtPosition(s, i) })))
	}
	restParameterSymbols := core.MapNonNil(candidates, func(s *Signature) *ast.Symbol {
		if signatureHasRestParameter(s) {
			return core.LastOrNil(s.parameters)
		}
		return nil
	})
	flags := SignatureFlagsIsSignatureCandidateForOverloadFailure
	if len(restParameterSymbols) != 0 {
		t := c.createArrayType(c.getUnionTypeEx(core.MapNonNil(candidates, c.tryGetRestTypeOfSignature), UnionReductionSubtype, nil, nil))
		parameters = append(parameters, c.createCombinedSymbolForOverloadFailure(restParameterSymbols, t))
		flags |= SignatureFlagsHasRestParameter
	}
	if core.Some(candidates, signatureHasLiteralTypes) {
		flags |= SignatureFlagsHasLiteralTypes
	}
	return c.newSignature(flags, candidates[0].declaration, nil, thisParameter, parameters, c.getIntersectionType(core.Map(candidates, c.getReturnTypeOfSignature)), nil, minArgumentCount)
}

func (c *Checker) createCombinedSymbolFromTypes(sources []*ast.Symbol, types []*Type) *ast.Symbol {
	return c.createCombinedSymbolForOverloadFailure(sources, c.getUnionTypeEx(types, UnionReductionSubtype, nil, nil))
}

func (c *Checker) createCombinedSymbolForOverloadFailure(sources []*ast.Symbol, t *Type) *ast.Symbol {
	// This function is currently only used for erroneous overloads, so it's good enough to just use the first source.
	return c.createSymbolWithType(core.FirstOrNil(sources), t)
}

func (c *Checker) getRestTypeOfSignature(signature *Signature) *Type {
	return core.OrElse(c.tryGetRestTypeOfSignature(signature), c.anyType)
}

func (c *Checker) tryGetRestTypeOfSignature(signature *Signature) *Type {
	if !signatureHasRestParameter(signature) {
		return nil
	}
	restType := c.getTypeOfSymbol(signature.parameters[len(signature.parameters)-1])
	if isTupleType(restType) {
		restType = c.getRestTypeOfTupleType(restType)
		if restType == nil {
			return nil
		}
	}
	return c.getIndexTypeOfType(restType, c.numberType)
}

func (c *Checker) reportCallResolutionErrors(node *ast.Node, s *CallState, signatures []*Signature, headMessage *diagnostics.Message) {
	switch {
	case len(s.candidatesForArgumentError) != 0:
		last := s.candidatesForArgumentError[len(s.candidatesForArgumentError)-1]
		var diags []*ast.Diagnostic
		c.isSignatureApplicable(s.node, s.args, last, c.assignableRelation, CheckModeNormal, true /*reportErrors*/, &diags)
		for _, diagnostic := range diags {
			if len(s.candidatesForArgumentError) > 1 {
				diagnostic = ast.NewDiagnosticChain(diagnostic, diagnostics.The_last_overload_gave_the_following_error)
				diagnostic = ast.NewDiagnosticChain(diagnostic, diagnostics.No_overload_matches_this_call)
			}
			if headMessage != nil {
				diagnostic = ast.NewDiagnosticChain(diagnostic, headMessage)
			}
			if last.declaration != nil && len(s.candidatesForArgumentError) > 1 {
				diagnostic.AddRelatedInfo(NewDiagnosticForNode(last.declaration, diagnostics.The_last_overload_is_declared_here))
			}
			c.addImplementationSuccessElaboration(s, last, diagnostic)
			c.addDiagnostic(diagnostic)
		}
	case s.candidateForArgumentArityError != nil:
		c.addDiagnostic(c.getArgumentArityError(s.node, []*Signature{s.candidateForArgumentArityError}, s.args, headMessage))
	case s.candidateForTypeArgumentError != nil:
		c.checkTypeArguments(s.candidateForTypeArgumentError, s.node.TypeArguments(), true /*reportErrors*/, headMessage)
	case !ast.IsJsxOpeningFragment(node):
		signaturesWithCorrectTypeArgumentArity := core.Filter(signatures, func(sig *Signature) bool {
			return c.hasCorrectTypeArgumentArity(sig, s.typeArguments)
		})
		if len(signaturesWithCorrectTypeArgumentArity) == 0 {
			c.addDiagnostic(c.getTypeArgumentArityError(s.node, signatures, s.typeArguments, headMessage))
		} else {
			c.addDiagnostic(c.getArgumentArityError(s.node, signaturesWithCorrectTypeArgumentArity, s.args, headMessage))
		}
	}
}

func (c *Checker) addImplementationSuccessElaboration(s *CallState, failed *Signature, diagnostic *ast.Diagnostic) {
	if failed.declaration != nil && failed.declaration.Symbol() != nil {
		declarations := failed.declaration.Symbol().Declarations
		if len(declarations) > 1 {
			implementation := core.Find(declarations, func(d *ast.Declaration) bool {
				return ast.IsFunctionLikeDeclaration(d) && ast.NodeIsPresent(d.Body())
			})
			if implementation != nil {
				candidate := c.getSignatureFromDeclaration(implementation)
				localState := *s
				localState.candidates = []*Signature{candidate}
				localState.isSingleNonGenericCandidate = len(candidate.typeParameters) == 0
				if c.chooseOverload(&localState, c.assignableRelation) != nil {
					diagnostic.AddRelatedInfo(NewDiagnosticForNode(implementation, diagnostics.The_call_would_have_succeeded_against_this_implementation_but_implementation_signatures_of_overloads_are_not_externally_visible))
				}
			}
		}
	}
}

func (c *Checker) getArgumentArityError(node *ast.Node, signatures []*Signature, args []*ast.Node, headMessage *diagnostics.Message) *ast.Diagnostic {
	spreadIndex := c.getSpreadArgumentIndex(args)
	if spreadIndex > -1 {
		return NewDiagnosticForNode(args[spreadIndex], diagnostics.A_spread_argument_must_either_have_a_tuple_type_or_be_passed_to_a_rest_parameter)
	}
	minCount := math.MaxInt // smallest parameter count
	maxCount := math.MinInt // largest parameter count
	maxBelow := math.MinInt // largest parameter count that is smaller than the number of arguments
	minAbove := math.MaxInt // smallest parameter count that is larger than the number of arguments
	var closestSignature *Signature
	for _, sig := range signatures {
		minParameter := c.getMinArgumentCount(sig)
		maxParameter := c.getParameterCount(sig)
		// smallest/largest parameter counts
		if minParameter < minCount {
			minCount = minParameter
			closestSignature = sig
		}
		maxCount = max(maxCount, maxParameter)
		// shortest parameter count *longer than the call*/longest parameter count *shorter than the call*
		if minParameter < len(args) && minParameter > maxBelow {
			maxBelow = minParameter
		}
		if len(args) < maxParameter && maxParameter < minAbove {
			minAbove = maxParameter
		}
	}
	hasRestParameter := core.Some(signatures, c.hasEffectiveRestParameter)
	var parameterRange string
	switch {
	case hasRestParameter:
		parameterRange = strconv.Itoa(minCount)
	case minCount < maxCount:
		parameterRange = strconv.Itoa(minCount) + "-" + strconv.Itoa(maxCount)
	default:
		parameterRange = strconv.Itoa(minCount)
	}
	isVoidPromiseError := !hasRestParameter && parameterRange == "1" && len(args) == 0 && c.isPromiseResolveArityError(node)
	errorNode := getErrorNodeForCallNode(node)
	if isVoidPromiseError && ast.IsInJSFile(node) {
		return NewDiagnosticForNode(errorNode, diagnostics.Expected_1_argument_but_got_0_new_Promise_needs_a_JSDoc_hint_to_produce_a_resolve_that_can_be_called_without_arguments)
	}
	var message *diagnostics.Message
	switch {
	case ast.IsDecorator(node):
		if hasRestParameter {
			message = diagnostics.The_runtime_will_invoke_the_decorator_with_1_arguments_but_the_decorator_expects_at_least_0
		} else {
			message = diagnostics.The_runtime_will_invoke_the_decorator_with_1_arguments_but_the_decorator_expects_0
		}
	case hasRestParameter:
		message = diagnostics.Expected_at_least_0_arguments_but_got_1
	case isVoidPromiseError:
		message = diagnostics.Expected_0_arguments_but_got_1_Did_you_forget_to_include_void_in_your_type_argument_to_Promise
	default:
		message = diagnostics.Expected_0_arguments_but_got_1
	}
	switch {
	case minCount < len(args) && len(args) < maxCount:
		// between min and max, but with no matching overload
		diagnostic := NewDiagnosticForNode(errorNode, diagnostics.No_overload_expects_0_arguments_but_overloads_do_exist_that_expect_either_1_or_2_arguments, len(args), maxBelow, minAbove)
		if headMessage != nil {
			diagnostic = ast.NewDiagnosticChain(diagnostic, headMessage)
		}
		return diagnostic
	case len(args) < minCount:
		// too short: put the error span on the call expression, not any of the args
		diagnostic := NewDiagnosticForNode(errorNode, message, parameterRange, len(args))
		if headMessage != nil {
			diagnostic = ast.NewDiagnosticChain(diagnostic, headMessage)
		}
		var parameter *ast.Node
		if closestSignature != nil && closestSignature.declaration != nil {
			parameter = core.ElementOrNil(closestSignature.declaration.Parameters(), len(args)+core.IfElse(closestSignature.thisParameter != nil, 1, 0))
		}
		if parameter != nil {
			var related *ast.Diagnostic
			switch {
			case ast.IsBindingPattern(parameter.Name()):
				related = NewDiagnosticForNode(parameter, diagnostics.An_argument_matching_this_binding_pattern_was_not_provided)
			case isRestParameter(parameter):
				related = NewDiagnosticForNode(parameter, diagnostics.Arguments_for_the_rest_parameter_0_were_not_provided, parameter.Name().Text())
			default:
				related = NewDiagnosticForNode(parameter, diagnostics.An_argument_for_0_was_not_provided, parameter.Name().Text())
			}
			diagnostic.AddRelatedInfo(related)
		}
		return diagnostic
	default:
		// Guard against out-of-bounds access when maxCount >= len(args).
		// This can happen when we reach this fallback error path but the argument
		// count actually matches the parameter count (e.g., due to trailing commas
		// causing signature resolution to fail for other reasons).
		if maxCount >= len(args) {
			diagnostic := NewDiagnosticForNode(errorNode, message, parameterRange, len(args))
			if headMessage != nil {
				diagnostic = ast.NewDiagnosticChain(diagnostic, headMessage)
			}
			return diagnostic
		}
		sourceFile := ast.GetSourceFileOfNode(node)
		pos := args[maxCount].Pos()
		end := args[len(args)-1].End()
		if end == pos {
			end++
		}
		pos = scanner.SkipTrivia(sourceFile.Text(), pos)
		if end < pos {
			end = pos
		}
		diagnostic := ast.NewDiagnostic(sourceFile, core.NewTextRange(pos, end), message, parameterRange, len(args))
		if headMessage != nil {
			diagnostic = ast.NewDiagnosticChain(diagnostic, headMessage)
		}
		return diagnostic
	}
}

func (c *Checker) isPromiseResolveArityError(node *ast.Node) bool {
	if !ast.IsCallExpression(node) || !ast.IsIdentifier(node.Expression()) {
		return false
	}
	symbol := c.resolveName(node.Expression(), node.Expression().Text(), ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, false /*isUse*/, false)
	if symbol == nil {
		return false
	}
	decl := symbol.ValueDeclaration
	if decl == nil || !ast.IsParameterDeclaration(decl) || !ast.IsFunctionExpressionOrArrowFunction(decl.Parent) || !ast.IsNewExpression(decl.Parent.Parent) || !ast.IsIdentifier(decl.Parent.Parent.Expression()) {
		return false
	}
	globalPromiseSymbol := c.getGlobalPromiseConstructorSymbolOrNil()
	if globalPromiseSymbol == nil {
		return false
	}
	constructorSymbol := c.getResolvedSymbol(decl.Parent.Parent.Expression())
	return constructorSymbol == globalPromiseSymbol
}

func getErrorNodeForCallNode(node *ast.Node) *ast.Node {
	if ast.IsCallExpression(node) {
		node = node.Expression()
		if ast.IsPropertyAccessExpression(node) {
			node = node.Name()
		}
	}
	return node
}

func (c *Checker) getTypeArgumentArityError(node *ast.Node, signatures []*Signature, typeArguments []*ast.Node, headMessage *diagnostics.Message) *ast.Diagnostic {
	var diagnostic *ast.Diagnostic
	argCount := len(typeArguments)
	sourceFile := ast.GetSourceFileOfNode(node)
	typeArgumentList := node.TypeArgumentList()
	loc := core.NewTextRange(scanner.SkipTrivia(sourceFile.Text(), typeArgumentList.Loc.Pos()), typeArgumentList.Loc.End())
	if len(signatures) == 1 {
		// No overloads exist
		sig := signatures[0]
		minCount := c.getMinTypeArgumentCount(sig.typeParameters)
		maxCount := len(sig.typeParameters)
		expected := strconv.Itoa(minCount)
		if minCount < maxCount {
			expected = expected + "-" + strconv.Itoa(maxCount)
		}
		diagnostic = ast.NewDiagnostic(sourceFile, loc, diagnostics.Expected_0_type_arguments_but_got_1, expected, argCount)
	} else {
		// Overloads exist
		belowArgCount := math.MinInt
		aboveArgCount := math.MaxInt
		for _, sig := range signatures {
			minCount := c.getMinTypeArgumentCount(sig.typeParameters)
			maxCount := len(sig.typeParameters)
			if minCount > argCount {
				aboveArgCount = min(aboveArgCount, minCount)
			} else if maxCount < argCount {
				belowArgCount = max(belowArgCount, maxCount)
			}
		}
		if belowArgCount != math.MinInt && aboveArgCount != math.MaxInt {
			diagnostic = ast.NewDiagnostic(sourceFile, loc, diagnostics.No_overload_expects_0_type_arguments_but_overloads_do_exist_that_expect_either_1_or_2_type_arguments, argCount, belowArgCount, aboveArgCount)
		} else {
			diagnostic = ast.NewDiagnostic(sourceFile, loc, diagnostics.Expected_0_type_arguments_but_got_1, core.IfElse(belowArgCount == math.MinInt, aboveArgCount, belowArgCount), argCount)
		}
	}
	if headMessage != nil {
		diagnostic = ast.NewDiagnosticChain(diagnostic, headMessage)
	}
	return diagnostic
}

func (c *Checker) reportCannotInvokePossiblyNullOrUndefinedError(node *ast.Node, facts TypeFacts) {
	c.error(node, core.IfElse(facts&TypeFactsIsUndefined != 0,
		core.IfElse(facts&TypeFactsIsNull != 0,
			diagnostics.Cannot_invoke_an_object_which_is_possibly_null_or_undefined,
			diagnostics.Cannot_invoke_an_object_which_is_possibly_undefined),
		diagnostics.Cannot_invoke_an_object_which_is_possibly_null))
}

func (c *Checker) resolveUntypedCall(node *ast.Node) *Signature {
	if c.callLikeExpressionMayHaveTypeArguments(node) {
		// Check type arguments even though we will give an error that untyped calls may not accept type arguments.
		// This gets us diagnostics for the type arguments and marks them as referenced.
		c.checkSourceElements(node.TypeArguments())
	}
	switch node.Kind {
	case ast.KindTaggedTemplateExpression:
		c.checkExpression(node.AsTaggedTemplateExpression().Template)
	case ast.KindJsxOpeningElement, ast.KindJsxSelfClosingElement:
		c.checkExpression(node.Attributes())
	case ast.KindBinaryExpression:
		c.checkExpression(node.AsBinaryExpression().Left)
	case ast.KindCallExpression, ast.KindNewExpression:
		for _, argument := range node.Arguments() {
			c.checkExpression(argument)
		}
	}
	return c.anySignature
}

func (c *Checker) resolveErrorCall(node *ast.Node) *Signature {
	c.resolveUntypedCall(node)
	return c.unknownSignature
}

/**
 * TS 1.0 spec: 4.12
 * If FuncExpr is of type Any, or of an object type that has no call or construct signatures
 * but is a subtype of the Function interface, the call is an untyped function call.
 */
func (c *Checker) isUntypedFunctionCall(funcType *Type, apparentFuncType *Type, numCallSignatures int, numConstructSignatures int) bool {
	// We exclude union types because we may have a union of function types that happen to have no common signatures.
	return IsTypeAny(funcType) || IsTypeAny(apparentFuncType) && funcType.flags&TypeFlagsTypeParameter != 0 ||
		numCallSignatures == 0 && numConstructSignatures == 0 && apparentFuncType.flags&TypeFlagsUnion == 0 &&
			c.getReducedType(apparentFuncType).flags&TypeFlagsNever == 0 && c.isTypeAssignableTo(funcType, c.globalFunctionType)
}

func (c *Checker) invocationErrorDetails(errorTarget *ast.Node, apparentType *Type, kind SignatureKind) *ast.Diagnostic {
	var diagnostic *ast.Diagnostic
	isCall := kind == SignatureKindCall
	awaitedType := c.getAwaitedType(apparentType)
	maybeMissingAwait := awaitedType != nil && len(c.getSignaturesOfType(awaitedType, kind)) > 0
	target := errorTarget
	if ast.IsPropertyAccessExpression(errorTarget) && ast.IsCallExpression(errorTarget.Parent) {
		target = errorTarget.Name()
	}
	if apparentType.flags&TypeFlagsUnion != 0 {
		types := apparentType.Types()
		hasSignatures := false
		for _, constituent := range types {
			signatures := c.getSignaturesOfType(constituent, kind)
			if len(signatures) != 0 {
				hasSignatures = true
				if diagnostic != nil {
					// Bail early if we already have an error, no chance of "No constituent of type is callable"
					break
				}
			} else {
				// Error on the first non callable constituent only
				if diagnostic == nil {
					diagnostic = NewDiagnosticForNode(target, core.IfElse(isCall, diagnostics.Type_0_has_no_call_signatures, diagnostics.Type_0_has_no_construct_signatures), c.TypeToString(constituent))
					diagnostic = NewDiagnosticChainForNode(diagnostic, target, core.IfElse(isCall, diagnostics.Not_all_constituents_of_type_0_are_callable, diagnostics.Not_all_constituents_of_type_0_are_constructable), c.TypeToString(apparentType))
				}
				if hasSignatures {
					// Bail early if we already found a signature, no chance of "No constituent of type is callable"
					break
				}
			}
		}
		if !hasSignatures {
			diagnostic = NewDiagnosticForNode(target, core.IfElse(isCall, diagnostics.No_constituent_of_type_0_is_callable, diagnostics.No_constituent_of_type_0_is_constructable), c.TypeToString(apparentType))
		}
		if diagnostic == nil {
			diagnostic = NewDiagnosticForNode(target, core.IfElse(isCall, diagnostics.Each_member_of_the_union_type_0_has_signatures_but_none_of_those_signatures_are_compatible_with_each_other, diagnostics.Each_member_of_the_union_type_0_has_construct_signatures_but_none_of_those_signatures_are_compatible_with_each_other), c.TypeToString(apparentType))
		}
	} else {
		diagnostic = NewDiagnosticChainForNode(diagnostic, target, core.IfElse(isCall, diagnostics.Type_0_has_no_call_signatures, diagnostics.Type_0_has_no_construct_signatures), c.TypeToString(apparentType))
	}
	headMessage := core.IfElse(isCall, diagnostics.This_expression_is_not_callable, diagnostics.This_expression_is_not_constructable)
	// Diagnose get accessors incorrectly called as functions
	if ast.IsCallExpression(errorTarget.Parent) && len(errorTarget.Parent.Arguments()) == 0 {
		resolvedSymbol := c.getResolvedSymbolOrNil(errorTarget)
		if resolvedSymbol != nil && resolvedSymbol.Flags&ast.SymbolFlagsGetAccessor != 0 {
			headMessage = diagnostics.This_expression_is_not_callable_because_it_is_a_get_accessor_Did_you_mean_to_use_it_without
		}
	}
	diagnostic = NewDiagnosticChainForNode(diagnostic, target, headMessage)
	if maybeMissingAwait {
		diagnostic.AddRelatedInfo(NewDiagnosticForNode(errorTarget, diagnostics.Did_you_forget_to_use_await))
	}
	return diagnostic
}

func (c *Checker) invocationError(errorTarget *ast.Node, apparentType *Type, kind SignatureKind, relatedInformation *ast.Diagnostic) {
	diagnostic := c.invocationErrorDetails(errorTarget, apparentType, kind)
	if relatedInformation != nil {
		diagnostic.AddRelatedInfo(relatedInformation)
	}
	c.addDiagnostic(diagnostic)
	c.invocationErrorRecovery(apparentType, kind, diagnostic)
}

func (c *Checker) invocationErrorRecovery(apparentType *Type, kind SignatureKind, diagnostic *ast.Diagnostic) {
	if apparentType.symbol == nil {
		return
	}
	importNode := c.exportTypeLinks.Get(apparentType.symbol).originatingImport
	// Create a diagnostic on the originating import if possible onto which we can attach a quickfix
	//  An import call expression cannot be rewritten into another form to correct the error - the only solution is to use `.default` at the use-site
	if importNode != nil && !ast.IsImportCall(importNode) {
		sigs := c.getSignaturesOfType(c.getTypeOfSymbol(c.exportTypeLinks.Get(apparentType.symbol).target), kind)
		if len(sigs) == 0 {
			return
		}
		diagnostic.AddRelatedInfo(NewDiagnosticForNode(importNode, diagnostics.Type_originates_at_this_import_A_namespace_style_import_cannot_be_called_or_constructed_and_will_cause_a_failure_at_runtime_Consider_using_a_default_import_or_import_require_here_instead))
	}
}

func (c *Checker) isGenericFunctionReturningFunction(signature *Signature) bool {
	return len(signature.typeParameters) != 0 && c.isFunctionType(c.getReturnTypeOfSignature(signature))
}

func (c *Checker) skippedGenericFunction(node *ast.Node, checkMode CheckMode) {
	if checkMode&CheckModeInferential != 0 {
		// We have skipped a generic function during inferential typing. Obtain the inference context and
		// indicate this has occurred such that we know a second pass of inference is be needed.
		context := c.getInferenceContext(node)
		context.flags |= InferenceFlagsSkippedGenericFunction
	}
}

func (c *Checker) checkTaggedTemplateExpression(node *ast.Node) *Type {
	if !c.checkGrammarTaggedTemplateChain(node.AsTaggedTemplateExpression()) {
		c.checkGrammarTypeArguments(node, node.TypeArgumentList())
	}
	signature := c.getResolvedSignature(node, nil, CheckModeNormal)
	c.checkDeprecatedSignature(signature, node)
	return c.getReturnTypeOfSignature(signature)
}

func (c *Checker) checkParenthesizedExpression(node *ast.Node, checkMode CheckMode) *Type {
	return c.checkExpressionEx(node.Expression(), checkMode)
}

func (c *Checker) checkClassExpression(node *ast.Node) *Type {
	c.checkClassLikeDeclaration(node)
	c.checkNodeDeferred(node)
	c.checkClassExpressionExternalHelpers(node)
	return c.getTypeOfSymbol(c.getSymbolOfDeclaration(node))
}

func (c *Checker) getFirstTransformableStaticClassElement(node *ast.Node) *ast.Node {
	willTransformStaticElementsOfDecoratedClass := !c.legacyDecorators &&
		c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators &&
		ast.ClassOrConstructorParameterIsDecorated(false, node)
	willTransformPrivateElementsOrClassStaticBlocks := c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks || c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators
	willTransformInitializers := !c.emitStandardClassFields
	if willTransformStaticElementsOfDecoratedClass || willTransformPrivateElementsOrClassStaticBlocks {
		for _, member := range node.Members() {
			if willTransformStaticElementsOfDecoratedClass && ast.ClassElementOrClassElementParameterIsDecorated(false, member, node) {
				if firstDecorator := core.FirstOrNil(node.Decorators()); firstDecorator != nil {
					return firstDecorator
				}
				return node
			} else if willTransformPrivateElementsOrClassStaticBlocks {
				if ast.IsClassStaticBlockDeclaration(member) {
					return member
				} else if ast.IsStatic(member) &&
					(ast.IsPrivateIdentifierClassElementDeclaration(member) ||
						willTransformInitializers && ast.IsInitializedProperty(member)) {
					return member
				}
			}
		}
	}
	return nil
}

func (c *Checker) checkClassExpressionExternalHelpers(node *ast.ClassExpressionNode) {
	if node.Name() != nil {
		return
	}
	parent := walkUpOuterExpressions(node)
	if !ast.IsNamedEvaluationSource(parent) {
		return
	}

	willTransformESDecorators := !c.legacyDecorators && c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators
	var location *ast.Node
	if willTransformESDecorators && ast.ClassOrConstructorParameterIsDecorated(false, node) {
		location = node
		if firstDecorator := core.FirstOrNil(node.Decorators()); firstDecorator != nil {
			location = firstDecorator
		}
	} else {
		location = c.getFirstTransformableStaticClassElement(node)
	}

	if location != nil {
		c.checkExternalEmitHelpers(location, ExternalEmitHelpersSetFunctionName)
		if (ast.IsPropertyAssignment(parent) || ast.IsPropertyDeclaration(parent) || ast.IsBindingElement(parent)) && ast.IsComputedPropertyName(parent.Name()) {
			c.checkExternalEmitHelpers(location, ExternalEmitHelpersPropKey)
		}
	}
}

func (c *Checker) checkClassExpressionDeferred(node *ast.Node) {
	c.checkSourceElements(node.Members())
	c.registerForUnusedIdentifiersCheck(node)
}

func (c *Checker) checkFunctionExpressionOrObjectLiteralMethod(node *ast.Node, checkMode CheckMode) *Type {
	c.checkNodeDeferred(node)
	if ast.IsFunctionExpression(node) {
		c.checkCollisionsForDeclarationName(node, node.Name())
	}
	if checkMode&CheckModeSkipContextSensitive != 0 && c.isContextSensitive(node) {
		// Skip parameters, return signature with return type that retains noncontextual parts so inferences can still be drawn in an early stage
		if node.Type() == nil && !ast.HasContextSensitiveParameters(node) {
			// Return plain anyFunctionType if there is no possibility we'll make inferences from the return type
			contextualSignature := c.getContextualSignature(node)
			if contextualSignature != nil && c.couldContainTypeVariables(c.getReturnTypeOfSignature(contextualSignature)) {
				if cached, ok := c.contextFreeTypes[node]; ok {
					return cached
				}
				returnType := c.getReturnTypeFromBody(node, checkMode)
				returnOnlySignature := c.newSignature(SignatureFlagsIsNonInferrable, nil, nil /*typeParameters*/, nil /*thisParameter*/, nil, returnType, nil /*resolvedTypePredicate*/, 0)
				returnOnlyType := c.newAnonymousType(node.Symbol(), nil, []*Signature{returnOnlySignature}, nil, nil)
				returnOnlyType.objectFlags |= ObjectFlagsNonInferrableType
				c.contextFreeTypes[node] = returnOnlyType
				return returnOnlyType
			}
		}
		return c.anyFunctionType
	}
	// Grammar checking
	hasGrammarError := c.checkGrammarFunctionLikeDeclaration(node)
	if !hasGrammarError && ast.IsFunctionExpression(node) {
		c.checkGrammarForGenerator(node)
	}
	if node.FunctionLikeData().FullSignature != nil {
		if c.getContextualCallSignature(c.getTypeFromTypeNode(node.FunctionLikeData().FullSignature), node) == nil {
			c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_on_a_function_must_have_a_signature_with_the_correct_number_of_arguments)
		}
	}
	c.contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node, checkMode)
	return c.getTypeOfSymbol(c.getSymbolOfDeclaration(node))
}

func (c *Checker) contextuallyCheckFunctionExpressionOrObjectLiteralMethod(node *ast.Node, checkMode CheckMode) {
	links := c.nodeLinks.Get(node)
	// Check if function expression is contextually typed and assign parameter types if so.
	if links.flags&NodeCheckFlagsContextChecked == 0 {
		contextualSignature := c.getContextualSignature(node)
		// If a type check is started at a function expression that is an argument of a function call, obtaining the
		// contextual type may recursively get back to here during overload resolution of the call. If so, we will have
		// already assigned contextual types.
		if links.flags&NodeCheckFlagsContextChecked == 0 {
			links.flags |= NodeCheckFlagsContextChecked
			signature := core.FirstOrNil(c.getSignaturesOfType(c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)), SignatureKindCall))
			if signature == nil {
				return
			}
			if c.isContextSensitive(node) {
				if contextualSignature != nil {
					inferenceContext := c.getInferenceContext(node)
					var instantiatedContextualSignature *Signature
					if checkMode&CheckModeInferential != 0 {
						c.inferFromAnnotatedParametersAndReturn(signature, contextualSignature, inferenceContext)
						restType := c.getEffectiveRestType(contextualSignature)
						if restType != nil && restType.flags&TypeFlagsTypeParameter != 0 {
							instantiatedContextualSignature = c.instantiateSignature(contextualSignature, inferenceContext.nonFixingMapper)
						}
					}
					if instantiatedContextualSignature == nil {
						if inferenceContext != nil {
							instantiatedContextualSignature = c.instantiateSignature(contextualSignature, inferenceContext.mapper)
						} else {
							instantiatedContextualSignature = contextualSignature
						}
					}
					c.assignContextualParameterTypes(signature, instantiatedContextualSignature)
				} else {
					// Force resolution of all parameter types such that the absence of a contextual type is consistently reflected.
					c.assignNonContextualParameterTypes(signature)
				}
			} else if contextualSignature != nil && node.TypeParameters() == nil && len(contextualSignature.parameters) > len(node.Parameters()) {
				inferenceContext := c.getInferenceContext(node)
				if checkMode&CheckModeInferential != 0 {
					c.inferFromAnnotatedParametersAndReturn(signature, contextualSignature, inferenceContext)
				}
			}
			if contextualSignature != nil && c.getReturnTypeFromAnnotation(node) == nil && signature.resolvedReturnType == nil {
				returnType := c.getReturnTypeFromBody(node, checkMode)
				if signature.resolvedReturnType == nil {
					signature.resolvedReturnType = returnType
				}
			}
			c.checkSignatureDeclaration(node)
		}
	}
}

func (c *Checker) checkFunctionExpressionOrObjectLiteralMethodDeferred(node *ast.Node) {
	functionFlags := ast.GetFunctionFlags(node)
	returnType := c.getReturnTypeFromAnnotation(node)
	c.checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnType)
	body := node.Body()
	if body != nil {
		if node.Type() == nil {
			// There are some checks that are only performed in getReturnTypeFromBody, that may produce errors
			// we need. An example is the noImplicitAny errors resulting from widening the return expression
			// of a function. Because checking of function expression bodies is deferred, there was never an
			// appropriate time to do this during the main walk of the file (see the comment at the top of
			// checkFunctionExpressionBodies). So it must be done now.
			c.getReturnTypeOfSignature(c.getSignatureFromDeclaration(node))
		}
		if ast.IsBlock(body) {
			c.checkSourceElement(body)
		} else {
			// From within an async function you can return either a non-promise value or a promise. Any
			// Promise/A+ compatible implementation will always assimilate any foreign promise, so we
			// should not be checking assignability of a promise to the return type. Instead, we need to
			// check assignability of the awaited type of the expression body against the promised type of
			// its return type annotation.
			exprType := c.checkExpression(body)
			if returnType != nil {
				returnOrPromisedType := c.unwrapReturnType(returnType, functionFlags)
				if returnOrPromisedType != nil {
					c.checkReturnExpression(node, returnOrPromisedType, body, body, exprType, false)
				}
			}
		}
	}
}

func (c *Checker) inferFromAnnotatedParametersAndReturn(sig *Signature, context *Signature, inferenceContext *InferenceContext) {
	length := len(sig.parameters) - core.IfElse(signatureHasRestParameter(sig), 1, 0)
	for i := range length {
		declaration := sig.parameters[i].ValueDeclaration
		typeNode := declaration.Type()
		if typeNode != nil {
			source := c.addOptionalityEx(c.getTypeFromTypeNode(typeNode), false /*isProperty*/, isOptionalDeclaration(declaration))
			target := c.getTypeAtPosition(context, i)
			c.inferTypes(inferenceContext.inferences, source, target, InferencePriorityNone, false)
		}
	}
	if declaration := sig.Declaration(); declaration != nil {
		if returnTypeNode := declaration.Type(); returnTypeNode != nil {
			source := c.getTypeFromTypeNode(returnTypeNode)
			target := c.getReturnTypeOfSignature(context)
			c.inferTypes(inferenceContext.inferences, source, target, InferencePriorityNone, false)
		}
	}
}

// Return the contextual signature for a given expression node. A contextual type provides a
// contextual signature if it has a single call signature and if that call signature is non-generic.
// If the contextual type is a union type, get the signature from each type possible and if they are
// all identical ignoring their return type, the result is same signature but with return type as
// union type of return types from these signatures
func (c *Checker) getContextualSignature(node *ast.Node) *Signature {
	t := c.getApparentTypeOfContextualType(node, ContextFlagsSignature)
	if t == nil {
		return nil
	}
	if t.flags&TypeFlagsUnion == 0 {
		return c.getContextualCallSignature(t, node)
	}
	var signatureList []*Signature
	types := t.Types()
	for _, current := range types {
		signature := c.getContextualCallSignature(current, node)
		if signature != nil {
			if len(signatureList) != 0 && c.compareSignaturesIdentical(signatureList[0], signature, false /*partialMatch*/, true /*ignoreThisTypes*/, true /*ignoreReturnTypes*/, c.compareTypesIdentical) == TernaryFalse {
				// Signatures aren't identical, do not use
				return nil
			}
			// Use this signature for contextual union signature
			signatureList = append(signatureList, signature)
		}
	}
	switch len(signatureList) {
	case 0:
		return nil
	case 1:
		return signatureList[0]
	}
	// Result is union of signatures collected (return type is union of return types of this signature set)
	return c.createUnionSignature(signatureList[0], signatureList)
}

func (c *Checker) createUnionSignature(sig *Signature, unionSignatures []*Signature) *Signature {
	result := c.cloneSignature(sig)
	result.composite = &CompositeSignature{isUnion: true, signatures: unionSignatures}
	result.target = nil
	result.mapper = nil
	return result
}

// If the given type is an object or union type with a single signature, and if that signature has at
// least as many parameters as the given function, return the signature. Otherwise return undefined.
func (c *Checker) getContextualCallSignature(t *Type, node *ast.Node) *Signature {
	signatures := c.getSignaturesOfType(t, SignatureKindCall)
	applicableByArity := core.Filter(signatures, func(s *Signature) bool { return !c.isAritySmaller(s, node) })
	if len(applicableByArity) == 1 {
		return applicableByArity[0]
	}
	return c.getIntersectedSignatures(applicableByArity)
}

func (c *Checker) getIntersectedSignatures(signatures []*Signature) *Signature {
	if !c.noImplicitAny {
		return nil
	}
	var combined *Signature
	for _, sig := range signatures {
		switch {
		case combined == sig || combined == nil:
			combined = sig
		case c.compareTypeParametersIdentical(combined.typeParameters, sig.typeParameters):
			combined = c.combineUnionOrIntersectionMemberSignatures(combined, sig, false /*isUnion*/)
		default:
			return nil
		}
	}
	return combined
}

/** If the contextual signature has fewer parameters than the function expression, do not use it */
func (c *Checker) isAritySmaller(signature *Signature, target *ast.Node) bool {
	parameters := target.Parameters()
	targetParameterCount := 0
	for targetParameterCount < len(parameters) {
		param := parameters[targetParameterCount]
		if param.Initializer() != nil || param.QuestionToken() != nil || hasDotDotDotToken(param) {
			break
		}
		targetParameterCount++
	}
	if len(parameters) != 0 && ast.IsThisParameter(parameters[0]) {
		targetParameterCount--
	}
	return !c.hasEffectiveRestParameter(signature) && c.getParameterCount(signature) < targetParameterCount
}

func (c *Checker) assignContextualParameterTypes(sig *Signature, context *Signature) {
	if len(context.typeParameters) != 0 {
		if len(sig.typeParameters) != 0 {
			// This signature has already has a contextual inference performed and cached on it
			return
		}
		sig.typeParameters = context.typeParameters
	}
	if context.thisParameter != nil {
		parameter := sig.thisParameter
		if parameter == nil || parameter.ValueDeclaration != nil && parameter.ValueDeclaration.Type() == nil {
			if parameter == nil {
				sig.thisParameter = c.createSymbolWithType(context.thisParameter, nil /*type*/)
			}
			c.assignParameterType(sig.thisParameter, c.getTypeOfSymbol(context.thisParameter))
		}
	}
	length := len(sig.parameters) - core.IfElse(signatureHasRestParameter(sig), 1, 0)
	for i := range length {
		parameter := sig.parameters[i]
		declaration := parameter.ValueDeclaration
		if declaration.Type() == nil {
			t := c.tryGetTypeAtPosition(context, i)
			if t != nil && declaration.Initializer() != nil {
				initializerType := c.checkDeclarationInitializer(declaration, CheckModeNormal, nil)
				if !c.isTypeAssignableTo(initializerType, t) {
					initializerType = c.widenTypeInferredFromInitializer(declaration, initializerType)
					if c.isTypeAssignableTo(t, initializerType) {
						t = initializerType
					}
				}
			}
			c.assignParameterType(parameter, t)
		}
	}
	if signatureHasRestParameter(sig) {
		// parameter might be a transient symbol generated by use of `arguments` in the function body.
		parameter := core.LastOrNil(sig.parameters)
		if parameter.ValueDeclaration != nil && parameter.ValueDeclaration.Type() == nil ||
			parameter.ValueDeclaration == nil && parameter.CheckFlags&ast.CheckFlagsDeferredType != 0 {
			contextualParameterType := c.getRestTypeAtPosition(context, length, false)
			c.assignParameterType(parameter, contextualParameterType)
		}
	}
}

func (c *Checker) assignNonContextualParameterTypes(signature *Signature) {
	if signature.thisParameter != nil {
		c.assignParameterType(signature.thisParameter, nil)
	}
	for _, parameter := range signature.parameters {
		c.assignParameterType(parameter, nil)
	}
}

func (c *Checker) assignParameterType(parameter *ast.Symbol, contextualType *Type) {
	links := c.valueSymbolLinks.Get(parameter)
	if links.resolvedType != nil {
		return
	}
	declaration := parameter.ValueDeclaration
	t := contextualType
	if t == nil {
		if declaration != nil {
			t = c.getWidenedTypeForVariableLikeDeclaration(declaration, true /*reportErrors*/)
		} else {
			t = c.getTypeOfSymbol(parameter)
		}
	}
	links.resolvedType = c.addOptionalityEx(t, false, declaration != nil && declaration.Initializer() == nil && isOptionalDeclaration(declaration))
	if declaration != nil && !ast.IsIdentifier(declaration.Name()) {
		// if inference didn't come up with anything but unknown, fall back to the binding pattern if present.
		if links.resolvedType == c.unknownType {
			links.resolvedType = c.getTypeFromBindingPattern(declaration.Name(), false, false)
		}
		c.assignBindingElementTypes(declaration.Name(), links.resolvedType)
	}
}

// When contextual typing assigns a type to a parameter that contains a binding pattern, we also need to push
// the destructured type into the contained binding elements.
func (c *Checker) assignBindingElementTypes(pattern *ast.Node, parentType *Type) {
	for _, element := range pattern.Elements() {
		name := element.Name()
		if name != nil {
			t := c.getBindingElementTypeFromParentType(element, parentType, false /*noTupleBoundsCheck*/)
			if ast.IsIdentifier(name) {
				c.valueSymbolLinks.Get(c.getSymbolOfDeclaration(element)).resolvedType = t
			} else {
				c.assignBindingElementTypes(name, t)
			}
		}
	}
}

func (c *Checker) checkCollisionsForDeclarationName(node *ast.Node, name *ast.Node) {
	if name == nil {
		return
	}
	c.checkCollisionWithRequireExportsInGeneratedCode(node, name)
	c.checkCollisionWithGlobalObjectInGeneratedCode(node, name)
	c.checkCollisionWithGlobalPromiseInGeneratedCode(node, name)
	c.recordPotentialCollisionWithWeakMapSetInGeneratedCode(node, name)
	c.recordPotentialCollisionWithReflectInGeneratedCode(node, name)
	if ast.IsClassLike(node) {
		c.checkTypeNameIsReserved(name, diagnostics.Class_name_cannot_be_0)
		if node.Flags&ast.NodeFlagsAmbient == 0 {
			c.checkClassNameCollisionWithObject(name)
		}
	} else if ast.IsEnumDeclaration(node) {
		c.checkTypeNameIsReserved(name, diagnostics.Enum_name_cannot_be_0)
	}
}

func (c *Checker) checkCollisionWithRequireExportsInGeneratedCode(node *ast.Node, name *ast.Node) {
	// No need to check for require or exports for ES6 modules and later
	if c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(node)) >= core.ModuleKindES2015 {
		return
	}
	if name == nil || !c.needCollisionCheckForIdentifier(node, name, "require") && !c.needCollisionCheckForIdentifier(node, name, "exports") {
		return
	}
	// Uninstantiated modules shouldnt do this check
	if ast.IsModuleDeclaration(node) && ast.GetModuleInstanceState(node) != ast.ModuleInstanceStateInstantiated {
		return
	}
	// In case of variable declaration, node.parent is variable statement so look at the variable statement's parent
	parent := ast.GetDeclarationContainer(node)
	if ast.IsSourceFile(parent) && ast.IsExternalOrCommonJSModule(parent.AsSourceFile()) {
		// If the declaration happens to be in external module, report error that require and exports are reserved keywords
		c.errorSkippedOnNoEmit(name, diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module, scanner.DeclarationNameToString(name), scanner.DeclarationNameToString(name))
	}
}

func (c *Checker) checkCollisionWithGlobalObjectInGeneratedCode(node *ast.Node, name *ast.Node) {
	if name == nil || ast.IsClassLike(node) || !c.needCollisionCheckForIdentifier(node, name, "Object") {
		return
	}
	// Uninstantiated modules shouldn't do this check
	if ast.IsModuleDeclaration(node) && ast.GetModuleInstanceState(node) != ast.ModuleInstanceStateInstantiated {
		return
	}
	// In case of variable declaration, node.parent is variable statement so look at the variable statement's parent
	parent := ast.GetDeclarationContainer(node)
	if ast.IsSourceFile(parent) && ast.IsExternalOrCommonJSModule(parent.AsSourceFile()) && c.program.GetEmitModuleFormatOfFile(parent.AsSourceFile()) == core.ModuleKindCommonJS {
		// If the declaration happens to be in external module, report error that Object is a reserved identifier.
		c.errorSkippedOnNoEmit(name, diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module, scanner.DeclarationNameToString(name), scanner.DeclarationNameToString(name))
	}
}

func (c *Checker) needCollisionCheckForIdentifier(node *ast.Node, identifier *ast.Node, name string) bool {
	if identifier != nil && identifier.Text() != name {
		return false
	}
	switch node.Kind {
	case ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindMethodDeclaration, ast.KindMethodSignature,
		ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyAssignment:
		// it is ok to have member named '_super', '_this', `Promise`, etc. - member access is always qualified
		return false
	}
	if node.Flags&ast.NodeFlagsAmbient != 0 {
		// ambient context - no codegen impact
		return false
	}
	if ast.IsImportClause(node) || ast.IsImportEqualsDeclaration(node) || ast.IsImportSpecifier(node) {
		// type-only imports do not require collision checks against runtime values.
		if ast.IsTypeOnlyImportOrExportDeclaration(node) {
			return false
		}
	}
	root := ast.GetRootDeclaration(node)
	if ast.IsParameterDeclaration(root) && ast.NodeIsMissing(root.Parent.Body()) {
		// just an overload - no codegen impact
		return false
	}
	return true
}

func (c *Checker) setNodeLinksForPrivateIdentifierScope(node *ast.Node) {
	if name := node.Name(); ast.IsPrivateIdentifier(name) {
		if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks ||
			c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators ||
			!c.compilerOptions.GetUseDefineForClassFields() {
			for lexicalScope := ast.GetEnclosingBlockScopeContainer(node); lexicalScope != nil; lexicalScope = ast.GetEnclosingBlockScopeContainer(lexicalScope) {
				c.nodeLinks.Get(lexicalScope).flags |= NodeCheckFlagsContainsClassWithPrivateIdentifiers
			}
		}
	}
}

func (c *Checker) recordPotentialCollisionWithWeakMapSetInGeneratedCode(node *ast.Node, name *ast.Node) {
	if c.languageVersion <= core.ScriptTargetES2021 &&
		(c.needCollisionCheckForIdentifier(node, name, "WeakMap") || c.needCollisionCheckForIdentifier(node, name, "WeakSet")) {
		c.addDeferredDiagnostic(func() {
			c.checkWeakMapSetCollision(node)
		})
	}
}

func (c *Checker) checkWeakMapSetCollision(node *ast.Node) {
	enclosingBlockScope := ast.GetEnclosingBlockScopeContainer(node)
	if c.nodeLinks.Get(enclosingBlockScope).flags&NodeCheckFlagsContainsClassWithPrivateIdentifiers != 0 {
		name := node.Name()
		if name != nil && ast.IsIdentifier(name) {
			c.errorSkippedOnNoEmit(node, diagnostics.Compiler_reserves_name_0_when_emitting_private_identifier_downlevel, name.Text())
		}
	}
}

func (c *Checker) checkCollisionWithGlobalPromiseInGeneratedCode(node *ast.Node, name *ast.Node) {
	if name == nil || c.languageVersion >= core.ScriptTargetES2017 || !c.needCollisionCheckForIdentifier(node, name, "Promise") {
		return
	}
	// Uninstantiated modules shouldn't do this check
	if ast.IsModuleDeclaration(node) && ast.GetModuleInstanceState(node) != ast.ModuleInstanceStateInstantiated {
		return
	}
	// In case of variable declaration, node.parent is variable statement so look at the variable statement's parent
	parent := ast.GetDeclarationContainer(node)
	if ast.IsSourceFile(parent) && ast.IsExternalOrCommonJSModule(parent.AsSourceFile()) && parent.Flags&ast.NodeFlagsHasAsyncFunctions != 0 {
		// If the declaration happens to be in external module, report error that Promise is a reserved identifier.
		c.errorSkippedOnNoEmit(name, diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_a_module_containing_async_functions, scanner.DeclarationNameToString(name), scanner.DeclarationNameToString(name))
	}
}

func (c *Checker) recordPotentialCollisionWithReflectInGeneratedCode(node *ast.Node, name *ast.Node) {
	if name != nil && c.languageVersion <= core.ScriptTargetES2021 && c.needCollisionCheckForIdentifier(node, name, "Reflect") {
		c.addDeferredDiagnostic(func() {
			c.checkReflectCollision(node)
		})
	}
}

func (c *Checker) checkReflectCollision(node *ast.Node) {
	hasCollision := false
	if ast.IsClassExpression(node) {
		// ClassExpression names don't contribute to their containers, but do matter for any of their block-scoped members.
		for _, member := range node.Members() {
			if c.nodeLinks.Get(member).flags&NodeCheckFlagsContainsSuperPropertyInStaticInitializer != 0 {
				hasCollision = true
				break
			}
		}
	} else if ast.IsFunctionExpression(node) {
		// FunctionExpression names don't contribute to their containers, but do matter for their contents
		if c.nodeLinks.Get(node).flags&NodeCheckFlagsContainsSuperPropertyInStaticInitializer != 0 {
			hasCollision = true
		}
	} else {
		container := ast.GetEnclosingBlockScopeContainer(node)
		if container != nil && c.nodeLinks.Get(container).flags&NodeCheckFlagsContainsSuperPropertyInStaticInitializer != 0 {
			hasCollision = true
		}
	}
	if hasCollision {
		name := node.Name()
		if name != nil && ast.IsIdentifier(name) {
			c.errorSkippedOnNoEmit(node, diagnostics.Duplicate_identifier_0_Compiler_reserves_name_1_when_emitting_super_references_in_static_initializers, scanner.DeclarationNameToString(name), "Reflect")
		}
	}
}

func (c *Checker) checkClassNameCollisionWithObject(name *ast.Node) {
	if name.Text() == "Object" && c.program.GetEmitModuleFormatOfFile(ast.GetSourceFileOfNode(name)) < core.ModuleKindES2015 {
		c.error(name, diagnostics.Class_name_cannot_be_Object_when_targeting_ES5_and_above_with_module_0, c.moduleKind.String())
	}
}

func (c *Checker) checkTypeOfExpression(node *ast.Node) *Type {
	c.checkExpression(node.Expression())
	return c.typeofType
}

func (c *Checker) checkNonNullAssertion(node *ast.Node) *Type {
	if node.Flags&ast.NodeFlagsOptionalChain != 0 {
		return c.checkNonNullChain(node)
	}
	return c.GetNonNullableType(c.checkExpression(node.Expression()))
}

func (c *Checker) checkNonNullChain(node *ast.Node) *Type {
	leftType := c.checkExpression(node.Expression())
	nonOptionalType := c.getOptionalExpressionType(leftType, node.Expression())
	return c.propagateOptionalTypeMarker(c.GetNonNullableType(nonOptionalType), node, nonOptionalType != leftType)
}

func (c *Checker) checkExpressionWithTypeArguments(node *ast.Node) *Type {
	c.checkGrammarExpressionWithTypeArguments(node)
	c.checkSourceElements(node.TypeArguments())
	if ast.IsExpressionWithTypeArguments(node) {
		parent := ast.WalkUpParenthesizedExpressions(node.Parent)
		if ast.IsBinaryExpression(parent) && parent.AsBinaryExpression().OperatorToken.Kind == ast.KindInstanceOfKeyword && isNodeDescendantOf(node, parent.AsBinaryExpression().Right) {
			c.error(node, diagnostics.The_right_hand_side_of_an_instanceof_expression_must_not_be_an_instantiation_expression)
		}
	}
	var exprType *Type
	if ast.IsExpressionWithTypeArguments(node) {
		exprType = c.checkExpression(node.Expression())
	} else {
		exprName := node.AsTypeQueryNode().ExprName
		if ast.IsThisIdentifier(exprName) {
			exprType = c.checkThisExpression(node.AsTypeQueryNode().ExprName)
		} else {
			exprType = c.checkExpression(node.AsTypeQueryNode().ExprName)
		}
	}
	return c.getInstantiationExpressionType(exprType, node)
}

func (c *Checker) getInstantiationExpressionType(exprType *Type, node *ast.Node) *Type {
	typeArguments := node.TypeArgumentList()
	if exprType == c.silentNeverType || c.isErrorType(exprType) || typeArguments == nil {
		return exprType
	}
	key := InstantiationExpressionKey{nodeId: ast.GetNodeId(node), typeId: exprType.id}
	if cached := c.instantiationExpressionTypes[key]; cached != nil {
		return cached
	}
	hasSomeApplicableSignature := false
	var nonApplicableType *Type
	getInstantiatedSignatures := func(signatures []*Signature) []*Signature {
		applicableSignatures := core.Filter(signatures, func(sig *Signature) bool {
			return len(sig.typeParameters) != 0 && c.hasCorrectTypeArgumentArity(sig, typeArguments.Nodes)
		})
		return core.SameMap(applicableSignatures, func(sig *Signature) *Signature {
			typeArgumentTypes := c.checkTypeArguments(sig, typeArguments.Nodes, true /*reportErrors*/, nil)
			if typeArgumentTypes != nil {
				return c.getSignatureInstantiation(sig, typeArgumentTypes, ast.IsInJSFile(sig.declaration), nil)
			}
			return sig
		})
	}
	var getInstantiatedType func(*Type) *Type
	getInstantiatedType = func(t *Type) *Type {
		hasSignatures := false
		hasApplicableSignature := false
		var getInstantiatedTypePart func(*Type) *Type
		getInstantiatedTypePart = func(t *Type) *Type {
			if t.flags&TypeFlagsObject != 0 {
				resolved := c.resolveStructuredTypeMembers(t)
				callSignatures := getInstantiatedSignatures(resolved.CallSignatures())
				constructSignatures := getInstantiatedSignatures(resolved.ConstructSignatures())
				hasSignatures = hasSignatures || len(resolved.CallSignatures()) != 0 || len(resolved.ConstructSignatures()) != 0
				hasApplicableSignature = hasApplicableSignature || len(callSignatures) != 0 || len(constructSignatures) != 0
				if !core.Same(callSignatures, resolved.CallSignatures()) || !core.Same(constructSignatures, resolved.ConstructSignatures()) {
					result := c.newObjectType(ObjectFlagsAnonymous|ObjectFlagsInstantiationExpressionType, c.newSymbol(ast.SymbolFlagsNone, ast.InternalSymbolNameInstantiationExpression))
					c.setStructuredTypeMembers(result, resolved.members, callSignatures, constructSignatures, resolved.indexInfos)
					result.AsInstantiationExpressionType().node = node
					return result
				}
			} else if t.flags&TypeFlagsInstantiableNonPrimitive != 0 {
				constraint := c.getBaseConstraintOfType(t)
				if constraint != nil {
					instantiated := getInstantiatedTypePart(constraint)
					if instantiated != constraint {
						return instantiated
					}
				}
			} else if t.flags&TypeFlagsUnion != 0 {
				return c.mapType(t, getInstantiatedType)
			} else if t.flags&TypeFlagsIntersection != 0 {
				return c.getIntersectionType(core.SameMap(t.AsIntersectionType().types, getInstantiatedTypePart))
			}
			return t
		}
		result := getInstantiatedTypePart(t)
		hasSomeApplicableSignature = hasSomeApplicableSignature || hasApplicableSignature
		if hasSignatures && !hasApplicableSignature {
			if nonApplicableType == nil {
				nonApplicableType = t
			}
		}
		return result
	}
	result := getInstantiatedType(exprType)
	c.instantiationExpressionTypes[key] = result
	var errorType *Type
	if hasSomeApplicableSignature {
		errorType = nonApplicableType
	} else {
		errorType = exprType
	}
	if errorType != nil {
		sourceFile := ast.GetSourceFileOfNode(node)
		loc := core.NewTextRange(scanner.SkipTrivia(sourceFile.Text(), typeArguments.Pos()), typeArguments.End())
		c.addDiagnostic(ast.NewDiagnostic(sourceFile, loc, diagnostics.Type_0_has_no_signatures_for_which_the_type_argument_list_is_applicable, c.TypeToString(errorType)))
	}
	return result
}

func (c *Checker) checkSatisfiesExpression(node *ast.Node) *Type {
	typeNode := node.Type()
	c.checkSourceElement(typeNode)
	exprType := c.checkExpression(node.Expression())
	targetType := c.getTypeFromTypeNode(typeNode)
	if c.isErrorType(targetType) {
		return targetType
	}
	c.checkTypeAssignableToAndOptionallyElaborate(exprType, targetType, node, node.Expression(), diagnostics.Type_0_does_not_satisfy_the_expected_type_1, nil)
	return exprType
}

func (c *Checker) checkMetaProperty(node *ast.Node) *Type {
	c.checkGrammarMetaProperty(node.AsMetaProperty())
	switch node.AsMetaProperty().KeywordToken {
	case ast.KindNewKeyword:
		return c.checkNewTargetMetaProperty(node)
	case ast.KindImportKeyword:
		if node.Name().Text() == "defer" {
			debug.Assert(!ast.IsCallExpression(node.Parent) || node.Parent.Expression() != node, "Trying to get the type of `import.defer` in `import.defer(...)`")
			return c.errorType
		}
		return c.checkImportMetaProperty(node)
	}
	panic("Unhandled case in checkMetaProperty")
}

func (c *Checker) checkNewTargetMetaProperty(node *ast.Node) *Type {
	container := ast.GetNewTargetContainer(node)
	if container == nil {
		c.error(node, diagnostics.Meta_property_0_is_only_allowed_in_the_body_of_a_function_declaration_function_expression_or_constructor, "new.target")
		return c.errorType
	}
	if ast.IsConstructorDeclaration(container) {
		symbol := c.getSymbolOfDeclaration(container.Parent)
		return c.getTypeOfSymbol(symbol)
	}
	symbol := c.getSymbolOfDeclaration(container)
	return c.getTypeOfSymbol(symbol)
}

func (c *Checker) checkImportMetaProperty(node *ast.Node) *Type {
	if core.ModuleKindNode16 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext {
		sourceFileMetaData := c.program.GetSourceFileMetaData(ast.GetSourceFileOfNode(node).Path())
		if sourceFileMetaData.ImpliedNodeFormat != core.ModuleKindESNext {
			c.error(node, diagnostics.The_import_meta_meta_property_is_not_allowed_in_files_which_will_build_into_CommonJS_output)
		}
	} else if c.moduleKind < core.ModuleKindES2020 && c.moduleKind != core.ModuleKindSystem {
		c.error(node, diagnostics.The_import_meta_meta_property_is_only_allowed_when_the_module_option_is_es2020_es2022_esnext_system_node16_node18_node20_or_nodenext)
	}
	file := ast.GetSourceFileOfNode(node)
	debug.Assert(file.Flags&ast.NodeFlagsPossiblyContainsImportMeta != 0, "Containing file is missing import meta node flag.")
	if node.Name().Text() == "meta" {
		return c.getGlobalImportMetaType()
	}
	return c.errorType
}

func (c *Checker) checkMetaPropertyKeyword(node *ast.Node) *Type {
	// !!! This is effectively a helper for GetSymbolAtLocation and GetTypeAtLocation
	return c.errorType
}

func (c *Checker) checkDeleteExpression(node *ast.Node) *Type {
	c.checkExpression(node.Expression())
	expr := ast.SkipParentheses(node.Expression())
	if !ast.IsAccessExpression(expr) {
		c.error(expr, diagnostics.The_operand_of_a_delete_operator_must_be_a_property_reference)
		return c.booleanType
	}
	if ast.IsPropertyAccessExpression(expr) && ast.IsPrivateIdentifier(expr.Name()) {
		c.error(expr, diagnostics.The_operand_of_a_delete_operator_cannot_be_a_private_identifier)
	}
	symbol := c.getExportSymbolOfValueSymbolIfExported(c.getResolvedSymbolOrNil(expr))
	if symbol != nil {
		if c.isReadonlySymbol(symbol) {
			c.error(expr, diagnostics.The_operand_of_a_delete_operator_cannot_be_a_read_only_property)
		} else {
			c.checkDeleteExpressionMustBeOptional(expr, symbol)
		}
	}
	return c.booleanType
}

func (c *Checker) checkDeleteExpressionMustBeOptional(expr *ast.Node, symbol *ast.Symbol) {
	t := c.getTypeOfSymbol(symbol)
	if c.strictNullChecks && t.flags&(TypeFlagsAnyOrUnknown|TypeFlagsNever) == 0 {
		var isOptional bool
		if c.exactOptionalPropertyTypes {
			isOptional = symbol.Flags&ast.SymbolFlagsOptional != 0
		} else {
			isOptional = c.hasTypeFacts(t, TypeFactsIsUndefined)
		}
		if !isOptional {
			c.error(expr, diagnostics.The_operand_of_a_delete_operator_must_be_optional)
		}
	}
}

func (c *Checker) checkVoidExpression(node *ast.Node) *Type {
	c.checkNodeDeferred(node)
	return c.undefinedWideningType
}

func (c *Checker) checkAwaitExpression(node *ast.Node) *Type {
	c.checkGrammarAwaitOrAwaitUsing(node)
	operandType := c.checkExpression(node.Expression())
	awaitedType := c.checkAwaitedType(operandType, true /*withAlias*/, node, diagnostics.Type_of_await_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
	if awaitedType == operandType && !c.isErrorType(awaitedType) && operandType.flags&TypeFlagsAnyOrUnknown == 0 {
		c.addErrorOrSuggestion(false, createDiagnosticForNode(node, diagnostics.X_await_has_no_effect_on_the_type_of_this_expression))
	}
	return awaitedType
}

func (c *Checker) checkPrefixUnaryExpression(node *ast.Node) *Type {
	expr := node.AsPrefixUnaryExpression()
	operandType := c.checkExpression(expr.Operand)
	if operandType == c.silentNeverType {
		return c.silentNeverType
	}
	switch expr.Operand.Kind {
	case ast.KindNumericLiteral:
		switch expr.Operator {
		case ast.KindMinusToken:
			return c.getFreshTypeOfLiteralType(c.getNumberLiteralType(-jsnum.FromString(expr.Operand.Text())))
		case ast.KindPlusToken:
			return c.getFreshTypeOfLiteralType(c.getNumberLiteralType(+jsnum.FromString(expr.Operand.Text())))
		}
	case ast.KindBigIntLiteral:
		if expr.Operator == ast.KindMinusToken {
			return c.getFreshTypeOfLiteralType(c.getBigIntLiteralType(jsnum.NewPseudoBigInt(jsnum.ParsePseudoBigInt(expr.Operand.Text()), true /*negative*/)))
		}
	}
	switch expr.Operator {
	case ast.KindPlusToken, ast.KindMinusToken, ast.KindTildeToken:
		c.checkNonNullType(operandType, expr.Operand)
		if c.maybeTypeOfKindConsideringBaseConstraint(operandType, TypeFlagsESSymbolLike) {
			c.error(expr.Operand, diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, scanner.TokenToString(expr.Operator))
		}
		if expr.Operator == ast.KindPlusToken {
			if c.maybeTypeOfKindConsideringBaseConstraint(operandType, TypeFlagsBigIntLike) {
				c.error(expr.Operand, diagnostics.Operator_0_cannot_be_applied_to_type_1, scanner.TokenToString(expr.Operator), c.TypeToString(c.getBaseTypeOfLiteralType(operandType)))
			}
			return c.numberType
		}
		return c.getUnaryResultType(operandType)
	case ast.KindExclamationToken:
		c.checkTruthinessOfType(operandType, expr.Operand)
		facts := c.getTypeFacts(operandType, TypeFactsTruthy|TypeFactsFalsy)
		switch {
		case facts == TypeFactsTruthy:
			return c.falseType
		case facts == TypeFactsFalsy:
			return c.trueType
		default:
			return c.booleanType
		}
	case ast.KindPlusPlusToken, ast.KindMinusMinusToken:
		ok := c.checkArithmeticOperandType(expr.Operand, c.checkNonNullType(operandType, expr.Operand), diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type, false)
		if ok {
			// run check only if former checks succeeded to avoid reporting cascading errors
			c.checkReferenceExpression(expr.Operand, diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access)
		}
		return c.getUnaryResultType(operandType)
	}
	return c.errorType
}

func (c *Checker) checkPostfixUnaryExpression(node *ast.Node) *Type {
	expr := node.AsPostfixUnaryExpression()
	operandType := c.checkExpression(expr.Operand)
	if operandType == c.silentNeverType {
		return c.silentNeverType
	}
	ok := c.checkArithmeticOperandType(expr.Operand, c.checkNonNullType(operandType, expr.Operand), diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type, false)
	if ok {
		// run check only if former checks succeeded to avoid reporting cascading errors
		c.checkReferenceExpression(expr.Operand, diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access)
	}
	return c.getUnaryResultType(operandType)
}

func (c *Checker) getUnaryResultType(operandType *Type) *Type {
	if c.maybeTypeOfKind(operandType, TypeFlagsBigIntLike) {
		if c.isTypeAssignableToKind(operandType, TypeFlagsAnyOrUnknown) || c.maybeTypeOfKind(operandType, TypeFlagsNumberLike) {
			return c.numberOrBigIntType
		}
		return c.bigintType
	}
	// If it's not a bigint type, implicit coercion will result in a number
	return c.numberType
}

func (c *Checker) checkConditionalExpression(node *ast.Node, checkMode CheckMode) *Type {
	cond := node.AsConditionalExpression()
	t := c.checkTruthinessExpression(cond.Condition, checkMode)
	c.checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(cond.Condition, t, cond.WhenTrue)
	type1 := c.checkExpressionEx(cond.WhenTrue, checkMode)
	type2 := c.checkExpressionEx(cond.WhenFalse, checkMode)
	return c.getUnionTypeEx([]*Type{type1, type2}, UnionReductionSubtype, nil, nil)
}

func (c *Checker) checkTruthinessExpression(node *ast.Node, checkMode CheckMode) *Type {
	return c.checkTruthinessOfType(c.checkExpressionEx(node, checkMode), node)
}

func (c *Checker) checkSpreadExpression(node *ast.Node, checkMode CheckMode) *Type {
	arrayOrIterableType := c.checkExpressionEx(node.Expression(), checkMode)
	return c.checkIteratedTypeOrElementType(IterationUseSpread, arrayOrIterableType, c.undefinedType, node.Expression())
}

func (c *Checker) checkYieldExpression(node *ast.Node) *Type {
	c.checkGrammarYieldExpression(node)
	fn := ast.GetContainingFunction(node)
	if fn == nil {
		return c.anyType
	}
	functionFlags := ast.GetFunctionFlags(fn)
	if functionFlags&ast.FunctionFlagsGenerator == 0 {
		// If the user's code is syntactically correct, the func should always have a star. After all, we are in a yield context.
		return c.anyType
	}
	isAsync := (functionFlags & ast.FunctionFlagsAsync) != 0
	if node.AsYieldExpression().AsteriskToken != nil {
		// Async generator functions prior to ES2018 require the __await, __asyncDelegator,
		// and __asyncValues helpers
		if isAsync && c.languageVersion < LanguageFeatureMinimumTarget.AsyncGenerators {
			c.checkExternalEmitHelpers(node, ExternalEmitHelpersAsyncDelegatorIncludes)
		}
	}
	// There is no point in doing an assignability check if the function
	// has no explicit return type because the return type is directly computed
	// from the yield expressions.
	returnType := c.getReturnTypeFromAnnotation(fn)
	if returnType != nil && returnType.flags&TypeFlagsUnion != 0 {
		returnType = c.filterType(returnType, func(t *Type) bool {
			return c.checkGeneratorInstantiationAssignabilityToReturnType(t, functionFlags, nil /*errorNode*/)
		})
	}
	var iterationTypes IterationTypes
	if returnType != nil {
		iterationTypes = c.getIterationTypesOfGeneratorFunctionReturnType(returnType, isAsync)
	}
	signatureYieldType := core.OrElse(iterationTypes.yieldType, c.anyType)
	signatureNextType := core.OrElse(iterationTypes.nextType, c.anyType)
	var yieldExpressionType *Type
	if node.Expression() != nil {
		yieldExpressionType = c.checkExpression(node.Expression())
	} else {
		yieldExpressionType = c.undefinedWideningType
	}
	yieldedType := c.getYieldedTypeOfYieldExpression(node, yieldExpressionType, signatureNextType, isAsync)
	if returnType != nil && yieldedType != nil {
		c.checkTypeAssignableToAndOptionallyElaborate(yieldedType, signatureYieldType, core.OrElse(node.Expression(), node), node.Expression(), nil, nil)
	}
	if node.AsYieldExpression().AsteriskToken != nil {
		use := core.IfElse(isAsync, IterationUseAsyncYieldStar, IterationUseYieldStar)
		return core.OrElse(c.getIterationTypeOfIterable(use, IterationTypeKindReturn, yieldExpressionType, node.Expression()), c.anyType)
	}
	if returnType != nil {
		return core.OrElse(c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindNext, returnType, isAsync), c.anyType)
	}
	t := c.getContextualIterationType(IterationTypeKindNext, fn)
	if t == nil {
		t = c.anyType
		if c.noImplicitAny && !expressionResultIsUnused(node) {
			contextualType := c.getContextualType(node, ContextFlagsNone)
			if contextualType == nil || IsTypeAny(contextualType) {
				c.error(node, diagnostics.X_yield_expression_implicitly_results_in_an_any_type_because_its_containing_generator_lacks_a_return_type_annotation)
			}
		}
	}
	return t
}

func (c *Checker) getYieldedTypeOfYieldExpression(node *ast.Node, expressionType *Type, sentType *Type, isAsync bool) *Type {
	errorNode := core.OrElse(node.Expression(), node)
	isYieldStar := node.AsYieldExpression().AsteriskToken != nil
	// A `yield*` expression effectively yields everything that its operand yields
	yieldedType := expressionType
	if isYieldStar {
		yieldedType = c.checkIteratedTypeOrElementType(core.IfElse(isAsync, IterationUseAsyncYieldStar, IterationUseYieldStar), expressionType, sentType, errorNode)
	}
	if !isAsync {
		return yieldedType
	}
	return c.getAwaitedTypeEx(yieldedType, errorNode, core.IfElse(isYieldStar,
		diagnostics.Type_of_iterated_elements_of_a_yield_Asterisk_operand_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member,
		diagnostics.Type_of_yield_operand_in_an_async_generator_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member))
}

func (c *Checker) checkSyntheticExpression(node *ast.Node) *Type {
	t := node.AsSyntheticExpression().Type.(*Type)
	if node.AsSyntheticExpression().IsSpread {
		return c.getIndexedAccessType(t, c.numberType)
	}
	return t
}

func (c *Checker) checkIdentifier(node *ast.Node, checkMode CheckMode) *Type {
	if ast.IsThisInTypeQuery(node) {
		return c.checkThisExpression(node)
	}
	symbol := c.getResolvedSymbol(node)
	if symbol == c.unknownSymbol {
		return c.errorType
	}
	if symbol == c.argumentsSymbol {
		if c.isInPropertyInitializerOrClassStaticBlock(node, true /*ignoreArrowFunctions*/) {
			c.error(node, diagnostics.X_arguments_cannot_be_referenced_in_property_initializers_or_class_static_initialization_blocks)
			return c.errorType
		}
		return c.getTypeOfSymbol(symbol)
	}
	if shouldMarkIdentifierAliasReferenced(node) {
		c.markLinkedReferences(node, ReferenceHintIdentifier, nil /*propSymbol*/, nil /*parentType*/)
	}
	localOrExportSymbol := c.getExportSymbolOfValueSymbolIfExported(symbol)
	targetSymbol := c.resolveAliasWithDeprecationCheck(localOrExportSymbol, node)
	if len(targetSymbol.Declarations) != 0 && c.isDeprecatedSymbol(targetSymbol) && c.isUncalledFunctionReference(node, targetSymbol) {
		c.addDeprecatedSuggestion(node, targetSymbol.Declarations, node.Text())
	}
	declaration := localOrExportSymbol.ValueDeclaration
	immediateDeclaration := declaration
	// If the identifier is declared in a binding pattern for which we're currently computing the implied type and the
	// reference occurs with the same binding pattern, return the non-inferrable any type. This for example occurs in
	// 'const [a, b = a + 1] = [2]' when we're computing the contextual type for the array literal '[2]'.
	if declaration != nil && declaration.Kind == ast.KindBindingElement && slices.Contains(c.contextualBindingPatterns, declaration.Parent) &&
		ast.FindAncestor(node, func(parent *ast.Node) bool { return parent == declaration.Parent }) != nil {
		return c.nonInferrableAnyType
	}
	t := c.getNarrowedTypeOfSymbol(localOrExportSymbol, node)
	assignmentKind := getAssignmentTargetKind(node)
	if assignmentKind != AssignmentKindNone {
		if localOrExportSymbol.Flags&ast.SymbolFlagsVariable == 0 && !(ast.IsInJSFile(node) && localOrExportSymbol.Flags&ast.SymbolFlagsValueModule != 0) {
			var assignmentError *diagnostics.Message
			switch {
			case localOrExportSymbol.Flags&ast.SymbolFlagsEnum != 0:
				assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_an_enum
			case localOrExportSymbol.Flags&ast.SymbolFlagsClass != 0:
				assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_a_class
			case localOrExportSymbol.Flags&ast.SymbolFlagsModule != 0:
				assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_a_namespace
			case localOrExportSymbol.Flags&ast.SymbolFlagsFunction != 0:
				assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_a_function
			case localOrExportSymbol.Flags&ast.SymbolFlagsAlias != 0:
				assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_an_import
			default:
				assignmentError = diagnostics.Cannot_assign_to_0_because_it_is_not_a_variable
			}
			c.error(node, assignmentError, c.symbolToString(symbol))
			return c.errorType
		}
		if c.isReadonlySymbol(localOrExportSymbol) {
			if localOrExportSymbol.Flags&ast.SymbolFlagsVariable != 0 {
				c.error(node, diagnostics.Cannot_assign_to_0_because_it_is_a_constant, c.symbolToString(symbol))
			} else {
				c.error(node, diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, c.symbolToString(symbol))
			}
			return c.errorType
		}
	}
	isAlias := localOrExportSymbol.Flags&ast.SymbolFlagsAlias != 0
	// We only narrow variables and parameters occurring in a non-assignment position. For all other
	// entities we simply return the declared type.
	if localOrExportSymbol.Flags&ast.SymbolFlagsVariable != 0 {
		if assignmentKind == AssignmentKindDefinite {
			if isInCompoundLikeAssignment(node) {
				return c.getBaseTypeOfLiteralType(t)
			}
			return t
		}
	} else if isAlias {
		declaration = c.getDeclarationOfAliasSymbol(symbol)
	} else {
		return t
	}
	if declaration == nil {
		return t
	}
	t = c.getNarrowableTypeForReference(t, node, checkMode)
	// The declaration container is the innermost function that encloses the declaration of the variable
	// or parameter. The flow container is the innermost function starting with which we analyze the control
	// flow graph to determine the control flow based type.
	isParameter := ast.GetRootDeclaration(declaration).Kind == ast.KindParameter
	declarationContainer := c.getControlFlowContainer(declaration)
	flowContainer := c.getControlFlowContainer(node)
	isOuterVariable := flowContainer != declarationContainer
	isSpreadDestructuringAssignmentTarget := node.Parent != nil && node.Parent.Parent != nil && ast.IsSpreadAssignment(node.Parent) && c.isDestructuringAssignmentTarget(node.Parent.Parent)
	isModuleExports := symbol.Flags&ast.SymbolFlagsModuleExports != 0
	typeIsAutomatic := t == c.autoType || t == c.autoArrayType
	isAutomaticTypeInNonNull := typeIsAutomatic && node.Parent.Kind == ast.KindNonNullExpression
	// When the control flow originates in a function expression, arrow function, method, or accessor, and
	// we are referencing a closed-over const variable or parameter or mutable local variable past its last
	// assignment, we extend the origin of the control flow analysis to include the immediately enclosing
	// control flow container.
	for flowContainer != declarationContainer &&
		(ast.IsFunctionExpressionOrArrowFunction(flowContainer) || ast.IsObjectLiteralOrClassExpressionMethodOrAccessor(flowContainer)) &&
		(c.isConstantVariable(localOrExportSymbol) && t != c.autoArrayType || c.isParameterOrMutableLocalVariable(localOrExportSymbol) && c.isPastLastAssignment(localOrExportSymbol, node)) {
		flowContainer = c.getControlFlowContainer(flowContainer)
	}
	// We only look for uninitialized variables in strict null checking mode, and only when we can analyze
	// the entire control flow graph from the variable's declaration (i.e. when the flow container and
	// declaration container are the same).
	isNeverInitialized := immediateDeclaration != nil && ast.IsVariableDeclaration(immediateDeclaration) && !ast.IsForInOrOfStatement(immediateDeclaration.Parent.Parent) &&
		immediateDeclaration.Initializer() == nil && immediateDeclaration.AsVariableDeclaration().ExclamationToken == nil &&
		c.isMutableLocalVariableDeclaration(immediateDeclaration) && !c.isSymbolAssignedDefinitely(symbol)
	assumeInitialized := isParameter ||
		isAlias ||
		(isOuterVariable && !isNeverInitialized) ||
		isSpreadDestructuringAssignmentTarget ||
		isModuleExports ||
		c.isSameScopedBindingElement(node, declaration) ||
		t != c.autoType && t != c.autoArrayType && (!c.strictNullChecks || t.flags&(TypeFlagsAnyOrUnknown|TypeFlagsVoid) != 0 || IsInTypeQuery(node) || c.isInAmbientOrTypeNode(node) || node.Parent.Kind == ast.KindExportSpecifier) ||
		ast.IsNonNullExpression(node.Parent) ||
		ast.IsVariableDeclaration(declaration) && declaration.AsVariableDeclaration().ExclamationToken != nil ||
		declaration.Flags&ast.NodeFlagsAmbient != 0
	var initialType *Type
	switch {
	case isAutomaticTypeInNonNull:
		initialType = c.undefinedType
	case assumeInitialized && isParameter:
		initialType = c.removeOptionalityFromDeclaredType(t, declaration)
	case assumeInitialized:
		initialType = t
	case typeIsAutomatic:
		initialType = c.undefinedType
	default:
		initialType = c.getOptionalType(t, false /*isProperty*/)
	}
	var flowType *Type
	if isAutomaticTypeInNonNull {
		flowType = c.GetNonNullableType(c.getFlowTypeOfReferenceEx(node, t, initialType, flowContainer, nil))
	} else {
		flowType = c.getFlowTypeOfReferenceEx(node, t, initialType, flowContainer, nil)
	}
	// A variable is considered uninitialized when it is possible to analyze the entire control flow graph
	// from declaration to use, and when the variable's declared type doesn't include undefined but the
	// control flow based type does include undefined.
	if !c.isEvolvingArrayOperationTarget(node) && (t == c.autoType || t == c.autoArrayType) {
		if flowType == c.autoType || flowType == c.autoArrayType {
			if c.noImplicitAny {
				c.error(ast.GetNameOfDeclaration(declaration), diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined, c.symbolToString(symbol), c.TypeToString(flowType))
				c.error(node, diagnostics.Variable_0_implicitly_has_an_1_type, c.symbolToString(symbol), c.TypeToString(flowType))
			}
			return c.convertAutoToAny(flowType)
		}
	} else if !assumeInitialized && !c.containsUndefinedType(t) && c.containsUndefinedType(flowType) {
		c.error(node, diagnostics.Variable_0_is_used_before_being_assigned, c.symbolToString(symbol))
		// Return the declared type to reduce follow-on errors
		return t
	}
	if assignmentKind != AssignmentKindNone {
		// Identifier is target of a compound assignment
		return c.getBaseTypeOfLiteralType(flowType)
	}
	return flowType
}

func (c *Checker) isSameScopedBindingElement(node *ast.Node, declaration *ast.Node) bool {
	if ast.IsBindingElement(declaration) {
		bindingElement := ast.FindAncestor(node, ast.IsBindingElement)
		return bindingElement != nil && ast.GetRootDeclaration(bindingElement) == ast.GetRootDeclaration(declaration)
	}
	return false
}

// Remove undefined from the annotated type of a parameter when there is an initializer (that doesn't include undefined)
func (c *Checker) removeOptionalityFromDeclaredType(declaredType *Type, declaration *ast.Node) *Type {
	removeUndefined := c.strictNullChecks && ast.IsParameterDeclaration(declaration) && declaration.Initializer() != nil && c.hasTypeFacts(declaredType, TypeFactsIsUndefined) && !c.parameterInitializerContainsUndefined(declaration)
	if removeUndefined {
		return c.getTypeWithFacts(declaredType, TypeFactsNEUndefined)
	}
	return declaredType
}

func (c *Checker) parameterInitializerContainsUndefined(declaration *ast.Node) bool {
	links := c.nodeLinks.Get(declaration)
	if links.flags&NodeCheckFlagsInitializerIsUndefinedComputed == 0 {
		if !c.pushTypeResolution(declaration, TypeSystemPropertyNameInitializerIsUndefined) {
			c.reportCircularityError(declaration.Symbol())
			return true
		}
		containsUndefined := c.hasTypeFacts(c.checkDeclarationInitializer(declaration, CheckModeNormal, nil), TypeFactsIsUndefined)
		if !c.popTypeResolution() {
			c.reportCircularityError(declaration.Symbol())
			return true
		}
		if links.flags&NodeCheckFlagsInitializerIsUndefinedComputed == 0 {
			links.flags |= NodeCheckFlagsInitializerIsUndefinedComputed | core.IfElse(containsUndefined, NodeCheckFlagsInitializerIsUndefined, 0)
		}
	}
	return links.flags&NodeCheckFlagsInitializerIsUndefined != 0
}

func (c *Checker) isInAmbientOrTypeNode(node *ast.Node) bool {
	return node.Flags&ast.NodeFlagsAmbient != 0 || ast.FindAncestor(node, func(n *ast.Node) bool {
		return ast.IsInterfaceDeclaration(n) || ast.IsTypeAliasDeclaration(n) || ast.IsJSTypeAliasDeclaration(n) || ast.IsTypeLiteralNode(n)
	}) != nil
}

func (c *Checker) checkPropertyAccessExpression(node *ast.Node, checkMode CheckMode, writeOnly bool) *Type {
	if node.Flags&ast.NodeFlagsOptionalChain != 0 {
		return c.checkPropertyAccessChain(node, checkMode)
	}
	expr := node.Expression()
	return c.checkPropertyAccessExpressionOrQualifiedName(node, expr, c.checkNonNullExpression(expr), node.AsPropertyAccessExpression().Name(), checkMode, writeOnly)
}

func (c *Checker) checkPropertyAccessChain(node *ast.Node, checkMode CheckMode) *Type {
	leftType := c.checkExpression(node.Expression())
	nonOptionalType := c.getOptionalExpressionType(leftType, node.Expression())
	return c.propagateOptionalTypeMarker(c.checkPropertyAccessExpressionOrQualifiedName(node, node.Expression(), c.checkNonNullType(nonOptionalType, node.Expression()), node.Name(), checkMode, false), node, nonOptionalType != leftType)
}

func (c *Checker) checkPropertyAccessExpressionOrQualifiedName(node *ast.Node, left *ast.Node, leftType *Type, right *ast.Node, checkMode CheckMode, writeOnly bool) *Type {
	parentSymbol := c.getResolvedSymbolOrNil(left)
	assignmentKind := getAssignmentTargetKind(node)
	widenedType := leftType
	if assignmentKind != AssignmentKindNone || c.isMethodAccessForCall(node) {
		widenedType = c.getWidenedType(leftType)
	}
	apparentType := c.getApparentType(widenedType)
	isAnyLike := IsTypeAny(apparentType) || apparentType == c.silentNeverType
	var prop *ast.Symbol
	if ast.IsPrivateIdentifier(right) {
		if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks ||
			c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators ||
			!c.compilerOptions.GetUseDefineForClassFields() {
			if assignmentKind != AssignmentKindNone {
				c.checkExternalEmitHelpers(node, ExternalEmitHelpersClassPrivateFieldSet)
			}
			if assignmentKind != AssignmentKindDefinite {
				c.checkExternalEmitHelpers(node, ExternalEmitHelpersClassPrivateFieldGet)
			}
		}
		lexicallyScopedSymbol := c.lookupSymbolForPrivateIdentifierDeclaration(right.Text(), right)
		if assignmentKind != AssignmentKindNone && lexicallyScopedSymbol != nil && lexicallyScopedSymbol.ValueDeclaration != nil && ast.IsMethodDeclaration(lexicallyScopedSymbol.ValueDeclaration) {
			c.grammarErrorOnNode(right, diagnostics.Cannot_assign_to_private_method_0_Private_methods_are_not_writable, right.Text())
		}
		if isAnyLike {
			if lexicallyScopedSymbol != nil {
				if c.isErrorType(apparentType) {
					return c.errorType
				}
				return apparentType
			}
			if getContainingClassExcludingClassDecorators(right) == nil {
				c.grammarErrorOnNode(right, diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies)
				return c.anyType
			}
		}
		if lexicallyScopedSymbol != nil {
			prop = c.getPrivateIdentifierPropertyOfType(leftType, lexicallyScopedSymbol)
		}
		if prop == nil {
			// Check for private-identifier-specific shadowing and lexical-scoping errors.
			if c.checkPrivateIdentifierPropertyAccess(leftType, right, lexicallyScopedSymbol) {
				return c.errorType
			}
			containingClass := getContainingClassExcludingClassDecorators(right)
			if containingClass != nil && ast.IsPlainJSFile(ast.GetSourceFileOfNode(containingClass), c.compilerOptions.CheckJs) {
				c.grammarErrorOnNode(right, diagnostics.Private_field_0_must_be_declared_in_an_enclosing_class, right.Text())
			}
		} else {
			isSetonlyAccessor := prop.Flags&ast.SymbolFlagsSetAccessor != 0 && prop.Flags&ast.SymbolFlagsGetAccessor == 0
			if isSetonlyAccessor && assignmentKind != AssignmentKindDefinite {
				c.error(node, diagnostics.Private_accessor_was_defined_without_a_getter)
			}
		}
	} else {
		if isAnyLike {
			if ast.IsIdentifier(left) && parentSymbol != nil {
				c.markLinkedReferences(node, ReferenceHintProperty, nil /*propSymbol*/, leftType)
			}
			if c.isErrorType(apparentType) {
				return c.errorType
			}
			return apparentType
		}
		prop = c.getPropertyOfTypeEx(apparentType, right.Text(), isConstEnumObjectType(apparentType) /*skipObjectFunctionPropertyAugment*/, node.Kind == ast.KindQualifiedName /*includeTypeOnlyMembers*/)
	}
	c.markLinkedReferences(node, ReferenceHintProperty, prop, leftType)
	var propType *Type
	if prop == nil {
		var indexInfo *IndexInfo
		if !ast.IsPrivateIdentifier(right) && (assignmentKind == AssignmentKindNone || !c.isGenericObjectType(leftType) || isThisTypeParameter(leftType)) {
			indexInfo = c.getApplicableIndexInfoForName(apparentType, right.Text())
		}
		if indexInfo == nil {
			isUncheckedJS := c.isUncheckedJSSuggestion(node, leftType.symbol, true /*excludeClasses*/)
			if !isUncheckedJS && c.isJSLiteralType(leftType) {
				return c.anyType
			}
			if leftType.symbol == c.globalThisSymbol {
				globalSymbol := c.globalThisSymbol.Exports[right.Text()]
				if globalSymbol != nil && globalSymbol.Flags&ast.SymbolFlagsBlockScoped != 0 {
					c.error(right, diagnostics.Property_0_does_not_exist_on_type_1, right.Text(), c.TypeToString(leftType))
				} else if c.noImplicitAny {
					c.error(right, diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature, c.TypeToString(leftType))
				}
				return c.anyType
			}
			if right.Text() != "" && !c.checkAndReportErrorForExtendingInterface(node) {
				c.reportNonexistentProperty(right, core.IfElse(isThisTypeParameter(leftType), apparentType, leftType), isUncheckedJS)
			}
			return c.errorType
		}
		if indexInfo.isReadonly && (ast.IsAssignmentTarget(node) || isDeleteTarget(node)) {
			c.error(node, diagnostics.Index_signature_in_type_0_only_permits_reading, c.TypeToString(apparentType))
		}
		propType = indexInfo.valueType
		if c.compilerOptions.NoUncheckedIndexedAccess == core.TSTrue && getAssignmentTargetKind(node) != AssignmentKindDefinite {
			propType = c.getUnionType([]*Type{propType, c.missingType})
		}
		if c.compilerOptions.NoPropertyAccessFromIndexSignature == core.TSTrue && ast.IsPropertyAccessExpression(node) {
			c.error(right, diagnostics.Property_0_comes_from_an_index_signature_so_it_must_be_accessed_with_0, right.Text())
		}
		if indexInfo.declaration != nil && c.IsDeprecatedDeclaration(indexInfo.declaration) {
			c.addDeprecatedSuggestion(right, []*ast.Node{indexInfo.declaration}, right.Text())
		}
	} else {
		targetPropSymbol := c.resolveAliasWithDeprecationCheck(prop, right)
		if c.isDeprecatedSymbol(targetPropSymbol) && c.isUncalledFunctionReference(node, targetPropSymbol) && targetPropSymbol.Declarations != nil {
			c.addDeprecatedSuggestion(right, targetPropSymbol.Declarations, right.Text())
		}
		c.checkPropertyNotUsedBeforeDeclaration(prop, node, right)
		c.markPropertyAsReferenced(prop, node, c.isSelfTypeAccess(left, parentSymbol))
		c.symbolNodeLinks.Get(node).resolvedSymbol = prop
		c.checkPropertyAccessibility(node, left.Kind == ast.KindSuperKeyword, ast.IsWriteAccess(node), apparentType, prop)
		if c.isAssignmentToReadonlyEntity(node, prop, assignmentKind) {
			c.error(right, diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, right.Text())
			return c.errorType
		}
		switch {
		case c.isThisPropertyAccessInConstructor(node, prop):
			propType = c.autoType
		case writeOnly || ast.IsWriteOnlyAccess(node):
			propType = c.getWriteTypeOfSymbol(prop)
		default:
			propType = c.getTypeOfSymbol(prop)
		}
	}
	return c.getFlowTypeOfAccessExpression(node, prop, propType, right, checkMode)
}

func (c *Checker) getFlowTypeOfAccessExpression(node *ast.Node, prop *ast.Symbol, propType *Type, errorNode *ast.Node, checkMode CheckMode) *Type {
	// Only compute control flow type if this is a property access expression that isn't an
	// assignment target, and the referenced property was declared as a variable, property,
	// accessor, or optional method.
	assignmentKind := getAssignmentTargetKind(node)
	if assignmentKind == AssignmentKindDefinite {
		return c.removeMissingType(propType, prop != nil && prop.Flags&ast.SymbolFlagsOptional != 0)
	}
	if prop != nil && prop.Flags&(ast.SymbolFlagsVariable|ast.SymbolFlagsProperty|ast.SymbolFlagsAccessor) == 0 && !(prop.Flags&ast.SymbolFlagsMethod != 0 && propType.flags&TypeFlagsUnion != 0) {
		return propType
	}
	if propType == c.autoType {
		return c.getFlowTypeOfProperty(node, prop)
	}
	propType = c.getNarrowableTypeForReference(propType, node, checkMode)
	// If strict null checks and strict property initialization checks are enabled, if we have
	// a this.xxx property access, if the property is an instance property without an initializer,
	// and if we are in a constructor of the same class as the property declaration, assume that
	// the property is uninitialized at the top of the control flow.
	assumeUninitialized := false
	if c.strictNullChecks && prop != nil {
		if declaration := prop.ValueDeclaration; declaration != nil {
			if c.strictPropertyInitialization && ast.IsAccessExpression(node) && node.Expression().Kind == ast.KindThisKeyword &&
				c.isPropertyWithoutInitializer(declaration) && !ast.IsStatic(declaration) {
				flowContainer := c.getControlFlowContainer(node)
				if ast.IsConstructorDeclaration(flowContainer) && flowContainer.Parent == declaration.Parent && declaration.Flags&ast.NodeFlagsAmbient == 0 {
					assumeUninitialized = true
				}
			} else if ast.IsBinaryExpression(declaration) && ast.IsPropertyAccessExpression(declaration.AsBinaryExpression().Left) &&
				c.getControlFlowContainer(node) == c.getControlFlowContainer(declaration) {
				assumeUninitialized = true
			}
		}
	}
	flowType := c.getFlowTypeOfReferenceEx(node, propType, c.addOptionalityEx(propType, false /*isProperty*/, assumeUninitialized), nil, nil)
	if assumeUninitialized && !c.containsUndefinedType(propType) && c.containsUndefinedType(flowType) {
		c.error(errorNode, diagnostics.Property_0_is_used_before_being_assigned, c.symbolToString(prop))
		// Return the declared type to reduce follow-on errors
		return propType
	}
	if assignmentKind != AssignmentKindNone {
		return c.getBaseTypeOfLiteralType(flowType)
	}
	return flowType
}

func (c *Checker) getControlFlowContainer(node *ast.Node) *ast.Node {
	return ast.FindAncestor(node.Parent, func(node *ast.Node) bool {
		return ast.IsFunctionLike(node) && ast.GetImmediatelyInvokedFunctionExpression(node) == nil || ast.IsModuleBlock(node) || ast.IsSourceFile(node) || ast.IsPropertyDeclaration(node)
	})
}

func (c *Checker) getFlowTypeOfProperty(reference *ast.Node, prop *ast.Symbol) *Type {
	initialType := c.undefinedType
	if prop != nil && prop.ValueDeclaration != nil && (!c.isAutoTypedProperty(prop) || prop.ValueDeclaration.ModifierFlags()&ast.ModifierFlagsAmbient != 0) {
		if baseType := c.getTypeOfPropertyInBaseClass(prop); baseType != nil {
			initialType = baseType
		}
	}
	return c.getFlowTypeOfReferenceEx(reference, c.autoType, initialType, nil, nil)
}

// Return the inherited type of the given property or undefined if property doesn't exist in a base class.
func (c *Checker) getTypeOfPropertyInBaseClass(property *ast.Symbol) *Type {
	classType := c.getDeclaringClass(property)
	if classType != nil {
		baseClassTypes := c.getBaseTypes(classType)
		if len(baseClassTypes) > 0 {
			return c.getTypeOfPropertyOfType(baseClassTypes[0], property.Name)
		}
	}
	return nil
}

func (c *Checker) isMethodAccessForCall(node *ast.Node) bool {
	for ast.IsParenthesizedExpression(node.Parent) {
		node = node.Parent
	}
	return ast.IsCallOrNewExpression(node.Parent) && node.Parent.Expression() == node
}

// Lookup the private identifier lexically.
func (c *Checker) lookupSymbolForPrivateIdentifierDeclaration(propName string, location *ast.Node) *ast.Symbol {
	for containingClass := getContainingClassExcludingClassDecorators(location); containingClass != nil; containingClass = ast.GetContainingClass(containingClass) {
		symbol := containingClass.Symbol()
		name := binder.GetSymbolNameForPrivateIdentifier(symbol, propName)
		prop := symbol.Members[name]
		if prop != nil {
			return prop
		}
		prop = symbol.Exports[name]
		if prop != nil {
			return prop
		}
	}
	return nil
}

func (c *Checker) getPrivateIdentifierPropertyOfType(leftType *Type, lexicallyScopedIdentifier *ast.Symbol) *ast.Symbol {
	return c.getPropertyOfType(leftType, lexicallyScopedIdentifier.Name)
}

func (c *Checker) checkPrivateIdentifierPropertyAccess(leftType *Type, right *ast.Node, lexicallyScopedIdentifier *ast.Symbol) bool {
	// Either the identifier could not be looked up in the lexical scope OR the lexically scoped identifier did not exist on the type.
	// Find a private identifier with the same description on the type.
	properties := c.getPropertiesOfType(leftType)
	var propertyOnType *ast.Symbol
	for _, symbol := range properties {
		decl := symbol.ValueDeclaration
		if decl != nil && decl.Name() != nil && ast.IsPrivateIdentifier(decl.Name()) && decl.Name().Text() == right.Text() {
			propertyOnType = symbol
			break
		}
	}
	diagName := scanner.DeclarationNameToString(right)
	if propertyOnType != nil {
		typeValueDecl := propertyOnType.ValueDeclaration
		typeClass := ast.GetContainingClass(typeValueDecl)
		// We found a private identifier property with the same description.
		// Either:
		// - There is a lexically scoped private identifier AND it shadows the one we found on the type.
		// - It is an attempt to access the private identifier outside of the class.
		if lexicallyScopedIdentifier != nil && lexicallyScopedIdentifier.ValueDeclaration != nil {
			lexicalValueDecl := lexicallyScopedIdentifier.ValueDeclaration
			lexicalClass := ast.GetContainingClass(lexicalValueDecl)
			if ast.FindAncestor(lexicalClass, func(n *ast.Node) bool { return typeClass == n }) != nil {
				diagnostic := c.error(right, diagnostics.The_property_0_cannot_be_accessed_on_type_1_within_this_class_because_it_is_shadowed_by_another_private_identifier_with_the_same_spelling, diagName, c.TypeToString(leftType))
				diagnostic.AddRelatedInfo(createDiagnosticForNode(lexicalValueDecl, diagnostics.The_shadowing_declaration_of_0_is_defined_here, diagName))
				diagnostic.AddRelatedInfo(createDiagnosticForNode(typeValueDecl, diagnostics.The_declaration_of_0_that_you_probably_intended_to_use_is_defined_here, diagName))
				return true
			}
		}
		c.error(right, diagnostics.Property_0_is_not_accessible_outside_class_1_because_it_has_a_private_identifier, diagName, c.SymbolToString(typeClass.Symbol()))
		return true
	}
	return false
}

func (c *Checker) reportNonexistentProperty(propNode *ast.Node, containingType *Type, isUncheckedJS bool) {
	key := NonExistentPropertyKey{propNode, containingType, isUncheckedJS}
	if c.nonExistentProperties.Has(key) {
		return
	}
	c.nonExistentProperties.Add(key)
	links := c.nodeLinks.Get(propNode)
	if links.flags&NodeCheckFlagsTypeChecked != 0 {
		return // error already made/in progress
	}
	links.flags |= NodeCheckFlagsTypeChecked
	if ast.IsJSDocNameReferenceContext(propNode) {
		return
	}
	var diagnostic *ast.Diagnostic
	if !ast.IsPrivateIdentifier(propNode) && containingType.flags&TypeFlagsUnion != 0 && containingType.flags&TypeFlagsPrimitive == 0 {
		for _, subtype := range containingType.Types() {
			if c.getPropertyOfType(subtype, propNode.Text()) == nil && c.getApplicableIndexInfoForName(subtype, propNode.Text()) == nil {
				diagnostic = NewDiagnosticChainForNode(diagnostic, propNode, diagnostics.Property_0_does_not_exist_on_type_1, scanner.DeclarationNameToString(propNode), c.TypeToString(subtype))
				break
			}
		}
	}
	if c.typeHasStaticProperty(propNode.Text(), containingType) {
		propName := scanner.DeclarationNameToString(propNode)
		typeName := c.TypeToString(containingType)
		diagnostic = NewDiagnosticChainForNode(diagnostic, propNode, diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_to_access_the_static_member_2_instead, propName, typeName, typeName+"."+propName)
	} else {
		promisedType := c.GetPromisedTypeOfPromise(containingType)
		if promisedType != nil && c.getPropertyOfType(promisedType, propNode.Text()) != nil {
			diagnostic = NewDiagnosticChainForNode(diagnostic, propNode, diagnostics.Property_0_does_not_exist_on_type_1, scanner.DeclarationNameToString(propNode), c.TypeToString(containingType))
			diagnostic.AddRelatedInfo(NewDiagnosticForNode(propNode, diagnostics.Did_you_forget_to_use_await))
		} else {
			missingProperty := scanner.DeclarationNameToString(propNode)
			container := c.TypeToString(containingType)
			libSuggestion := c.getSuggestedLibForNonExistentProperty(missingProperty, containingType)
			if libSuggestion != "" {
				diagnostic = NewDiagnosticChainForNode(diagnostic, propNode, diagnostics.Property_0_does_not_exist_on_type_1_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_2_or_later, missingProperty, container, libSuggestion)
			} else {
				suggestion := c.getSuggestedSymbolForNonexistentProperty(propNode, containingType)
				if suggestion != nil {
					suggestedName := ast.SymbolName(suggestion)
					message := core.IfElse(isUncheckedJS, diagnostics.Property_0_may_not_exist_on_type_1_Did_you_mean_2, diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2)
					diagnostic = NewDiagnosticChainForNode(diagnostic, propNode, message, missingProperty, container, suggestedName)
					if suggestion.ValueDeclaration != nil {
						diagnostic.AddRelatedInfo(NewDiagnosticForNode(suggestion.ValueDeclaration, diagnostics.X_0_is_declared_here, suggestedName))
					}
				} else {
					diagnostic = c.elaborateNeverIntersection(diagnostic, propNode, containingType)
					var message *diagnostics.Message
					if c.containerSeemsToBeEmptyDomElement(containingType) {
						message = diagnostics.Property_0_does_not_exist_on_type_1_Try_changing_the_lib_compiler_option_to_include_dom
					} else {
						message = diagnostics.Property_0_does_not_exist_on_type_1
					}
					diagnostic = NewDiagnosticChainForNode(diagnostic, propNode, message, missingProperty, container)
				}
			}
		}
	}
	c.addErrorOrSuggestion(!isUncheckedJS || diagnostic.Code() != diagnostics.Property_0_may_not_exist_on_type_1_Did_you_mean_2.Code(), diagnostic)
}

func (c *Checker) getSuggestedLibForNonExistentProperty(missingProperty string, containingType *Type) string {
	container := c.getApparentType(containingType).symbol
	if container != nil {
		featureMap := getFeatureMap()
		if typeFeatures, ok := featureMap[container.Name]; ok {
			for _, entry := range typeFeatures {
				if slices.Contains(entry.props, missingProperty) {
					return entry.lib
				}
			}
		}
	}
	return ""
}

func (c *Checker) getSuggestedSymbolForNonexistentProperty(name *ast.Node, containingType *Type) *ast.Symbol {
	props := c.getPropertiesOfType(containingType)
	parent := name.Parent
	if ast.IsPropertyAccessExpression(parent) {
		props = core.Filter(props, func(prop *ast.Symbol) bool {
			return c.isValidPropertyAccessForCompletions(parent, containingType, prop)
		})
	}
	return c.getSpellingSuggestionForName(name.Text(), slices.Values(props), ast.SymbolFlagsValue)
}

// Checks if an existing property access is valid for completions purposes.
// @param node a property access-like node where we want to check if we can access a property.
// This node does not need to be an access of the property we are checking.
// e.g. in completions, this node will often be an incomplete property access node, as in `foo.`.
// Besides providing a location (i.e. scope) used to check property accessibility, we use this node for
// computing whether this is a `super` property access.
// @param type the type whose property we are checking.
// @param property the accessed property's symbol.
func (c *Checker) isValidPropertyAccessForCompletions(node *ast.Node, t *Type, property *ast.Symbol) bool {
	return c.isPropertyAccessible(node, ast.IsPropertyAccessExpression(node) && node.Expression().Kind == ast.KindSuperKeyword, false /*isWrite*/, t, property)
	// Previously we validated the 'this' type of methods but this adversely affected performance. See #31377 for more context.
}

// Checks if a property can be accessed in a location.
// The location is given by the `node` parameter.
// The node does not need to be a property access.
// @param node location where to check property accessibility
// @param isSuper whether to consider this a `super` property access, e.g. `super.foo`.
// @param isWrite whether this is a write access, e.g. `++foo.x`.
// @param containingType type where the property comes from.
// @param property property symbol.
func (c *Checker) isPropertyAccessible(node *ast.Node, isSuper bool, isWrite bool, containingType *Type, property *ast.Symbol) bool {
	// Short-circuiting for improved performance.
	if IsTypeAny(containingType) {
		return true
	}
	// A #private property access in an optional chain is an error dealt with by the parser.
	// The checker does not check for it, so we need to do our own check here.
	if property.ValueDeclaration != nil && ast.IsPrivateIdentifierClassElementDeclaration(property.ValueDeclaration) {
		declClass := ast.GetContainingClass(property.ValueDeclaration)
		return !ast.IsOptionalChain(node) && ast.IsNodeDescendantOf(node, declClass)
	}
	return c.checkPropertyAccessibilityAtLocation(node, isSuper, isWrite, containingType, property, nil)
}

func (c *Checker) containerSeemsToBeEmptyDomElement(containingType *Type) bool {
	return !slices.Contains(c.compilerOptions.Lib, "lib.dom.d.ts") && everyContainedType(containingType, hasCommonDomTypeName) && c.isEmptyObjectType(containingType)
}

func hasCommonDomTypeName(t *Type) bool {
	if t.symbol == nil {
		return false
	}
	name := t.symbol.Name
	return name == "EventTarget" || name == "Node" || name == "Element" || strings.HasPrefix(name, "HTML") && strings.HasSuffix(name, "Element")
}

func (c *Checker) checkAndReportErrorForExtendingInterface(errorLocation *ast.Node) bool {
	expression := c.getEntityNameForExtendingInterface(errorLocation)
	if expression != nil && c.resolveEntityName(expression, ast.SymbolFlagsInterface, true /*ignoreErrors*/, false, nil) != nil {
		c.error(errorLocation, diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, scanner.GetTextOfNode(expression))
		return true
	}
	return false
}

/**
 * Climbs up parents to an ExpressionWithTypeArguments, and returns its expression,
 * but returns undefined if that expression is not an EntityNameExpression.
 */
func (c *Checker) getEntityNameForExtendingInterface(node *ast.Node) *ast.Node {
	switch node.Kind {
	case ast.KindIdentifier, ast.KindPropertyAccessExpression:
		if node.Parent != nil {
			return c.getEntityNameForExtendingInterface(node.Parent)
		}
	case ast.KindExpressionWithTypeArguments:
		if ast.IsEntityNameExpression(node.Expression()) {
			return node.Expression()
		}
	}
	return nil
}

func (c *Checker) isUncalledFunctionReference(node *ast.Node, symbol *ast.Symbol) bool {
	if symbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0 {
		parent := ast.FindAncestor(node.Parent, func(n *ast.Node) bool { return !ast.IsAccessExpression(n) })
		if parent == nil {
			parent = node.Parent
		}
		if ast.IsCallLikeExpression(parent) {
			return ast.IsCallOrNewExpression(parent) && ast.IsIdentifier(node) && c.hasMatchingArgument(parent, node)
		}
		return core.Every(symbol.Declarations, func(d *ast.Node) bool {
			return !ast.IsFunctionLike(d) || c.IsDeprecatedDeclaration(d)
		})
	}
	return true
}

func (c *Checker) checkPropertyNotUsedBeforeDeclaration(prop *ast.Symbol, node *ast.Node, right *ast.Node) {
	valueDeclaration := prop.ValueDeclaration
	if valueDeclaration == nil || ast.GetSourceFileOfNode(node).IsDeclarationFile {
		return
	}
	var diagnostic *ast.Diagnostic
	declarationName := right.Text()
	if c.isInPropertyInitializerOrClassStaticBlock(node, false /*ignoreArrowFunctions*/) &&
		!c.isOptionalPropertyDeclaration(valueDeclaration) &&
		!(ast.IsAccessExpression(node) && ast.IsAccessExpression(node.Expression())) &&
		!c.isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right) &&
		!(ast.IsMethodDeclaration(valueDeclaration) && c.getCombinedModifierFlagsCached(valueDeclaration)&ast.ModifierFlagsStatic != 0) &&
		(c.compilerOptions.GetUseDefineForClassFields() || !c.isPropertyDeclaredInAncestorClass(prop)) {
		diagnostic = c.error(right, diagnostics.Property_0_is_used_before_its_initialization, declarationName)
	} else if ast.IsClassDeclaration(valueDeclaration) && !ast.IsTypeReferenceNode(node.Parent) && valueDeclaration.Flags&ast.NodeFlagsAmbient == 0 && !c.isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right) {
		diagnostic = c.error(right, diagnostics.Class_0_used_before_its_declaration, declarationName)
	}
	if diagnostic != nil {
		diagnostic.AddRelatedInfo(NewDiagnosticForNode(valueDeclaration, diagnostics.X_0_is_declared_here, declarationName))
	}
}

func (c *Checker) isOptionalPropertyDeclaration(node *ast.Node) bool {
	return ast.IsPropertyDeclaration(node) && !ast.HasAccessorModifier(node) && ast.IsQuestionToken(node.PostfixToken())
}

func (c *Checker) isPropertyDeclaredInAncestorClass(prop *ast.Symbol) bool {
	if prop.Parent.Flags&ast.SymbolFlagsClass != 0 {
		if baseTypes := c.getBaseTypes(c.getDeclaredTypeOfSymbol(prop.Parent)); len(baseTypes) != 0 {
			superProperty := c.getPropertyOfType(baseTypes[0], prop.Name)
			return superProperty != nil && superProperty.ValueDeclaration != nil
		}
	}
	return false
}

/**
 * Check whether the requested property access is valid.
 * Returns true if node is a valid property access, and false otherwise.
 * @param node The node to be checked.
 * @param isSuper True if the access is from `super.`.
 * @param type The type of the object whose property is being accessed. (Not the type of the property.)
 * @param prop The symbol for the property being accessed.
 */
func (c *Checker) checkPropertyAccessibility(node *ast.Node, isSuper bool, writing bool, t *Type, prop *ast.Symbol) bool {
	return c.checkPropertyAccessibilityEx(node, isSuper, writing, t, prop, true /*reportError*/)
}

func (c *Checker) checkPropertyAccessibilityEx(node *ast.Node, isSuper bool, writing bool, t *Type, prop *ast.Symbol, reportError bool /*  = true */) bool {
	var errorNode *ast.Node
	if reportError {
		switch node.Kind {
		case ast.KindPropertyAccessExpression:
			errorNode = node.AsPropertyAccessExpression().Name()
		case ast.KindQualifiedName:
			errorNode = node.AsQualifiedName().Right
		case ast.KindImportType:
			errorNode = node
		case ast.KindBindingElement:
			errorNode = getBindingElementPropertyName(node)
		default:
			errorNode = node.Name()
		}
	}
	return c.checkPropertyAccessibilityAtLocation(node, isSuper, writing, t, prop, errorNode)
}

/**
 * Check whether the requested property can be accessed at the requested location.
 * Returns true if node is a valid property access, and false otherwise.
 * @param location The location node where we want to check if the property is accessible.
 * @param isSuper True if the access is from `super.`.
 * @param writing True if this is a write property access, false if it is a read property access.
 * @param containingType The type of the object whose property is being accessed. (Not the type of the property.)
 * @param prop The symbol for the property being accessed.
 * @param errorNode The node where we should report an invalid property access error, or undefined if we should not report errors.
 */
func (c *Checker) checkPropertyAccessibilityAtLocation(location *ast.Node, isSuper bool, writing bool, containingType *Type, prop *ast.Symbol, errorNode *ast.Node) bool {
	flags := getDeclarationModifierFlagsFromSymbolEx(prop, writing)
	if isSuper {
		// TS 1.0 spec (April 2014): 4.8.2
		// - In a constructor, instance member function, instance member accessor, or
		//   instance member variable initializer where this references a derived class instance,
		//   a super property access is permitted and must specify a public instance member function of the base class.
		// - In a static member function or static member accessor
		//   where this references the constructor function object of a derived class,
		//   a super property access is permitted and must specify a public static member function of the base class.
		if flags&ast.ModifierFlagsAbstract != 0 {
			// A method cannot be accessed in a super property access if the method is abstract.
			// This error could mask a private property access error. But, a member
			// cannot simultaneously be private and abstract, so this will trigger an
			// additional error elsewhere.
			if errorNode != nil {
				c.error(errorNode, diagnostics.Abstract_method_0_in_class_1_cannot_be_accessed_via_super_expression, c.symbolToString(prop), c.TypeToString(c.getDeclaringClass(prop)))
			}
			return false
		}
		// A class field cannot be accessed via super.* from a derived class.
		// This is true for both [[Set]] (old) and [[Define]] (ES spec) semantics.
		if flags&ast.ModifierFlagsStatic == 0 && core.Some(prop.Declarations, isClassInstanceProperty) {
			if errorNode != nil {
				c.error(errorNode, diagnostics.Class_field_0_defined_by_the_parent_class_is_not_accessible_in_the_child_class_via_super, c.symbolToString(prop))
			}
			return false
		}
	}
	// Referencing abstract properties within their own constructors is not allowed
	if flags&ast.ModifierFlagsAbstract != 0 && c.symbolHasNonMethodDeclaration(prop) && (isThisProperty(location) ||
		isThisInitializedObjectBindingExpression(location) ||
		ast.IsObjectBindingPattern(location.Parent) && isThisInitializedDeclaration(location.Parent.Parent)) {
		parentSymbol := c.getParentOfSymbol(prop)
		if parentSymbol != nil && parentSymbol.Flags&ast.SymbolFlagsClass != 0 && c.isNodeUsedDuringClassInitialization(location) {
			if errorNode != nil {
				c.error(errorNode, diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, c.symbolToString(prop), c.symbolToString(parentSymbol))
			}
			return false
		}
	}
	// Public properties are otherwise accessible.
	if flags&ast.ModifierFlagsNonPublicAccessibilityModifier == 0 {
		return true
	}
	// Property is known to be private or protected at this point
	// Private property is accessible if the property is within the declaring class
	if flags&ast.ModifierFlagsPrivate != 0 {
		declaringClassDeclaration := ast.GetClassLikeDeclarationOfSymbol(c.getParentOfSymbol(prop))
		if !c.isNodeWithinClass(location, declaringClassDeclaration) {
			if errorNode != nil {
				c.error(errorNode, diagnostics.Property_0_is_private_and_only_accessible_within_class_1, c.symbolToString(prop), c.TypeToString(c.getDeclaringClass(prop)))
			}
			return false
		}
		return true
	}
	// Property is known to be protected at this point
	// All protected properties of a supertype are accessible in a super access
	if isSuper {
		return true
	}
	// Find the first enclosing class that has the declaring classes of the protected constituents
	// of the property as base classes
	var enclosingClass *Type
	container := ast.GetContainingClass(location)
	for container != nil {
		class := c.getDeclaredTypeOfSymbol(c.getSymbolOfDeclaration(container))
		if c.isClassDerivedFromDeclaringClasses(class, prop, writing) {
			enclosingClass = class
			break
		}
		container = ast.GetContainingClass(container)
	}
	// A protected property is accessible if the property is within the declaring class or classes derived from it
	if enclosingClass == nil {
		// allow PropertyAccessibility if context is in function with this parameter
		// static member access is disallowed
		class := c.getEnclosingClassFromThisParameter(location)
		if class != nil && c.isClassDerivedFromDeclaringClasses(class, prop, writing) {
			enclosingClass = class
		}
		if flags&ast.ModifierFlagsStatic != 0 || enclosingClass == nil {
			if errorNode != nil {
				class := c.getDeclaringClass(prop)
				if class == nil {
					class = containingType
				}
				c.error(errorNode, diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses, c.symbolToString(prop), c.TypeToString(class))
			}
			return false
		}
	}
	// No further restrictions for static properties
	if flags&ast.ModifierFlagsStatic != 0 {
		return true
	}
	if containingType.flags&TypeFlagsTypeParameter != 0 {
		// get the original type -- represented as the type constraint of the 'this' type
		if containingType.AsTypeParameter().isThisType {
			containingType = c.getConstraintOfTypeParameter(containingType)
		} else {
			containingType = c.getBaseConstraintOfType(containingType)
		}
	}
	if containingType == nil || !c.hasBaseType(containingType, enclosingClass) {
		if errorNode != nil && containingType != nil {
			c.error(errorNode, diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1_This_is_an_instance_of_class_2, c.symbolToString(prop), c.TypeToString(enclosingClass), c.TypeToString(containingType))
		}
		return false
	}
	return true
}

func (c *Checker) symbolHasNonMethodDeclaration(symbol *ast.Symbol) bool {
	return c.forEachProperty(symbol, func(prop *ast.Symbol) bool { return prop.Flags&ast.SymbolFlagsMethod == 0 })
}

// Invoke the callback for each underlying property symbol of the given symbol and return the first
// value that isn't undefined.
func (c *Checker) forEachProperty(prop *ast.Symbol, callback func(p *ast.Symbol) bool) bool {
	if prop.CheckFlags&ast.CheckFlagsSynthetic == 0 {
		return callback(prop)
	}
	for _, t := range c.valueSymbolLinks.Get(prop).containingType.Types() {
		p := c.getPropertyOfType(t, prop.Name)
		if p != nil && c.forEachProperty(p, callback) {
			return true
		}
	}
	return false
}

// Return the declaring class type of a property or undefined if property not declared in class
func (c *Checker) getDeclaringClass(prop *ast.Symbol) *Type {
	if prop.Parent != nil && prop.Parent.Flags&ast.SymbolFlagsClass != 0 {
		return c.getDeclaredTypeOfSymbol(c.getParentOfSymbol(prop))
	}
	return nil
}

// Return true if source property is a valid override of protected parts of target property.
func (c *Checker) isValidOverrideOf(sourceProp *ast.Symbol, targetProp *ast.Symbol) bool {
	return !c.forEachProperty(targetProp, func(tp *ast.Symbol) bool {
		if getDeclarationModifierFlagsFromSymbol(tp)&ast.ModifierFlagsProtected != 0 {
			return !c.isPropertyInClassDerivedFrom(sourceProp, c.getDeclaringClass(tp))
		}
		return false
	})
}

// Return true if some underlying source property is declared in a class that derives
// from the given base class.
func (c *Checker) isPropertyInClassDerivedFrom(prop *ast.Symbol, baseClass *Type) bool {
	return c.forEachProperty(prop, func(sp *ast.Symbol) bool {
		sourceClass := c.getDeclaringClass(sp)
		if sourceClass != nil {
			return c.hasBaseType(sourceClass, baseClass)
		}
		return false
	})
}

func (c *Checker) isNodeUsedDuringClassInitialization(node *ast.Node) bool {
	return ast.FindAncestorOrQuit(node, func(element *ast.Node) ast.FindAncestorResult {
		if ast.IsConstructorDeclaration(element) && ast.NodeIsPresent(element.Body()) || ast.IsPropertyDeclaration(element) {
			return ast.FindAncestorTrue
		} else if ast.IsClassLike(element) || ast.IsFunctionLikeDeclaration(element) {
			return ast.FindAncestorQuit
		}
		return ast.FindAncestorFalse
	}) != nil
}

func (c *Checker) isNodeWithinClass(node *ast.Node, classDeclaration *ast.Node) bool {
	return c.forEachEnclosingClass(node, func(n *ast.Node) bool { return n == classDeclaration })
}

func (c *Checker) forEachEnclosingClass(node *ast.Node, callback func(node *ast.Node) bool) bool {
	containingClass := ast.GetContainingClass(node)
	for containingClass != nil {
		result := callback(containingClass)
		if result {
			return true
		}
		containingClass = ast.GetContainingClass(containingClass)
	}
	return false
}

// Return true if the given class derives from each of the declaring classes of the protected
// constituents of the given property.
func (c *Checker) isClassDerivedFromDeclaringClasses(checkClass *Type, prop *ast.Symbol, writing bool) bool {
	return !c.forEachProperty(prop, func(p *ast.Symbol) bool {
		if getDeclarationModifierFlagsFromSymbolEx(p, writing)&ast.ModifierFlagsProtected != 0 {
			return !c.hasBaseType(checkClass, c.getDeclaringClass(p))
		}
		return false
	})
}

func (c *Checker) getEnclosingClassFromThisParameter(node *ast.Node) *Type {
	// 'this' type for a node comes from, in priority order...
	// 1. The type of a syntactic 'this' parameter in the enclosing function scope
	thisParameter := getThisParameterFromNodeContext(node)
	var thisType *Type
	if thisParameter != nil && thisParameter.Type() != nil {
		thisType = c.getTypeFromTypeNode(thisParameter.Type())
	}
	if thisType != nil {
		// 2. The constraint of a type parameter used for an explicit 'this' parameter
		if thisType.flags&TypeFlagsTypeParameter != 0 {
			thisType = c.getConstraintOfTypeParameter(thisType)
		}
	} else {
		// 3. The 'this' parameter of a contextual type
		thisContainer := ast.GetThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/)
		if thisContainer != nil && ast.IsFunctionLike(thisContainer) {
			thisType = c.getContextualThisParameterType(thisContainer)
		}
	}
	if thisType != nil && thisType.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 {
		return getTargetType(thisType)
	}
	return nil
}

func getThisParameterFromNodeContext(node *ast.Node) *ast.Node {
	thisContainer := ast.GetThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/)
	if thisContainer != nil && ast.IsFunctionLike(thisContainer) {
		return ast.GetThisParameter(thisContainer)
	}
	return nil
}

func (c *Checker) getContextualThisParameterType(fn *ast.Node) *Type {
	if ast.IsArrowFunction(fn) {
		return nil
	}
	if c.isContextSensitiveFunctionOrObjectLiteralMethod(fn) {
		contextualSignature := c.getContextualSignature(fn)
		if contextualSignature != nil {
			thisParameter := contextualSignature.thisParameter
			if thisParameter != nil {
				return c.getTypeOfSymbol(thisParameter)
			}
		}
	}
	inJs := ast.IsInJSFile(fn)
	if c.noImplicitThis || inJs {
		containingLiteral := getContainingObjectLiteral(fn)
		if containingLiteral != nil {
			// We have an object literal method. Check if the containing object literal has a contextual type
			// that includes a ThisType<T>. If so, T is the contextual type for 'this'. We continue looking in
			// any directly enclosing object literals.
			contextualType := c.getApparentTypeOfContextualType(containingLiteral, ContextFlagsNone)
			thisType := c.getThisTypeOfObjectLiteralFromContextualType(containingLiteral, contextualType)
			if thisType != nil {
				return c.instantiateType(thisType, c.getMapperFromContext(c.getInferenceContext(containingLiteral)))
			}
			// There was no contextual ThisType<T> for the containing object literal, so the contextual type
			// for 'this' is the non-null form of the contextual type for the containing object literal or
			// the type of the object literal itself.
			if contextualType != nil {
				thisType = c.GetNonNullableType(contextualType)
			} else {
				thisType = c.checkExpressionCached(containingLiteral)
			}
			return c.getWidenedType(thisType)
		}
		// In an assignment of the form 'obj.xxx = function(...)' or 'obj[xxx] = function(...)', the
		// contextual type for 'this' is 'obj'.
		parent := ast.WalkUpParenthesizedExpressions(fn.Parent)
		if ast.IsAssignmentExpression(parent, false) {
			target := parent.AsBinaryExpression().Left
			if ast.IsAccessExpression(target) {
				expression := target.Expression()
				// Don't contextually type `this` as `exports` in `exports.Point = function(x, y) { this.x = x; this.y = y; }`
				if inJs && ast.IsIdentifier(expression) {
					sourceFile := ast.GetSourceFileOfNode(parent)
					if sourceFile.CommonJSModuleIndicator != nil && c.getResolvedSymbol(expression).Flags&ast.SymbolFlagsModuleExports != 0 {
						return nil
					}
				}
				return c.getWidenedType(c.checkExpressionCached(expression))
			}
		}
	}
	return nil
}

func (c *Checker) checkThisExpression(node *ast.Node) *Type {
	// Stop at the first arrow function so that we can
	// tell whether 'this' needs to be captured.
	container := ast.GetThisContainer(node, true /*includeArrowFunctions*/, true /*includeClassComputedPropertyName*/)
	capturedByArrowFunction := false
	thisInComputedPropertyName := false
	if ast.IsConstructorDeclaration(container) {
		c.checkThisBeforeSuper(node, container, diagnostics.X_super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class)
	}
	for {
		// Now skip arrow functions to get the "real" owner of 'this'.
		if ast.IsArrowFunction(container) {
			container = ast.GetThisContainer(container, false /*includeArrowFunctions*/, !thisInComputedPropertyName)
			capturedByArrowFunction = true
		}
		if ast.IsComputedPropertyName(container) {
			container = ast.GetThisContainer(container, !capturedByArrowFunction, false /*includeClassComputedPropertyName*/)
			thisInComputedPropertyName = true
			continue
		}
		break
	}
	c.checkThisInStaticClassFieldInitializerInDecoratedClass(node, container)
	if thisInComputedPropertyName {
		c.error(node, diagnostics.X_this_cannot_be_referenced_in_a_computed_property_name)
	} else {
		switch container.Kind {
		case ast.KindModuleDeclaration:
			c.error(node, diagnostics.X_this_cannot_be_referenced_in_a_module_or_namespace_body)
			// do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks
		case ast.KindEnumDeclaration:
			c.error(node, diagnostics.X_this_cannot_be_referenced_in_current_location)
			// do not return here so in case if lexical this is captured - it will be reflected in flags on NodeLinks
		}
	}
	t := c.tryGetThisTypeAtEx(node, true /*includeGlobalThis*/, container)
	if c.noImplicitThis {
		globalThisType := c.getTypeOfSymbol(c.globalThisSymbol)
		if t == globalThisType && capturedByArrowFunction {
			c.error(node, diagnostics.The_containing_arrow_function_captures_the_global_value_of_this)
		} else if t == nil {
			// With noImplicitThis, functions may not reference 'this' if it has type 'any'
			diag := c.error(node, diagnostics.X_this_implicitly_has_type_any_because_it_does_not_have_a_type_annotation)
			if !ast.IsSourceFile(container) {
				outsideThis := c.tryGetThisTypeAt(container)
				if outsideThis != nil && outsideThis != globalThisType {
					diag.AddRelatedInfo(createDiagnosticForNode(container, diagnostics.An_outer_value_of_this_is_shadowed_by_this_container))
				}
			}
		}
	}
	if t == nil {
		return c.anyType
	}
	return t
}

func (c *Checker) tryGetThisTypeAt(node *ast.Node) *Type {
	return c.tryGetThisTypeAtEx(node, true /*includeGlobalThis*/, nil /*container*/)
}

func (c *Checker) TryGetThisTypeAtEx(node *ast.Node, includeGlobalThis bool, container *ast.Node) *Type {
	reparsed := ast.GetReparsedNodeForNode(node)
	if reparsed.Flags&ast.NodeFlagsJSDoc != 0 && reparsed.Flags&ast.NodeFlagsReparsed == 0 {
		return nil // Binder doesn't process non-reparsed JSDoc nodes
	}
	return c.tryGetThisTypeAtEx(reparsed, includeGlobalThis, ast.GetReparsedNodeForNode(container))
}

func (c *Checker) tryGetThisTypeAtEx(node *ast.Node, includeGlobalThis bool, container *ast.Node) *Type {
	if container == nil {
		container = c.getThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/)
	}
	if ast.IsFunctionLike(container) && (!c.isInParameterInitializerBeforeContainingFunction(node) || ast.GetThisParameter(container) != nil) {
		sig := c.getSignatureOfFullSignatureType(container)
		if sig == nil {
			sig = c.getSignatureFromDeclaration(container)
		}
		thisType := c.getThisTypeOfSignature(sig)
		// Note: a parameter initializer should refer to class-this unless function-this is explicitly annotated.
		// If this is a function in a JS file, it might be a class method.
		if thisType == nil {
			thisType = c.getContextualThisParameterType(container)
		}
		if thisType != nil {
			return c.getFlowTypeOfReference(node, thisType)
		}
	}
	if container.Parent != nil && ast.IsClassLike(container.Parent) {
		symbol := c.getSymbolOfDeclaration(container.Parent)
		var t *Type
		if ast.IsStatic(container) {
			t = c.getTypeOfSymbol(symbol)
		} else {
			t = c.getDeclaredTypeOfSymbol(symbol).AsInterfaceType().thisType
		}
		return c.getFlowTypeOfReference(node, t)
	}
	if ast.IsSourceFile(container) {
		// look up in the source file's locals or exports
		if container.AsSourceFile().ExternalModuleIndicator != nil {
			// TODO: Maybe issue a better error than 'object is possibly undefined'
			return c.undefinedType
		}
		if includeGlobalThis {
			return c.getTypeOfSymbol(c.globalThisSymbol)
		}
	}
	return nil
}

func (c *Checker) getThisContainer(node *ast.Node, includeArrowFunctions bool, includeClassComputedPropertyName bool) *ast.Node {
	for {
		node = node.Parent
		if node == nil {
			// If we never pass in a SourceFile, this should be unreachable, since we'll stop when we reach that.
			panic("No parent in getThisContainer")
		}
		switch node.Kind {
		case ast.KindComputedPropertyName:
			// If the grandparent node is an object literal (as opposed to a class),
			// then the computed property is not a 'this' container.
			// A computed property name in a class needs to be a this container
			// so that we can error on it.
			if includeClassComputedPropertyName && ast.IsClassLike(node.Parent.Parent) {
				return node
			}
			// If this is a computed property, then the parent should not
			// make it a this container. The parent might be a property
			// in an object literal, like a method or accessor. But in order for
			// such a parent to be a this container, the reference must be in
			// the *body* of the container.
			node = node.Parent.Parent
		case ast.KindDecorator:
			// Decorators are always applied outside of the body of a class or method.
			if node.Parent.Kind == ast.KindParameter && ast.IsClassElement(node.Parent.Parent) {
				// If the decorator's parent is a Parameter, we resolve the this container from
				// the grandparent class declaration.
				node = node.Parent.Parent
			} else if ast.IsClassElement(node.Parent) {
				// If the decorator's parent is a class element, we resolve the 'this' container
				// from the parent class declaration.
				node = node.Parent
			}
		case ast.KindArrowFunction:
			if !includeArrowFunctions {
				continue
			}
			fallthrough
		case ast.KindFunctionDeclaration, ast.KindFunctionExpression, ast.KindModuleDeclaration, ast.KindClassStaticBlockDeclaration,
			ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor,
			ast.KindGetAccessor, ast.KindSetAccessor, ast.KindCallSignature, ast.KindConstructSignature, ast.KindIndexSignature,
			ast.KindEnumDeclaration, ast.KindSourceFile:
			return node
		}
	}
}

func (c *Checker) isInParameterInitializerBeforeContainingFunction(node *ast.Node) bool {
	inBindingInitializer := false
	for node.Parent != nil && !ast.IsFunctionLike(node.Parent) {
		if ast.IsParameterDeclaration(node.Parent) {
			if inBindingInitializer || node.Parent.Initializer() == node {
				return true
			}
		}

		if ast.IsBindingElement(node.Parent) && node.Parent.Initializer() == node {
			inBindingInitializer = true
		}

		node = node.Parent
	}

	return false
}

func (c *Checker) checkThisInStaticClassFieldInitializerInDecoratedClass(thisExpression *ast.Node, container *ast.Node) {
	if ast.IsPropertyDeclaration(container) && ast.HasStaticModifier(container) && c.legacyDecorators {
		initializer := container.Initializer()
		if initializer != nil && initializer.Loc.ContainsInclusive(thisExpression.Pos()) && ast.HasDecorators(container.Parent) {
			c.error(thisExpression, diagnostics.Cannot_use_this_in_a_static_property_initializer_of_a_decorated_class)
		}
	}
}

func (c *Checker) checkThisBeforeSuper(node *ast.Node, container *ast.Node, diagnosticMessage *diagnostics.Message) {
	containingClassDecl := container.Parent
	baseTypeNode := ast.GetExtendsHeritageClauseElement(containingClassDecl)
	// If a containing class does not have extends clause or the class extends null
	// skip checking whether super statement is called before "this" accessing.
	if baseTypeNode != nil && !c.classDeclarationExtendsNull(containingClassDecl) {
		if node.FlowNodeData() != nil && !c.isPostSuperFlowNode(node.FlowNodeData().FlowNode, false /*noCacheCheck*/) {
			c.error(node, diagnosticMessage)
		}
	}
}

/**
 * Check if the given class-declaration extends null then return true.
 * Otherwise, return false
 * @param classDecl a class declaration to check if it extends null
 */
func (c *Checker) classDeclarationExtendsNull(classDecl *ast.Node) bool {
	classSymbol := c.getSymbolOfDeclaration(classDecl)
	classInstanceType := c.getDeclaredTypeOfSymbol(classSymbol)
	baseConstructorType := c.getBaseConstructorTypeOfClass(classInstanceType)
	return baseConstructorType == c.nullWideningType
}

func (c *Checker) checkAssertion(node *ast.Node, checkMode CheckMode) *Type {
	if node.Kind == ast.KindTypeAssertionExpression {
		if c.shouldCheckErasableSyntax(node) {
			c.addDiagnostic(ast.NewDiagnostic(ast.GetSourceFileOfNode(node), core.NewTextRange(scanner.SkipTrivia(ast.GetSourceFileOfNode(node).Text(), node.Pos()), node.Expression().Pos()), diagnostics.This_syntax_is_not_allowed_when_erasableSyntaxOnly_is_enabled))
		}
	}
	typeNode := node.Type()
	exprType := c.checkExpressionEx(node.Expression(), checkMode)
	if isConstTypeReference(typeNode) {
		if !c.isValidConstAssertionArgument(node.Expression()) {
			c.error(node.Expression(), diagnostics.A_const_assertion_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals)
		}
		return c.getRegularTypeOfLiteralType(exprType)
	}
	links := c.assertionLinks.Get(node)
	links.exprType = exprType
	c.checkSourceElement(typeNode)
	c.checkNodeDeferred(node)
	return c.getTypeFromTypeNode(typeNode)
}

func (c *Checker) checkAssertionDeferred(node *ast.Node) {
	typeNode := node.Type()
	exprType := c.getRegularTypeOfObjectLiteral(c.getBaseTypeOfLiteralType(c.assertionLinks.Get(node).exprType))
	targetType := c.getTypeFromTypeNode(typeNode)
	if !c.isErrorType(targetType) {
		widenedType := c.getWidenedType(exprType)
		if !c.isTypeComparableTo(targetType, widenedType) {
			errNode := node
			if typeNode.Flags&ast.NodeFlagsReparsed != 0 {
				errNode = typeNode
			}
			c.checkTypeComparableTo(exprType, targetType, errNode, diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first)
		}
	}
}

func (c *Checker) checkBinaryExpression(node *ast.Node, checkMode CheckMode) *Type {
	binary := node.AsBinaryExpression()
	return c.checkBinaryLikeExpression(binary.Left, binary.OperatorToken, binary.Right, checkMode, node)
}

func (c *Checker) checkBinaryLikeExpression(left *ast.Node, operatorToken *ast.Node, right *ast.Node, checkMode CheckMode, errorNode *ast.Node) *Type {
	operator := operatorToken.Kind
	if operator == ast.KindEqualsToken && (left.Kind == ast.KindObjectLiteralExpression || left.Kind == ast.KindArrayLiteralExpression) {
		return c.checkDestructuringAssignment(left, c.checkExpressionEx(right, checkMode), checkMode, right.Kind == ast.KindThisKeyword)
	}
	leftType := c.checkExpressionEx(left, checkMode)
	rightType := c.checkExpressionEx(right, checkMode)
	if ast.IsLogicalOrCoalescingBinaryOperator(operator) {
		parent := left.Parent.Parent
		for ast.IsParenthesizedExpression(parent) || ast.IsLogicalOrCoalescingBinaryExpression(parent) {
			parent = parent.Parent
		}
		if operator == ast.KindAmpersandAmpersandToken || ast.IsIfStatement(parent) {
			var body *ast.Node
			if ast.IsIfStatement(parent) {
				body = parent.AsIfStatement().ThenStatement
			}
			c.checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(left, leftType, body)
		}
		if ast.IsLogicalBinaryOperator(operator) {
			c.checkTruthinessOfType(leftType, left)
		}
	}
	switch operator {
	case ast.KindAsteriskToken, ast.KindAsteriskAsteriskToken, ast.KindAsteriskEqualsToken, ast.KindAsteriskAsteriskEqualsToken,
		ast.KindSlashToken, ast.KindSlashEqualsToken, ast.KindPercentToken, ast.KindPercentEqualsToken, ast.KindMinusToken,
		ast.KindMinusEqualsToken, ast.KindLessThanLessThanToken, ast.KindLessThanLessThanEqualsToken, ast.KindGreaterThanGreaterThanToken,
		ast.KindGreaterThanGreaterThanEqualsToken, ast.KindGreaterThanGreaterThanGreaterThanToken, ast.KindGreaterThanGreaterThanGreaterThanEqualsToken,
		ast.KindBarToken, ast.KindBarEqualsToken, ast.KindCaretToken, ast.KindCaretEqualsToken, ast.KindAmpersandToken, ast.KindAmpersandEqualsToken:
		if leftType == c.silentNeverType || rightType == c.silentNeverType {
			return c.silentNeverType
		}
		leftType = c.checkNonNullType(leftType, left)
		rightType = c.checkNonNullType(rightType, right)
		// if a user tries to apply a bitwise operator to 2 boolean operands
		// try and return them a helpful suggestion
		if leftType.flags&TypeFlagsBooleanLike != 0 && rightType.flags&TypeFlagsBooleanLike != 0 {
			suggestedOperator := c.getSuggestedBooleanOperator(operator)
			if suggestedOperator != ast.KindUnknown {
				c.error(operatorToken, diagnostics.The_0_operator_is_not_allowed_for_boolean_types_Consider_using_1_instead, scanner.TokenToString(operatorToken.Kind), scanner.TokenToString(suggestedOperator))
				return c.numberType
			}
		}
		// otherwise just check each operand separately and report errors as normal
		leftOk := c.checkArithmeticOperandType(left, leftType, diagnostics.The_left_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, true /*isAwaitValid*/)
		rightOk := c.checkArithmeticOperandType(right, rightType, diagnostics.The_right_hand_side_of_an_arithmetic_operation_must_be_of_type_any_number_bigint_or_an_enum_type, true /*isAwaitValid*/)
		var resultType *Type
		// If both are any or unknown, allow operation; assume it will resolve to number
		if c.isTypeAssignableToKind(leftType, TypeFlagsAnyOrUnknown) && c.isTypeAssignableToKind(rightType, TypeFlagsAnyOrUnknown) || !c.maybeTypeOfKind(leftType, TypeFlagsBigIntLike) && !c.maybeTypeOfKind(rightType, TypeFlagsBigIntLike) {
			resultType = c.numberType
		} else if c.bothAreBigIntLike(leftType, rightType) {
			switch operator {
			case ast.KindGreaterThanGreaterThanGreaterThanToken, ast.KindGreaterThanGreaterThanGreaterThanEqualsToken:
				c.reportOperatorError(leftType, operator, rightType, errorNode, nil)
			case ast.KindAsteriskAsteriskToken, ast.KindAsteriskAsteriskEqualsToken:
				if c.languageVersion < core.ScriptTargetES2016 {
					c.error(errorNode, diagnostics.Exponentiation_cannot_be_performed_on_bigint_values_unless_the_target_option_is_set_to_es2016_or_later)
				}
			}
			resultType = c.bigintType
		} else {
			c.reportOperatorError(leftType, operator, rightType, errorNode, c.bothAreBigIntLike)
			resultType = c.errorType
		}
		if leftOk && rightOk {
			c.checkAssignmentOperator(left, operator, right, leftType, resultType)
			switch operator {
			case ast.KindLessThanLessThanToken, ast.KindLessThanLessThanEqualsToken, ast.KindGreaterThanGreaterThanToken,
				ast.KindGreaterThanGreaterThanEqualsToken, ast.KindGreaterThanGreaterThanGreaterThanToken,
				ast.KindGreaterThanGreaterThanGreaterThanEqualsToken:
				rhsEval := c.evaluate(right, right)
				if numValue, ok := rhsEval.Value.(jsnum.Number); ok && numValue.Abs() >= 32 {
					// Elevate from suggestion to error within an enum member
					c.errorOrSuggestion(ast.IsEnumMember(ast.WalkUpParenthesizedExpressions(right.Parent.Parent)), errorNode, diagnostics.This_operation_can_be_simplified_This_shift_is_identical_to_0_1_2, scanner.GetTextOfNode(left), scanner.TokenToString(operator), numValue.Remainder(32))
				}
			}
		}
		return resultType
	case ast.KindPlusToken, ast.KindPlusEqualsToken:
		if leftType == c.silentNeverType || rightType == c.silentNeverType {
			return c.silentNeverType
		}
		if !c.isTypeAssignableToKind(leftType, TypeFlagsStringLike) && !c.isTypeAssignableToKind(rightType, TypeFlagsStringLike) {
			leftType = c.checkNonNullType(leftType, left)
			rightType = c.checkNonNullType(rightType, right)
		}
		var resultType *Type
		if c.isTypeAssignableToKindEx(leftType, TypeFlagsNumberLike, true /*strict*/) && c.isTypeAssignableToKindEx(rightType, TypeFlagsNumberLike, true /*strict*/) {
			// Operands of an enum type are treated as having the primitive type Number.
			// If both operands are of the Number primitive type, the result is of the Number primitive type.
			resultType = c.numberType
		} else if c.isTypeAssignableToKindEx(leftType, TypeFlagsBigIntLike, true /*strict*/) && c.isTypeAssignableToKindEx(rightType, TypeFlagsBigIntLike, true /*strict*/) {
			// If both operands are of the BigInt primitive type, the result is of the BigInt primitive type.
			resultType = c.bigintType
		} else if c.isTypeAssignableToKindEx(leftType, TypeFlagsStringLike, true /*strict*/) || c.isTypeAssignableToKindEx(rightType, TypeFlagsStringLike, true /*strict*/) {
			// If one or both operands are of the String primitive type, the result is of the String primitive type.
			resultType = c.stringType
		} else if IsTypeAny(leftType) || IsTypeAny(rightType) {
			// Otherwise, the result is of type Any.
			// NOTE: unknown type here denotes error type. Old compiler treated this case as any type so do we.
			if c.isErrorType(leftType) || c.isErrorType(rightType) {
				resultType = c.errorType
			} else {
				resultType = c.anyType
			}
		}
		// Symbols are not allowed at all in arithmetic expressions
		if resultType != nil && !c.checkForDisallowedESSymbolOperand(left, right, leftType, rightType, operator) {
			return resultType
		}
		if resultType == nil {
			// Types that have a reasonably good chance of being a valid operand type.
			// If both types have an awaited type of one of these, we'll assume the user
			// might be missing an await without doing an exhaustive check that inserting
			// await(s) will actually be a completely valid binary expression.
			closeEnoughKind := TypeFlagsNumberLike | TypeFlagsBigIntLike | TypeFlagsStringLike | TypeFlagsAnyOrUnknown
			c.reportOperatorError(leftType, operator, rightType, errorNode, func(left *Type, right *Type) bool {
				return c.isTypeAssignableToKind(left, closeEnoughKind) && c.isTypeAssignableToKind(right, closeEnoughKind)
			})
			return c.anyType
		}
		if operator == ast.KindPlusEqualsToken {
			c.checkAssignmentOperator(left, operator, right, leftType, resultType)
		}
		return resultType
	case ast.KindLessThanToken, ast.KindGreaterThanToken, ast.KindLessThanEqualsToken, ast.KindGreaterThanEqualsToken:
		if c.checkForDisallowedESSymbolOperand(left, right, leftType, rightType, operator) {
			leftType = c.getBaseTypeOfLiteralTypeForComparison(c.checkNonNullType(leftType, left))
			rightType = c.getBaseTypeOfLiteralTypeForComparison(c.checkNonNullType(rightType, right))
			c.reportOperatorErrorUnless(leftType, operator, rightType, errorNode, func(left *Type, right *Type) bool {
				if IsTypeAny(left) || IsTypeAny(right) {
					return true
				}
				leftAssignableToNumber := c.isTypeAssignableTo(left, c.numberOrBigIntType)
				rightAssignableToNumber := c.isTypeAssignableTo(right, c.numberOrBigIntType)
				return leftAssignableToNumber && rightAssignableToNumber || !leftAssignableToNumber && !rightAssignableToNumber && c.areTypesComparable(left, right)
			})
		}
		return c.booleanType
	case ast.KindEqualsEqualsToken, ast.KindExclamationEqualsToken, ast.KindEqualsEqualsEqualsToken, ast.KindExclamationEqualsEqualsToken:
		// We suppress errors in CheckMode.TypeOnly (meaning the invocation came from getTypeOfExpression). During
		// control flow analysis it is possible for operands to temporarily have narrower types, and those narrower
		// types may cause the operands to not be comparable. We don't want such errors reported (see #46475).
		if checkMode&CheckModeTypeOnly == 0 {
			if (isLiteralExpressionOfObject(left) || isLiteralExpressionOfObject(right)) &&
				// only report for === and !== in JS, not == or !=
				(!ast.IsInJSFile(left) || (operator == ast.KindEqualsEqualsEqualsToken || operator == ast.KindExclamationEqualsEqualsToken)) {
				eqType := operator == ast.KindEqualsEqualsToken || operator == ast.KindEqualsEqualsEqualsToken
				c.error(errorNode, diagnostics.This_condition_will_always_return_0_since_JavaScript_compares_objects_by_reference_not_value, core.IfElse(eqType, "false", "true"))
			}
			c.checkNaNEquality(errorNode, operator, left, right)
			c.reportOperatorErrorUnless(leftType, operator, rightType, errorNode, func(left *Type, right *Type) bool {
				return c.isTypeEqualityComparableTo(left, right) || c.isTypeEqualityComparableTo(right, left)
			})
		}
		return c.booleanType
	case ast.KindInstanceOfKeyword:
		return c.checkInstanceOfExpression(left, right, leftType, rightType, checkMode)
	case ast.KindInKeyword:
		return c.checkInExpression(left, right, leftType, rightType)
	case ast.KindAmpersandAmpersandToken, ast.KindAmpersandAmpersandEqualsToken:
		resultType := leftType
		if c.hasTypeFacts(leftType, TypeFactsTruthy) {
			t := leftType
			if !c.strictNullChecks {
				t = c.getBaseTypeOfLiteralType(rightType)
			}
			resultType = c.getUnionType([]*Type{c.extractDefinitelyFalsyTypes(t), rightType})
		}
		if operator == ast.KindAmpersandAmpersandEqualsToken {
			c.checkAssignmentOperator(left, operator, right, leftType, rightType)
		}
		return resultType
	case ast.KindBarBarToken, ast.KindBarBarEqualsToken:
		resultType := leftType
		if c.hasTypeFacts(leftType, TypeFactsFalsy) {
			resultType = c.getUnionTypeEx([]*Type{c.GetNonNullableType(c.removeDefinitelyFalsyTypes(leftType)), rightType}, UnionReductionSubtype, nil, nil)
		}
		if operator == ast.KindBarBarEqualsToken {
			c.checkAssignmentOperator(left, operator, right, leftType, rightType)
		}
		return resultType
	case ast.KindQuestionQuestionToken, ast.KindQuestionQuestionEqualsToken:
		if operator == ast.KindQuestionQuestionToken {
			c.checkNullishCoalesceOperands(left, right)
		}
		resultType := leftType
		if c.hasTypeFacts(leftType, TypeFactsEQUndefinedOrNull) {
			resultType = c.getUnionTypeEx([]*Type{c.GetNonNullableType(leftType), rightType}, UnionReductionSubtype, nil, nil)
		}
		if operator == ast.KindQuestionQuestionEqualsToken {
			c.checkAssignmentOperator(left, operator, right, leftType, rightType)
		}
		return resultType
	case ast.KindEqualsToken:
		c.checkAssignmentOperator(left, operator, right, leftType, rightType)
		return rightType
	case ast.KindCommaToken:
		if !c.compilerOptions.AllowUnreachableCode.IsTrue() && c.isSideEffectFree(left) && !c.isIndirectCall(left.Parent) {
			sf := ast.GetSourceFileOfNode(left)
			start := scanner.SkipTrivia(sf.Text(), left.Pos())
			isInDiag2657 := core.Some(sf.Diagnostics(), func(d *ast.Diagnostic) bool {
				if d.Code() != diagnostics.JSX_expressions_must_have_one_parent_element.Code() {
					return false
				}
				return d.Loc().Contains(start)
			})
			if !isInDiag2657 {
				c.error(left, diagnostics.Left_side_of_comma_operator_is_unused_and_has_no_side_effects)
			}
		}
		return rightType
	}
	panic("Unhandled case in checkBinaryLikeExpression")
}

func (c *Checker) checkDestructuringAssignment(node *ast.Node, sourceType *Type, checkMode CheckMode, rightIsThis bool) *Type {
	var target *ast.Node
	if ast.IsShorthandPropertyAssignment(node) {
		initializer := node.AsShorthandPropertyAssignment().ObjectAssignmentInitializer
		if initializer != nil {
			// In strict null checking mode, if a default value of a non-undefined type is specified, remove
			// undefined from the final type.
			if c.strictNullChecks && !c.hasTypeFacts(c.checkExpression(initializer), TypeFactsIsUndefined) {
				sourceType = c.getTypeWithFacts(sourceType, TypeFactsNEUndefined)
			}
			c.checkBinaryLikeExpression(node.Name(), node.AsShorthandPropertyAssignment().EqualsToken, initializer, checkMode, nil)
		}
		target = node.Name()
	} else {
		target = node
	}
	if ast.IsBinaryExpression(target) && target.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken {
		c.checkBinaryExpression(target, checkMode)
		target = target.AsBinaryExpression().Left
		// A default value is specified, so remove undefined from the final type.
		if c.strictNullChecks {
			sourceType = c.getTypeWithFacts(sourceType, TypeFactsNEUndefined)
		}
	}
	if ast.IsObjectLiteralExpression(target) {
		return c.checkObjectLiteralAssignment(target, sourceType, rightIsThis)
	}
	if ast.IsArrayLiteralExpression(target) {
		return c.checkArrayLiteralAssignment(target, sourceType, checkMode)
	}
	return c.checkReferenceAssignment(target, sourceType, checkMode)
}

func (c *Checker) checkObjectLiteralAssignment(node *ast.Node, sourceType *Type, rightIsThis bool) *Type {
	properties := node.PropertyList()
	if c.strictNullChecks && len(properties.Nodes) == 0 {
		return c.checkNonNullType(sourceType, node)
	}
	for i := range properties.Nodes {
		c.checkObjectLiteralDestructuringPropertyAssignment(node, sourceType, i, properties, rightIsThis)
	}
	return sourceType
}

// Note: If property cannot be a SpreadAssignment, then allProperties does not need to be provided
func (c *Checker) checkObjectLiteralDestructuringPropertyAssignment(node *ast.Node, objectLiteralType *Type, propertyIndex int, allProperties *ast.NodeList, rightIsThis bool) *Type {
	properties := node.Properties()
	property := properties[propertyIndex]
	if ast.IsPropertyAssignment(property) || ast.IsShorthandPropertyAssignment(property) {
		name := property.Name()
		exprType := c.getLiteralTypeFromPropertyName(name)
		if isTypeUsableAsPropertyName(exprType) {
			text := getPropertyNameFromType(exprType)
			prop := c.getPropertyOfType(objectLiteralType, text)
			if prop != nil {
				c.markPropertyAsReferenced(prop, property, rightIsThis)
				c.checkPropertyAccessibility(property, false /*isSuper*/, true /*writing*/, objectLiteralType, prop)
			}
		}
		elementType := c.getIndexedAccessTypeEx(objectLiteralType, exprType, AccessFlagsExpressionPosition|core.IfElse(c.hasDefaultValue(property), AccessFlagsAllowMissing, 0), name, nil)
		t := c.getFlowTypeOfDestructuring(property, elementType)
		expr := property
		if ast.IsPropertyAssignment(property) {
			expr = property.Initializer()
		}
		return c.checkDestructuringAssignment(expr, t, CheckModeNormal, false)
	}
	if ast.IsSpreadAssignment(property) {
		if propertyIndex < len(properties)-1 {
			c.error(property, diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern)
			return nil
		}
		if c.languageVersion < LanguageFeatureMinimumTarget.ObjectSpreadRest {
			c.checkExternalEmitHelpers(property, ExternalEmitHelpersRest)
		}
		var nonRestNames []*ast.Node
		if allProperties != nil {
			for _, otherProperty := range allProperties.Nodes {
				if !ast.IsSpreadAssignment(otherProperty) {
					nonRestNames = append(nonRestNames, otherProperty.Name())
				}
			}
		}
		t := c.getRestType(objectLiteralType, nonRestNames, objectLiteralType.symbol)
		c.checkGrammarForDisallowedTrailingComma(allProperties, diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma)
		return c.checkDestructuringAssignment(property.Expression(), t, CheckModeNormal, false)
	}
	c.error(property, diagnostics.Property_assignment_expected)
	return nil
}

func (c *Checker) checkArrayLiteralAssignment(node *ast.Node, sourceType *Type, checkMode CheckMode) *Type {
	elements := node.Elements()
	// This elementType will be used if the specific property corresponding to this index is not
	// present (aka the tuple element property). This call also checks that the parentType is in
	// fact an iterable or array (depending on target language).
	possiblyOutOfBoundsType := core.OrElse(c.checkIteratedTypeOrElementType(IterationUseDestructuring|IterationUsePossiblyOutOfBounds, sourceType, c.undefinedType, node), c.errorType)
	inBoundsType := core.IfElse(c.compilerOptions.NoUncheckedIndexedAccess == core.TSTrue, nil, possiblyOutOfBoundsType)
	for i := range elements {
		t := possiblyOutOfBoundsType
		if elements[i].Kind == ast.KindSpreadElement {
			if inBoundsType == nil {
				inBoundsType = core.OrElse(c.checkIteratedTypeOrElementType(IterationUseDestructuring, sourceType, c.undefinedType, node), c.errorType)
			}
			t = inBoundsType
		}
		c.checkArrayLiteralDestructuringElementAssignment(node, sourceType, i, t, checkMode)
	}
	return sourceType
}

func (c *Checker) checkArrayLiteralDestructuringElementAssignment(node *ast.Node, sourceType *Type, elementIndex int, elementType *Type, checkMode CheckMode) *Type {
	elements := node.ElementList()
	element := elements.Nodes[elementIndex]
	if !ast.IsOmittedExpression(element) {
		if !ast.IsSpreadElement(element) {
			indexType := c.getNumberLiteralType(jsnum.Number(elementIndex))
			if c.isArrayLikeType(sourceType) {
				// We create a synthetic expression so that getIndexedAccessType doesn't get confused
				// when the element is a SyntaxKind.ElementAccessExpression.
				accessFlags := AccessFlagsExpressionPosition | core.IfElse(c.hasDefaultValue(element), AccessFlagsAllowMissing, 0)
				elementType := core.OrElse(c.getIndexedAccessTypeOrUndefined(sourceType, indexType, accessFlags, c.createSyntheticExpression(element, indexType, false, nil), nil), c.errorType)
				assignedType := elementType
				if c.hasDefaultValue(element) {
					assignedType = c.getTypeWithFacts(elementType, TypeFactsNEUndefined)
				}
				t := c.getFlowTypeOfDestructuring(element, assignedType)
				return c.checkDestructuringAssignment(element, t, checkMode, false)
			}
			return c.checkDestructuringAssignment(element, elementType, checkMode, false)
		}
		if elementIndex < len(elements.Nodes)-1 {
			c.error(element, diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern)
		} else {
			restExpression := element.Expression()
			if ast.IsBinaryExpression(restExpression) && restExpression.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken {
				c.error(restExpression.AsBinaryExpression().OperatorToken, diagnostics.A_rest_element_cannot_have_an_initializer)
			} else {
				c.checkGrammarForDisallowedTrailingComma(elements, diagnostics.A_rest_parameter_or_binding_pattern_may_not_have_a_trailing_comma)
				var t *Type
				if everyType(sourceType, isTupleType) {
					t = c.mapType(sourceType, func(t *Type) *Type { return c.sliceTupleType(t, elementIndex, 0) })
				} else {
					t = c.createArrayType(elementType)
				}
				return c.checkDestructuringAssignment(restExpression, t, checkMode, false)
			}
		}
	}
	return nil
}

func (c *Checker) checkReferenceAssignment(target *ast.Node, sourceType *Type, checkMode CheckMode) *Type {
	targetType := c.checkExpressionEx(target, checkMode)
	message := core.IfElse(ast.IsSpreadAssignment(target.Parent),
		diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access,
		diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access)
	optionalMessage := core.IfElse(ast.IsSpreadAssignment(target.Parent),
		diagnostics.The_target_of_an_object_rest_assignment_may_not_be_an_optional_property_access,
		diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access)
	if c.checkReferenceExpression(target, message, optionalMessage) {
		c.checkTypeAssignableToAndOptionallyElaborate(sourceType, targetType, target, target, nil, nil)
	}
	return sourceType
}

func (c *Checker) reportOperatorError(leftType *Type, operator ast.Kind, rightType *Type, errorNode *ast.Node, isRelated func(left *Type, right *Type) bool) {
	wouldWorkWithAwait := false
	if isRelated != nil {
		awaitedLeftType := c.getAwaitedTypeNoAlias(leftType)
		awaitedRightType := c.getAwaitedTypeNoAlias(rightType)
		wouldWorkWithAwait = !(awaitedLeftType == leftType && awaitedRightType == rightType) && awaitedLeftType != nil && awaitedRightType != nil && isRelated(awaitedLeftType, awaitedRightType)
	}
	effectiveLeft := leftType
	effectiveRight := rightType
	if !wouldWorkWithAwait && isRelated != nil {
		effectiveLeft, effectiveRight = c.getBaseTypesIfUnrelated(leftType, rightType, isRelated)
	}
	leftStr, rightStr := c.getTypeNamesForErrorDisplay(effectiveLeft, effectiveRight)
	switch operator {
	case ast.KindEqualsEqualsEqualsToken, ast.KindEqualsEqualsToken, ast.KindExclamationEqualsEqualsToken, ast.KindExclamationEqualsToken:
		c.errorAndMaybeSuggestAwait(errorNode, wouldWorkWithAwait, diagnostics.This_comparison_appears_to_be_unintentional_because_the_types_0_and_1_have_no_overlap, leftStr, rightStr)
	default:
		c.errorAndMaybeSuggestAwait(errorNode, wouldWorkWithAwait, diagnostics.Operator_0_cannot_be_applied_to_types_1_and_2, scanner.TokenToString(operator), leftStr, rightStr)
	}
}

func (c *Checker) reportOperatorErrorUnless(leftType *Type, operator ast.Kind, rightType *Type, errorNode *ast.Node, typesAreCompatible func(left *Type, right *Type) bool) {
	if !typesAreCompatible(leftType, rightType) {
		c.reportOperatorError(leftType, operator, rightType, errorNode, typesAreCompatible)
	}
}

func (c *Checker) getBaseTypesIfUnrelated(leftType *Type, rightType *Type, isRelated func(left *Type, right *Type) bool) (*Type, *Type) {
	effectiveLeft := leftType
	effectiveRight := rightType
	leftBase := c.getBaseTypeOfLiteralType(leftType)
	rightBase := c.getBaseTypeOfLiteralType(rightType)
	if !isRelated(leftBase, rightBase) {
		effectiveLeft = leftBase
		effectiveRight = rightBase
	}
	return effectiveLeft, effectiveRight
}

func (c *Checker) checkAssignmentOperator(left *ast.Node, operator ast.Kind, right *ast.Node, leftType *Type, rightType *Type) {
	if ast.IsAssignmentOperator(operator) {
		// We ignore assignments of undefined to CommonJS exports when there are multiple assignment declarations
		if ast.IsDeclarationNode(left.Parent) && ast.GetAssignmentDeclarationKind(left.Parent) == ast.JSDeclarationKindExportsProperty {
			if symbol := c.symbolNodeLinks.Get(left).resolvedSymbol; symbol != nil && len(symbol.Declarations) > 1 && rightType.flags&TypeFlagsUndefined != 0 {
				return
			}
		}
		// getters can be a subtype of setters, so to check for assignability we use the setter's type instead
		if ast.IsCompoundAssignment(operator) && ast.IsPropertyAccessExpression(left) {
			leftType = c.checkPropertyAccessExpression(left, CheckModeNormal, true /*writeOnly*/)
		}
		if c.checkReferenceExpression(left, diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access, diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access) {
			var headMessage *diagnostics.Message
			if c.exactOptionalPropertyTypes && ast.IsPropertyAccessExpression(left) && c.maybeTypeOfKind(rightType, TypeFlagsUndefined) {
				target := c.getTypeOfPropertyOfType(c.getTypeOfExpression(left.Expression()), left.Name().Text())
				if c.isExactOptionalPropertyMismatch(rightType, target) {
					headMessage = diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_type_of_the_target
				}
			}
			// to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported
			c.checkTypeAssignableToAndOptionallyElaborate(rightType, leftType, left, right, headMessage, nil)
		}
	}
}

func (c *Checker) bothAreBigIntLike(left *Type, right *Type) bool {
	return c.isTypeAssignableToKind(left, TypeFlagsBigIntLike) && c.isTypeAssignableToKind(right, TypeFlagsBigIntLike)
}

func (c *Checker) getSuggestedBooleanOperator(operator ast.Kind) ast.Kind {
	switch operator {
	case ast.KindBarToken, ast.KindBarEqualsToken:
		return ast.KindBarBarToken
	case ast.KindCaretToken, ast.KindCaretEqualsToken:
		return ast.KindExclamationEqualsEqualsToken
	case ast.KindAmpersandToken, ast.KindAmpersandEqualsToken:
		return ast.KindAmpersandAmpersandToken
	}
	return ast.KindUnknown
}

func (c *Checker) checkArithmeticOperandType(operand *ast.Node, t *Type, diagnostic *diagnostics.Message, isAwaitValid bool) bool {
	if !c.isTypeAssignableTo(t, c.numberOrBigIntType) {
		var awaitedType *Type
		if isAwaitValid {
			awaitedType = c.getAwaitedTypeOfPromise(t)
		}
		c.errorAndMaybeSuggestAwait(operand, awaitedType != nil && c.isTypeAssignableTo(awaitedType, c.numberOrBigIntType), diagnostic)
		return false
	}
	return true
}

// Return true if there was no error, false if there was an error.
func (c *Checker) checkForDisallowedESSymbolOperand(left *ast.Node, right *ast.Node, leftType *Type, rightType *Type, operator ast.Kind) bool {
	var offendingSymbolOperand *ast.Node
	switch {
	case c.maybeTypeOfKindConsideringBaseConstraint(leftType, TypeFlagsESSymbolLike):
		offendingSymbolOperand = left
	case c.maybeTypeOfKindConsideringBaseConstraint(rightType, TypeFlagsESSymbolLike):
		offendingSymbolOperand = right
	}
	if offendingSymbolOperand != nil {
		c.error(offendingSymbolOperand, diagnostics.The_0_operator_cannot_be_applied_to_type_symbol, scanner.TokenToString(operator))
		return false
	}
	return true
}

func (c *Checker) checkNaNEquality(errorNode *ast.Node, operator ast.Kind, left *ast.Expression, right *ast.Expression) {
	isLeftNaN := c.isGlobalNaN(ast.SkipParentheses(left))
	isRightNaN := c.isGlobalNaN(ast.SkipParentheses(right))
	if isLeftNaN || isRightNaN {
		err := c.error(errorNode, diagnostics.This_condition_will_always_return_0, scanner.TokenToString(core.IfElse(operator == ast.KindEqualsEqualsEqualsToken || operator == ast.KindEqualsEqualsToken, ast.KindFalseKeyword, ast.KindTrueKeyword)))
		if isLeftNaN && isRightNaN {
			return
		}
		var operatorString string
		if operator == ast.KindExclamationEqualsEqualsToken || operator == ast.KindExclamationEqualsToken {
			operatorString = scanner.TokenToString(ast.KindExclamationToken)
		}
		location := left
		if isLeftNaN {
			location = right
		}
		expression := ast.SkipParentheses(location)
		entityName := "..."
		if ast.IsEntityNameExpression(expression) {
			entityName = entityNameToString(expression)
		}
		suggestion := operatorString + "Number.isNaN(" + entityName + ")"
		err.AddRelatedInfo(createDiagnosticForNode(location, diagnostics.Did_you_mean_0, suggestion))
	}
}

func (c *Checker) isGlobalNaN(expr *ast.Expression) bool {
	if ast.IsIdentifier(expr) && expr.Text() == "NaN" {
		globalNaNSymbol := c.getGlobalNaNSymbolOrNil()
		return globalNaNSymbol != nil && globalNaNSymbol == c.getResolvedSymbol(expr)
	}
	return false
}

func (c *Checker) isTypeEqualityComparableTo(source *Type, target *Type) bool {
	return (target.flags&TypeFlagsNullable) != 0 || c.isTypeComparableTo(source, target)
}

func (c *Checker) checkTruthinessOfType(t *Type, node *ast.Node) *Type {
	if t.flags&TypeFlagsVoid != 0 {
		c.error(node, diagnostics.An_expression_of_type_void_cannot_be_tested_for_truthiness)
		return t
	}
	semantics := c.getSyntacticTruthySemantics(node)
	if semantics != PredicateSemanticsSometimes {
		c.error(node, core.IfElse(semantics == PredicateSemanticsAlways, diagnostics.This_kind_of_expression_is_always_truthy, diagnostics.This_kind_of_expression_is_always_falsy))
	}
	return t
}

type PredicateSemantics uint32

const (
	PredicateSemanticsNone      PredicateSemantics = 0
	PredicateSemanticsAlways    PredicateSemantics = 1 << 0
	PredicateSemanticsNever     PredicateSemantics = 1 << 1
	PredicateSemanticsSometimes                    = PredicateSemanticsAlways | PredicateSemanticsNever
)

func (c *Checker) getSyntacticTruthySemantics(node *ast.Node) PredicateSemantics {
	node = ast.SkipOuterExpressions(node, ast.OEKAll)
	switch node.Kind {
	case ast.KindNumericLiteral:
		// Allow `while(0)` or `while(1)`
		if node.Text() == "0" || node.Text() == "1" {
			return PredicateSemanticsSometimes
		}
		return PredicateSemanticsAlways
	case ast.KindArrayLiteralExpression, ast.KindArrowFunction, ast.KindBigIntLiteral, ast.KindClassExpression, ast.KindFunctionExpression,
		ast.KindJsxElement, ast.KindJsxSelfClosingElement, ast.KindObjectLiteralExpression, ast.KindRegularExpressionLiteral:
		return PredicateSemanticsAlways
	case ast.KindVoidExpression, ast.KindNullKeyword:
		return PredicateSemanticsNever
	case ast.KindNoSubstitutionTemplateLiteral, ast.KindStringLiteral:
		if node.Text() != "" {
			return PredicateSemanticsAlways
		}
		return PredicateSemanticsNever
	case ast.KindConditionalExpression:
		return c.getSyntacticTruthySemantics(node.AsConditionalExpression().WhenTrue) | c.getSyntacticTruthySemantics(node.AsConditionalExpression().WhenFalse)
	case ast.KindIdentifier:
		if c.getResolvedSymbol(node) == c.undefinedSymbol {
			return PredicateSemanticsNever
		}
	}
	return PredicateSemanticsSometimes
}

func (c *Checker) checkNullishCoalesceOperands(left *ast.Node, right *ast.Node) {
	if ast.IsBinaryExpression(left.Parent.Parent) {
		grandparentLeft := left.Parent.Parent.AsBinaryExpression().Left
		grandparentOperatorToken := left.Parent.Parent.AsBinaryExpression().OperatorToken
		if ast.IsBinaryExpression(grandparentLeft) && grandparentOperatorToken.Kind == ast.KindBarBarToken {
			c.grammarErrorOnNode(grandparentLeft, diagnostics.X_0_and_1_operations_cannot_be_mixed_without_parentheses, scanner.TokenToString(ast.KindQuestionQuestionToken), scanner.TokenToString(grandparentOperatorToken.Kind))
		}
	} else if ast.IsBinaryExpression(left) {
		operatorToken := left.AsBinaryExpression().OperatorToken
		if operatorToken.Kind == ast.KindBarBarToken || operatorToken.Kind == ast.KindAmpersandAmpersandToken {
			c.grammarErrorOnNode(left, diagnostics.X_0_and_1_operations_cannot_be_mixed_without_parentheses, scanner.TokenToString(operatorToken.Kind), scanner.TokenToString(ast.KindQuestionQuestionToken))
		}
	} else if ast.IsBinaryExpression(right) {
		operatorToken := right.AsBinaryExpression().OperatorToken
		if operatorToken.Kind == ast.KindAmpersandAmpersandToken {
			c.grammarErrorOnNode(right, diagnostics.X_0_and_1_operations_cannot_be_mixed_without_parentheses, scanner.TokenToString(ast.KindQuestionQuestionToken), scanner.TokenToString(operatorToken.Kind))
		}
	}
	c.checkNullishCoalesceOperandLeft(left)
}

func (c *Checker) checkNullishCoalesceOperandLeft(left *ast.Node) {
	leftTarget := ast.SkipOuterExpressions(left, ast.OEKAll)
	nullishSemantics := c.getSyntacticNullishnessSemantics(leftTarget)
	if nullishSemantics != PredicateSemanticsSometimes {
		if nullishSemantics == PredicateSemanticsAlways {
			c.error(leftTarget, diagnostics.This_expression_is_always_nullish)
		} else {
			c.error(leftTarget, diagnostics.Right_operand_of_is_unreachable_because_the_left_operand_is_never_nullish)
		}
	}
}

func (c *Checker) getSyntacticNullishnessSemantics(node *ast.Node) PredicateSemantics {
	node = ast.SkipOuterExpressions(node, ast.OEKAll)
	switch node.Kind {
	case ast.KindAwaitExpression,
		ast.KindCallExpression,
		ast.KindTaggedTemplateExpression,
		ast.KindElementAccessExpression,
		ast.KindMetaProperty,
		ast.KindNewExpression,
		ast.KindPropertyAccessExpression,
		ast.KindYieldExpression,
		ast.KindThisKeyword:
		return PredicateSemanticsSometimes
	case ast.KindBinaryExpression:
		// List of operators that can produce null/undefined:
		// || ||= && &&=
		switch node.AsBinaryExpression().OperatorToken.Kind {
		case ast.KindBarBarToken,
			ast.KindBarBarEqualsToken,
			ast.KindAmpersandAmpersandToken,
			ast.KindAmpersandAmpersandEqualsToken:
			return PredicateSemanticsSometimes
		// For these operator kinds, the right operand is effectively controlling
		case ast.KindCommaToken,
			ast.KindEqualsToken,
			ast.KindQuestionQuestionToken,
			ast.KindQuestionQuestionEqualsToken:
			return c.getSyntacticNullishnessSemantics(node.AsBinaryExpression().Right)
		}
		return PredicateSemanticsNever
	case ast.KindConditionalExpression:
		return c.getSyntacticNullishnessSemantics(node.AsConditionalExpression().WhenTrue) | c.getSyntacticNullishnessSemantics(node.AsConditionalExpression().WhenFalse)
	case ast.KindNullKeyword:
		return PredicateSemanticsAlways
	case ast.KindIdentifier:
		if c.getResolvedSymbol(node) == c.undefinedSymbol {
			return PredicateSemanticsAlways
		}
		return PredicateSemanticsSometimes
	}
	return PredicateSemanticsNever
}

/**
 * This is a *shallow* check: An expression is side-effect-free if the
 * evaluation of the expression *itself* cannot produce side effects.
 * For example, x++ / 3 is side-effect free because the / operator
 * does not have side effects.
 * The intent is to "smell test" an expression for correctness in positions where
 * its value is discarded (e.g. the left side of the comma operator).
 */
func (c *Checker) isSideEffectFree(node *ast.Node) bool {
	node = ast.SkipParentheses(node)
	switch node.Kind {
	case ast.KindIdentifier, ast.KindStringLiteral, ast.KindRegularExpressionLiteral, ast.KindTaggedTemplateExpression, ast.KindTemplateExpression,
		ast.KindNoSubstitutionTemplateLiteral, ast.KindNumericLiteral, ast.KindBigIntLiteral, ast.KindTrueKeyword, ast.KindFalseKeyword,
		ast.KindNullKeyword, ast.KindUndefinedKeyword, ast.KindFunctionExpression, ast.KindClassExpression, ast.KindArrowFunction,
		ast.KindArrayLiteralExpression, ast.KindObjectLiteralExpression, ast.KindTypeOfExpression, ast.KindNonNullExpression, ast.KindJsxSelfClosingElement,
		ast.KindJsxElement:
		return true
	case ast.KindConditionalExpression:
		return c.isSideEffectFree(node.AsConditionalExpression().WhenTrue) && c.isSideEffectFree(node.AsConditionalExpression().WhenFalse)
	case ast.KindBinaryExpression:
		if ast.IsAssignmentOperator(node.AsBinaryExpression().OperatorToken.Kind) {
			return false
		}
		return c.isSideEffectFree(node.AsBinaryExpression().Left) && c.isSideEffectFree(node.AsBinaryExpression().Right)
	case ast.KindPrefixUnaryExpression:
		// Unary operators ~, !, +, and - have no side effects.
		// The rest do.
		switch node.AsPrefixUnaryExpression().Operator {
		case ast.KindExclamationToken, ast.KindPlusToken, ast.KindMinusToken, ast.KindTildeToken:
			return true
		}
	}
	return false
}

// Return true for "indirect calls", (i.e. `(0, x.f)(...)` or `(0, eval)(...)`), which prevents passing `this`.
func (c *Checker) isIndirectCall(node *ast.Node) bool {
	left := node.AsBinaryExpression().Left
	right := node.AsBinaryExpression().Right
	return ast.IsParenthesizedExpression(node.Parent) && ast.IsNumericLiteral(left) && left.Text() == "0" &&
		(ast.IsCallExpression(node.Parent.Parent) && node.Parent.Parent.Expression() == node.Parent ||
			ast.IsTaggedTemplateExpression(node.Parent.Parent)) && (ast.IsAccessExpression(right) || ast.IsIdentifier(right) && right.Text() == "eval")
}

func (c *Checker) checkInstanceOfExpression(left *ast.Expression, right *ast.Expression, leftType *Type, rightType *Type, checkMode CheckMode) *Type {
	if leftType == c.silentNeverType || rightType == c.silentNeverType {
		return c.silentNeverType
	}
	// TypeScript 1.0 spec (April 2014): 4.15.4
	// The instanceof operator requires the left operand to be of type Any, an object type, or a type parameter type,
	// and the right operand to be of type Any, a subtype of the 'Function' interface type, or have a call or construct signature.
	// The result is always of the Boolean primitive type.
	// NOTE: do not raise error if leftType is unknown as related error was already reported
	if !IsTypeAny(leftType) && c.allTypesAssignableToKind(leftType, TypeFlagsPrimitive) {
		c.error(left, diagnostics.The_left_hand_side_of_an_instanceof_expression_must_be_of_type_any_an_object_type_or_a_type_parameter)
	}
	signature := c.getResolvedSignature(left.Parent, nil /*candidatesOutArray*/, checkMode)
	if signature == c.resolvingSignature {
		// CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that
		// returns a function type. We defer checking and return silentNeverType.
		return c.silentNeverType
	}
	// If rightType has a `[Symbol.hasInstance]` method that is not `(value: unknown) => boolean`, we
	// must check the expression as if it were a call to `right[Symbol.hasInstance](left)`. The call to
	// `getResolvedSignature`, below, will check that leftType is assignable to the type of the first
	// parameter.
	returnType := c.getReturnTypeOfSignature(signature)
	// We also verify that the return type of the `[Symbol.hasInstance]` method is assignable to
	// `boolean`. According to the spec, the runtime will actually perform `ToBoolean` on the result,
	// but this is more type-safe.
	c.checkTypeAssignableTo(returnType, c.booleanType, right, diagnostics.An_object_s_Symbol_hasInstance_method_must_return_a_boolean_value_for_it_to_be_used_on_the_right_hand_side_of_an_instanceof_expression)
	return c.booleanType
}

func (c *Checker) checkInExpression(left *ast.Expression, right *ast.Expression, leftType *Type, rightType *Type) *Type {
	if leftType == c.silentNeverType || rightType == c.silentNeverType {
		return c.silentNeverType
	}
	if ast.IsPrivateIdentifier(left) {
		if c.languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks ||
			c.languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators ||
			!c.compilerOptions.GetUseDefineForClassFields() {
			c.checkExternalEmitHelpers(left, ExternalEmitHelpersClassPrivateFieldIn)
		}
		// Unlike in 'checkPrivateIdentifierExpression' we now have access to the RHS type
		// which provides us with the opportunity to emit more detailed errors
		if c.symbolNodeLinks.Get(left).resolvedSymbol == nil && ast.GetContainingClass(left) != nil {
			isUncheckedJS := c.isUncheckedJSSuggestion(left, rightType.symbol, true /*excludeClasses*/)
			c.reportNonexistentProperty(left, rightType, isUncheckedJS)
		}
	} else {
		// The type of the left operand must be assignable to string, number, or symbol.
		c.checkTypeAssignableTo(c.checkNonNullType(leftType, left), c.stringNumberSymbolType, left, nil)
	}
	// The type of the right operand must be assignable to 'object'.
	if c.checkTypeAssignableTo(c.checkNonNullType(rightType, right), c.nonPrimitiveType, right, nil) {
		// The {} type is assignable to the object type, yet {} might represent a primitive type. Here we
		// detect and error on {} that results from narrowing the unknown type, as well as intersections
		// that include {} (we know that the other types in such intersections are assignable to object
		// since we already checked for that).
		if c.hasEmptyObjectIntersection(rightType) {
			c.error(right, diagnostics.Type_0_may_represent_a_primitive_value_which_is_not_permitted_as_the_right_operand_of_the_in_operator, c.TypeToString(rightType))
		}
	}
	// The result is always of the Boolean primitive type.
	return c.booleanType
}

func (c *Checker) hasEmptyObjectIntersection(t *Type) bool {
	return someType(t, func(t *Type) bool {
		return t == c.unknownEmptyObjectType || t.flags&TypeFlagsIntersection != 0 && c.IsEmptyAnonymousObjectType(c.getBaseConstraintOrType(t))
	})
}

func (c *Checker) getExactOptionalUnassignableProperties(source *Type, target *Type) []*ast.Symbol {
	if isTupleType(source) && isTupleType(target) {
		return nil
	}
	return core.Filter(c.getPropertiesOfType(target), func(targetProp *ast.Symbol) bool {
		return c.isExactOptionalPropertyMismatch(c.getTypeOfPropertyOfType(source, targetProp.Name), c.getTypeOfSymbol(targetProp))
	})
}

func (c *Checker) isExactOptionalPropertyMismatch(source *Type, target *Type) bool {
	return source != nil && target != nil && c.maybeTypeOfKind(source, TypeFlagsUndefined) && c.containsMissingType(target)
}

func (c *Checker) checkReferenceExpression(expr *ast.Node, invalidReferenceMessage *diagnostics.Message, invalidOptionalChainMessage *diagnostics.Message) bool {
	// References are combinations of identifiers, parentheses, and property accesses.
	node := ast.SkipOuterExpressions(expr, ast.OEKAssertions|ast.OEKParentheses)
	if node.Kind != ast.KindIdentifier && !ast.IsAccessExpression(node) {
		c.error(expr, invalidReferenceMessage)
		return false
	}
	if node.Flags&ast.NodeFlagsOptionalChain != 0 {
		c.error(expr, invalidOptionalChainMessage)
		return false
	}
	return true
}

func (c *Checker) checkObjectLiteral(node *ast.Node, checkMode CheckMode) *Type {
	// Expando object literals have empty properties but filled exports
	if len(node.Properties()) == 0 && node.Symbol() != nil && len(node.Symbol().Exports) != 0 {
		result := c.newAnonymousType(node.Symbol(), node.Symbol().Exports, nil, nil, nil)
		if ast.IsInJSFile(node) && !ast.IsInJsonFile(node) {
			result.objectFlags |= ObjectFlagsJSLiteral
		}
		return result
	}
	inDestructuringPattern := ast.IsAssignmentTarget(node)
	// Grammar checking
	c.checkGrammarObjectLiteralExpression(node.AsObjectLiteralExpression(), inDestructuringPattern)
	var allPropertiesTable ast.SymbolTable
	if c.strictNullChecks {
		allPropertiesTable = make(ast.SymbolTable)
	}
	propertiesTable := make(ast.SymbolTable)
	var propertiesArray []*ast.Symbol
	spread := c.emptyObjectType
	c.pushCachedContextualType(node)
	contextualType := c.getApparentTypeOfContextualType(node, ContextFlagsNone)
	var contextualTypeHasPattern bool
	if contextualType != nil {
		if pattern := c.patternForType[contextualType]; pattern != nil && (ast.IsObjectBindingPattern(pattern) || ast.IsObjectLiteralExpression(pattern)) {
			contextualTypeHasPattern = true
		}
	}
	inConstContext := c.isConstContext(node)
	var checkFlags ast.CheckFlags
	if inConstContext {
		checkFlags = ast.CheckFlagsReadonly
	}
	objectFlags := ObjectFlagsFreshLiteral
	patternWithComputedProperties := false
	hasComputedStringProperty := false
	hasComputedNumberProperty := false
	hasComputedSymbolProperty := false
	// Spreads may cause an early bail; ensure computed names are always checked (this is cached)
	// As otherwise they may not be checked until exports for the type at this position are retrieved,
	// which may never occur.
	for _, elem := range node.Properties() {
		if elem.Name() != nil && ast.IsComputedPropertyName(elem.Name()) {
			c.checkComputedPropertyName(elem.Name())
		}
	}
	offset := 0
	createObjectLiteralType := func() *Type {
		var indexInfos []*IndexInfo
		isReadonly := c.isConstContext(node)
		if hasComputedStringProperty {
			indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(isReadonly, propertiesArray[offset:], c.stringType))
		}
		if hasComputedNumberProperty {
			indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(isReadonly, propertiesArray[offset:], c.numberType))
		}
		if hasComputedSymbolProperty {
			indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(isReadonly, propertiesArray[offset:], c.esSymbolType))
		}
		result := c.newAnonymousType(node.Symbol(), propertiesTable, nil, nil, indexInfos)
		result.objectFlags |= objectFlags | ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral
		if contextualType == nil && ast.IsInJSFile(node) && !ast.IsInJsonFile(node) {
			result.objectFlags |= ObjectFlagsJSLiteral
		}
		if patternWithComputedProperties {
			result.objectFlags |= ObjectFlagsObjectLiteralPatternWithComputedProperties
		}
		if inDestructuringPattern {
			c.patternForType[result] = node
		}
		return result
	}
	for _, memberDecl := range node.Properties() {
		member := c.getSymbolOfDeclaration(memberDecl)
		var computedNameType *Type
		if memberDecl.Name() != nil && memberDecl.Name().Kind == ast.KindComputedPropertyName {
			computedNameType = c.checkComputedPropertyName(memberDecl.Name())
		}
		if ast.IsPropertyAssignment(memberDecl) || ast.IsShorthandPropertyAssignment(memberDecl) || ast.IsObjectLiteralMethod(memberDecl) {
			var t *Type
			switch memberDecl.Kind {
			case ast.KindPropertyAssignment:
				t = c.checkPropertyAssignment(memberDecl, checkMode)
			case ast.KindShorthandPropertyAssignment:
				t = c.checkShorthandPropertyAssignment(memberDecl, inDestructuringPattern, checkMode)
			default:
				t = c.checkObjectLiteralMethod(memberDecl, checkMode)
			}
			objectFlags |= t.objectFlags & ObjectFlagsPropagatingFlags
			var nameType *Type
			if computedNameType != nil && isTypeUsableAsPropertyName(computedNameType) {
				nameType = computedNameType
			}
			var prop *ast.Symbol
			if nameType != nil {
				prop = c.newSymbolEx(ast.SymbolFlagsProperty|member.Flags, getPropertyNameFromType(nameType), checkFlags|ast.CheckFlagsLate)
			} else {
				prop = c.newSymbolEx(ast.SymbolFlagsProperty|member.Flags, member.Name, checkFlags)
			}
			links := c.valueSymbolLinks.Get(prop)
			if nameType != nil {
				links.nameType = nameType
			}
			if inDestructuringPattern && c.hasDefaultValue(memberDecl) {
				// If object literal is an assignment pattern and if the assignment pattern specifies a default value
				// for the property, make the property optional.
				prop.Flags |= ast.SymbolFlagsOptional
			} else if contextualTypeHasPattern && contextualType.objectFlags&ObjectFlagsObjectLiteralPatternWithComputedProperties == 0 {
				// If object literal is contextually typed by the implied type of a binding pattern, and if the
				// binding pattern specifies a default value for the property, make the property optional.
				impliedProp := c.getPropertyOfType(contextualType, member.Name)
				if impliedProp != nil {
					prop.Flags |= impliedProp.Flags & ast.SymbolFlagsOptional
				} else if c.getIndexInfoOfType(contextualType, c.stringType) == nil {
					c.error(memberDecl.Name(), diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, c.symbolToString(member), c.TypeToString(contextualType))
				}
			}
			prop.Declarations = member.Declarations
			prop.Parent = member.Parent
			prop.ValueDeclaration = member.ValueDeclaration
			links.resolvedType = t
			links.target = member
			member = prop
			if allPropertiesTable != nil {
				allPropertiesTable[prop.Name] = prop
			}
			if contextualType != nil && checkMode&CheckModeInferential != 0 && checkMode&CheckModeSkipContextSensitive == 0 && (ast.IsPropertyAssignment(memberDecl) || ast.IsMethodDeclaration(memberDecl)) && c.isContextSensitive(memberDecl) {
				inferenceContext := c.getInferenceContext(node)
				// In CheckMode.Inferential we should always have an inference context
				inferenceNode := memberDecl
				if ast.IsPropertyAssignment(memberDecl) {
					inferenceNode = memberDecl.Initializer()
				}
				c.addIntraExpressionInferenceSite(inferenceContext, inferenceNode, t)
			}
		} else if memberDecl.Kind == ast.KindSpreadAssignment {
			if len(propertiesArray) > 0 {
				spread = c.getSpreadType(spread, createObjectLiteralType(), node.Symbol(), objectFlags, inConstContext)
				propertiesArray = nil
				propertiesTable = make(ast.SymbolTable)
				hasComputedStringProperty = false
				hasComputedNumberProperty = false
				hasComputedSymbolProperty = false
			}
			t := c.getReducedType(c.checkExpressionEx(memberDecl.Expression(), checkMode&CheckModeInferential))
			if c.isValidSpreadType(t) {
				mergedType := c.tryMergeUnionOfObjectTypeAndEmptyObject(t, inConstContext)
				if allPropertiesTable != nil {
					c.checkSpreadPropOverrides(mergedType, allPropertiesTable, memberDecl)
				}
				offset = len(propertiesArray)
				if c.isErrorType(spread) {
					continue
				}
				spread = c.getSpreadType(spread, mergedType, node.Symbol(), objectFlags, inConstContext)
			} else {
				c.error(memberDecl, diagnostics.Spread_types_may_only_be_created_from_object_types)
				spread = c.errorType
			}
			continue
		} else {
			// TypeScript 1.0 spec (April 2014)
			// A get accessor declaration is processed in the same manner as
			// an ordinary function declaration(section 6.1) with no parameters.
			// A set accessor declaration is processed in the same manner
			// as an ordinary function declaration with a single parameter and a Void return type.
			debug.Assert(memberDecl.Kind == ast.KindGetAccessor || memberDecl.Kind == ast.KindSetAccessor)
			c.checkNodeDeferred(memberDecl)
		}
		if computedNameType != nil && computedNameType.flags&TypeFlagsStringOrNumberLiteralOrUnique == 0 {
			if c.isTypeAssignableTo(computedNameType, c.stringNumberSymbolType) {
				if c.isTypeAssignableTo(computedNameType, c.numberType) {
					hasComputedNumberProperty = true
				} else if c.isTypeAssignableTo(computedNameType, c.esSymbolType) {
					hasComputedSymbolProperty = true
				} else {
					hasComputedStringProperty = true
				}
				if inDestructuringPattern {
					patternWithComputedProperties = true
				}
			}
		} else {
			propertiesTable[member.Name] = member
		}
		propertiesArray = append(propertiesArray, member)
	}
	c.popContextualType()
	if c.isErrorType(spread) {
		return c.errorType
	}
	if spread != c.emptyObjectType {
		if len(propertiesArray) > 0 {
			spread = c.getSpreadType(spread, createObjectLiteralType(), node.Symbol(), objectFlags, inConstContext)
			propertiesArray = nil
			propertiesTable = make(ast.SymbolTable)
			hasComputedStringProperty = false
			hasComputedNumberProperty = false
		}
		// remap the raw emptyObjectType fed in at the top into a fresh empty object literal type, unique to this use site
		return c.mapType(spread, func(t *Type) *Type {
			if t == c.emptyObjectType {
				return createObjectLiteralType()
			}
			return t
		})
	}
	return createObjectLiteralType()
}

func (c *Checker) checkSpreadPropOverrides(t *Type, props ast.SymbolTable, spread *ast.Node) {
	for _, right := range c.getPropertiesOfType(t) {
		if right.Flags&ast.SymbolFlagsOptional == 0 && right.CheckFlags&ast.CheckFlagsPartial == 0 {
			if left := props[right.Name]; left != nil {
				diagnostic := c.error(left.ValueDeclaration, diagnostics.X_0_is_specified_more_than_once_so_this_usage_will_be_overwritten, left.Name)
				diagnostic.AddRelatedInfo(NewDiagnosticForNode(spread, diagnostics.This_spread_always_overwrites_this_property))
			}
		}
	}
}

/**
 * Since the source of spread types are object literals, which are not binary,
 * this function should be called in a left folding style, with left = previous result of getSpreadType
 * and right = the new element to be spread.
 */
func (c *Checker) getSpreadType(left *Type, right *Type, symbol *ast.Symbol, objectFlags ObjectFlags, readonly bool) *Type {
	if left.flags&TypeFlagsAny != 0 || right.flags&TypeFlagsAny != 0 {
		return c.anyType
	}
	if left.flags&TypeFlagsUnknown != 0 || right.flags&TypeFlagsUnknown != 0 {
		return c.unknownType
	}
	if left.flags&TypeFlagsNever != 0 {
		return right
	}
	if right.flags&TypeFlagsNever != 0 {
		return left
	}
	left = c.tryMergeUnionOfObjectTypeAndEmptyObject(left, readonly)
	if left.flags&TypeFlagsUnion != 0 {
		if c.checkCrossProductUnion([]*Type{left, right}) {
			return c.mapType(left, func(t *Type) *Type {
				return c.getSpreadType(t, right, symbol, objectFlags, readonly)
			})
		}
		return c.errorType
	}
	right = c.tryMergeUnionOfObjectTypeAndEmptyObject(right, readonly)
	if right.flags&TypeFlagsUnion != 0 {
		if c.checkCrossProductUnion([]*Type{left, right}) {
			return c.mapType(right, func(t *Type) *Type {
				return c.getSpreadType(left, t, symbol, objectFlags, readonly)
			})
		}
		return c.errorType
	}
	if right.flags&(TypeFlagsBooleanLike|TypeFlagsNumberLike|TypeFlagsBigIntLike|TypeFlagsStringLike|TypeFlagsEnumLike|TypeFlagsNonPrimitive|TypeFlagsIndex) != 0 {
		return left
	}
	if c.isGenericObjectType(left) || c.isGenericObjectType(right) {
		if c.isEmptyObjectType(left) {
			return right
		}
		// When the left type is an intersection, we may need to merge the last constituent of the
		// intersection with the right type. For example when the left type is 'T & { a: string }'
		// and the right type is '{ b: string }' we produce 'T & { a: string, b: string }'.
		if left.flags&TypeFlagsIntersection != 0 {
			types := left.Types()
			lastLeft := types[len(types)-1]
			if c.isNonGenericObjectType(lastLeft) && c.isNonGenericObjectType(right) {
				newTypes := slices.Clone(types)
				newTypes[len(newTypes)-1] = c.getSpreadType(lastLeft, right, symbol, objectFlags, readonly)
				return c.getIntersectionType(newTypes)
			}
		}
		return c.getIntersectionType([]*Type{left, right})
	}
	members := make(ast.SymbolTable)
	var skippedPrivateMembers collections.Set[string]
	var indexInfos []*IndexInfo
	if left == c.emptyObjectType {
		indexInfos = c.getIndexInfosOfType(right)
	} else {
		indexInfos = c.getUnionIndexInfos([]*Type{left, right})
	}
	for _, rightProp := range c.getPropertiesOfType(right) {
		if getDeclarationModifierFlagsFromSymbol(rightProp)&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 {
			skippedPrivateMembers.Add(rightProp.Name)
		} else if c.isSpreadableProperty(rightProp) {
			members[rightProp.Name] = c.getSpreadSymbol(rightProp, readonly)
		}
	}

	for _, leftProp := range c.getPropertiesOfType(left) {
		if skippedPrivateMembers.Has(leftProp.Name) || !c.isSpreadableProperty(leftProp) {
			continue
		}
		if members[leftProp.Name] != nil {
			rightProp := members[leftProp.Name]
			rightType := c.getTypeOfSymbol(rightProp)
			if rightProp.Flags&ast.SymbolFlagsOptional != 0 {
				declarations := core.Concatenate(leftProp.Declarations, rightProp.Declarations)
				flags := ast.SymbolFlagsProperty | (leftProp.Flags & ast.SymbolFlagsOptional)
				result := c.newSymbol(flags, leftProp.Name)
				links := c.valueSymbolLinks.Get(result)
				// Optimization: avoid calculating the union type if spreading into the exact same type.
				// This is common, e.g. spreading one options bag into another where the bags have the
				// same type, or have properties which overlap. If the unions are large, it may turn out
				// to be expensive to perform subtype reduction.
				leftType := c.getTypeOfSymbol(leftProp)
				leftTypeWithoutUndefined := c.removeMissingOrUndefinedType(leftType)
				rightTypeWithoutUndefined := c.removeMissingOrUndefinedType(rightType)
				if leftTypeWithoutUndefined == rightTypeWithoutUndefined {
					links.resolvedType = leftType
				} else {
					links.resolvedType = c.getUnionTypeEx([]*Type{leftType, rightTypeWithoutUndefined}, UnionReductionSubtype, nil, nil)
				}
				c.spreadLinks.Get(result).leftSpread = leftProp
				c.spreadLinks.Get(result).rightSpread = rightProp
				result.Declarations = declarations
				links.nameType = c.valueSymbolLinks.Get(leftProp).nameType
				members[leftProp.Name] = result
			}
		} else {
			members[leftProp.Name] = c.getSpreadSymbol(leftProp, readonly)
		}
	}
	spreadIndexInfos := core.SameMap(indexInfos, func(info *IndexInfo) *IndexInfo {
		return c.getIndexInfoWithReadonly(info, readonly)
	})
	spread := c.newAnonymousType(symbol, members, nil, nil, spreadIndexInfos)
	spread.objectFlags |= ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral | ObjectFlagsContainsSpread | objectFlags
	return spread
}

func (c *Checker) getIndexInfoWithReadonly(info *IndexInfo, readonly bool) *IndexInfo {
	if info.isReadonly != readonly {
		return c.newIndexInfo(info.keyType, info.valueType, readonly, info.declaration, info.components)
	}
	return info
}

func (c *Checker) isValidSpreadType(t *Type) bool {
	s := c.removeDefinitelyFalsyTypes(c.mapType(t, c.getBaseConstraintOrType))
	return s.flags&(TypeFlagsAny|TypeFlagsNonPrimitive|TypeFlagsObject|TypeFlagsInstantiableNonPrimitive) != 0 ||
		s.flags&TypeFlagsUnionOrIntersection != 0 && core.Every(s.Types(), c.isValidSpreadType)
}

func (c *Checker) getUnionIndexInfos(types []*Type) []*IndexInfo {
	sourceInfos := c.getIndexInfosOfType(types[0])
	var result []*IndexInfo
	for _, info := range sourceInfos {
		indexType := info.keyType
		if core.Every(types, func(t *Type) bool { return c.getIndexInfoOfType(t, indexType) != nil }) {
			valueType := c.getUnionType(core.Map(types, func(t *Type) *Type {
				return c.getIndexTypeOfType(t, indexType)
			}))
			isReadonly := core.Some(types, func(t *Type) bool { return c.getIndexInfoOfType(t, indexType).isReadonly })
			result = append(result, c.newIndexInfo(indexType, valueType, isReadonly, nil, nil))
		}
	}
	return result
}

func (c *Checker) isNonGenericObjectType(t *Type) bool {
	return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t)
}

func (c *Checker) tryMergeUnionOfObjectTypeAndEmptyObject(t *Type, readonly bool) *Type {
	if t.flags&TypeFlagsUnion == 0 {
		return t
	}
	if core.Every(t.Types(), c.isEmptyObjectTypeOrSpreadsIntoEmptyObject) {
		empty := core.Find(t.Types(), c.isEmptyObjectType)
		if empty != nil {
			return empty
		}
		return c.emptyObjectType
	}
	firstType := core.Find(t.Types(), func(t *Type) bool {
		return !c.isEmptyObjectTypeOrSpreadsIntoEmptyObject(t)
	})
	if firstType == nil {
		return t
	}
	secondType := core.Find(t.Types(), func(t *Type) bool {
		return t != firstType && !c.isEmptyObjectTypeOrSpreadsIntoEmptyObject(t)
	})
	if secondType != nil {
		return t
	}
	// gets the type as if it had been spread, but where everything in the spread is made optional
	members := make(ast.SymbolTable)
	for _, prop := range c.getPropertiesOfType(firstType) {
		if getDeclarationModifierFlagsFromSymbol(prop)&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) != 0 {
			// do nothing, skip privates
		} else if c.isSpreadableProperty(prop) {
			isSetonlyAccessor := prop.Flags&ast.SymbolFlagsSetAccessor != 0 && prop.Flags&ast.SymbolFlagsGetAccessor == 0
			flags := ast.SymbolFlagsProperty | ast.SymbolFlagsOptional
			result := c.newSymbolEx(flags, prop.Name, prop.CheckFlags&ast.CheckFlagsLate|core.IfElse(readonly, ast.CheckFlagsReadonly, 0))
			links := c.valueSymbolLinks.Get(result)
			if isSetonlyAccessor {
				links.resolvedType = c.undefinedType
			} else {
				links.resolvedType = c.addOptionalityEx(c.getTypeOfSymbol(prop), true /*isProperty*/, true /*isOptional*/)
			}
			result.Declarations = prop.Declarations
			links.nameType = c.valueSymbolLinks.Get(prop).nameType
			c.mappedSymbolLinks.Get(result).syntheticOrigin = prop
			members[prop.Name] = result
		}
	}
	spread := c.newAnonymousType(firstType.symbol, members, nil, nil, c.getIndexInfosOfType(firstType))
	spread.objectFlags |= ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral
	return spread
}

// We approximate own properties as non-methods plus methods that are inside the object literal
func (c *Checker) isSpreadableProperty(prop *ast.Symbol) bool {
	return !core.Some(prop.Declarations, ast.IsPrivateIdentifierClassElementDeclaration) && prop.Flags&(ast.SymbolFlagsMethod|ast.SymbolFlagsGetAccessor|ast.SymbolFlagsSetAccessor) == 0 ||
		!core.Some(prop.Declarations, func(d *ast.Node) bool { return d.Parent != nil && ast.IsClassLike(d.Parent) })
}

func (c *Checker) getSpreadSymbol(prop *ast.Symbol, readonly bool) *ast.Symbol {
	isSetonlyAccessor := prop.Flags&ast.SymbolFlagsSetAccessor != 0 && prop.Flags&ast.SymbolFlagsGetAccessor == 0
	if !isSetonlyAccessor && readonly == c.isReadonlySymbol(prop) {
		return prop
	}
	flags := ast.SymbolFlagsProperty | (prop.Flags & ast.SymbolFlagsOptional)
	result := c.newSymbolEx(flags, prop.Name, prop.CheckFlags&ast.CheckFlagsLate|core.IfElse(readonly, ast.CheckFlagsReadonly, 0))
	links := c.valueSymbolLinks.Get(result)
	if isSetonlyAccessor {
		links.resolvedType = c.undefinedType
	} else {
		links.resolvedType = c.getTypeOfSymbol(prop)
	}
	result.Declarations = prop.Declarations
	links.nameType = c.valueSymbolLinks.Get(prop).nameType
	c.mappedSymbolLinks.Get(result).syntheticOrigin = prop
	return result
}

func (c *Checker) isEmptyObjectTypeOrSpreadsIntoEmptyObject(t *Type) bool {
	return c.isEmptyObjectType(t) || t.flags&(TypeFlagsNull|TypeFlagsUndefined|TypeFlagsBooleanLike|TypeFlagsNumberLike|TypeFlagsBigIntLike|TypeFlagsStringLike|TypeFlagsEnumLike|TypeFlagsNonPrimitive|TypeFlagsIndex) != 0
}

func (c *Checker) hasDefaultValue(node *ast.Node) bool {
	return ast.IsBindingElement(node) && node.Initializer() != nil ||
		ast.IsPropertyAssignment(node) && c.hasDefaultValue(node.Initializer()) ||
		ast.IsShorthandPropertyAssignment(node) && node.AsShorthandPropertyAssignment().ObjectAssignmentInitializer != nil ||
		ast.IsBinaryExpression(node) && node.AsBinaryExpression().OperatorToken.Kind == ast.KindEqualsToken
}

func (c *Checker) isConstContext(node *ast.Node) bool {
	parent := node.Parent
	return ast.IsConstAssertion(parent) ||
		c.isValidConstAssertionArgument(node) && c.isConstTypeVariable(c.getContextualType(node, ContextFlagsNone), 0) ||
		(ast.IsParenthesizedExpression(parent) || ast.IsArrayLiteralExpression(parent) || ast.IsSpreadElement(parent)) && c.isConstContext(parent) ||
		(ast.IsPropertyAssignment(parent) || ast.IsShorthandPropertyAssignment(parent) || ast.IsTemplateSpan(parent)) && c.isConstContext(parent.Parent)
}

func (c *Checker) isValidConstAssertionArgument(node *ast.Node) bool {
	switch node.Kind {
	case ast.KindStringLiteral, ast.KindNoSubstitutionTemplateLiteral, ast.KindNumericLiteral, ast.KindBigIntLiteral, ast.KindTrueKeyword,
		ast.KindFalseKeyword, ast.KindArrayLiteralExpression, ast.KindObjectLiteralExpression, ast.KindTemplateExpression:
		return true
	case ast.KindParenthesizedExpression:
		return c.isValidConstAssertionArgument(node.Expression())
	case ast.KindPrefixUnaryExpression:
		op := node.AsPrefixUnaryExpression().Operator
		arg := node.AsPrefixUnaryExpression().Operand
		return op == ast.KindMinusToken && (arg.Kind == ast.KindNumericLiteral || arg.Kind == ast.KindBigIntLiteral) || op == ast.KindPlusToken && arg.Kind == ast.KindNumericLiteral
	case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression:
		expr := ast.SkipParentheses(node.Expression())
		var symbol *ast.Symbol
		if ast.IsEntityNameExpression(expr) {
			symbol = c.resolveEntityName(expr, ast.SymbolFlagsValue, true /*ignoreErrors*/, false, nil)
		}
		return symbol != nil && symbol.Flags&ast.SymbolFlagsEnum != 0
	}
	return false
}

func (c *Checker) isConstTypeVariable(t *Type, depth int) bool {
	if depth >= 5 || t == nil {
		return false
	}
	switch {
	case t.flags&TypeFlagsTypeParameter != 0:
		return t.symbol != nil && core.Some(t.symbol.Declarations, func(d *ast.Node) bool { return ast.HasSyntacticModifier(d, ast.ModifierFlagsConst) })
	case t.flags&TypeFlagsUnionOrIntersection != 0:
		return core.Some(t.Types(), func(s *Type) bool { return c.isConstTypeVariable(s, depth) })
	case t.flags&TypeFlagsIndexedAccess != 0:
		return c.isConstTypeVariable(t.AsIndexedAccessType().objectType, depth+1)
	case t.flags&TypeFlagsConditional != 0:
		return c.isConstTypeVariable(c.getConstraintOfConditionalType(t), depth+1)
	case t.flags&TypeFlagsSubstitution != 0:
		return c.isConstTypeVariable(t.AsSubstitutionType().baseType, depth)
	case t.objectFlags&ObjectFlagsMapped != 0:
		typeVariable := c.getHomomorphicTypeVariable(t)
		return typeVariable != nil && c.isConstTypeVariable(typeVariable, depth)
	case c.isGenericTupleType(t):
		for i, s := range c.getElementTypes(t) {
			if t.TargetTupleType().elementInfos[i].flags&ElementFlagsVariadic != 0 && c.isConstTypeVariable(s, depth) {
				return true
			}
		}
	}
	return false
}

func (c *Checker) checkPropertyAssignment(node *ast.Node, checkMode CheckMode) *Type {
	// Do not use hasDynamicName here, because that returns false for well known symbols.
	// We want to perform checkComputedPropertyName for all computed properties, including
	// well known symbols.
	if ast.IsComputedPropertyName(node.Name()) {
		c.checkComputedPropertyName(node.Name())
	}
	initializerType := c.checkExpressionForMutableLocation(node.Initializer(), checkMode)
	if node.Type() != nil {
		t := c.getTypeFromTypeNode(node.Type())
		c.checkTypeAssignableToAndOptionallyElaborate(initializerType, t, node, node.Initializer(), nil /*headMessage*/, nil)
		return t
	}
	return initializerType
}

func (c *Checker) checkShorthandPropertyAssignment(node *ast.Node, inDestructuringPattern bool, checkMode CheckMode) *Type {
	var expr *ast.Node
	if !inDestructuringPattern {
		expr = node.AsShorthandPropertyAssignment().ObjectAssignmentInitializer
	}
	if expr == nil {
		expr = node.Name()
	}
	expressionType := c.checkExpressionForMutableLocation(expr, checkMode)
	if node.Type() != nil {
		t := c.getTypeFromTypeNode(node.Type())
		c.checkTypeAssignableToAndOptionallyElaborate(expressionType, t, node, expr, nil /*headMessage*/, nil)
		return t
	}
	return expressionType
}

func (c *Checker) isInPropertyInitializerOrClassStaticBlock(node *ast.Node, ignoreArrowFunctions bool) bool {
	return ast.FindAncestorOrQuit(node, func(node *ast.Node) ast.FindAncestorResult {
		switch node.Kind {
		case ast.KindPropertyDeclaration, ast.KindClassStaticBlockDeclaration:
			return ast.FindAncestorTrue
		case ast.KindTypeQuery, ast.KindJsxClosingElement:
			return ast.FindAncestorQuit
		case ast.KindArrowFunction:
			return core.IfElse(ignoreArrowFunctions, ast.FindAncestorFalse, ast.FindAncestorQuit)
		case ast.KindBlock:
			return core.IfElse(ast.IsFunctionLikeDeclaration(node.Parent) && node.Parent.Kind != ast.KindArrowFunction, ast.FindAncestorQuit, ast.FindAncestorFalse)
		default:
			return ast.FindAncestorFalse
		}
	}) != nil
}

func (c *Checker) getNarrowedTypeOfSymbol(symbol *ast.Symbol, location *ast.Node) *Type {
	t := c.getTypeOfSymbol(symbol)
	declaration := symbol.ValueDeclaration
	if declaration != nil {
		switch {
		// If we have a non-rest binding element with no initializer declared as a const variable or a const-like
		// parameter (a parameter for which there are no assignments in the function body), and if the parent type
		// for the destructuring is a union type, one or more of the binding elements may represent discriminant
		// properties, and we want the effects of conditional checks on such discriminants to affect the types of
		// other binding elements from the same destructuring. Consider:
		//
		//   type Action =
		//       | { kind: 'A', payload: number }
		//       | { kind: 'B', payload: string };
		//
		//   function f({ kind, payload }: Action) {
		//       if (kind === 'A') {
		//           payload.toFixed();
		//       }
		//       if (kind === 'B') {
		//           payload.toUpperCase();
		//       }
		//   }
		//
		// Above, we want the conditional checks on 'kind' to affect the type of 'payload'. To facilitate this, we use
		// the binding pattern AST instance for '{ kind, payload }' as a pseudo-reference and narrow this reference
		// as if it occurred in the specified location. We then recompute the narrowed binding element type by
		// destructuring from the narrowed parent type.
		case ast.IsBindingElement(declaration) && declaration.Initializer() == nil && !hasDotDotDotToken(declaration) && len(declaration.Parent.Elements()) >= 2:
			parent := declaration.Parent.Parent
			rootDeclaration := ast.GetRootDeclaration(parent)
			if ast.IsVariableDeclaration(rootDeclaration) && c.getCombinedNodeFlagsCached(rootDeclaration)&ast.NodeFlagsConstant != 0 || ast.IsParameterDeclaration(rootDeclaration) {
				links := c.nodeLinks.Get(parent)
				if links.flags&NodeCheckFlagsInCheckIdentifier == 0 {
					links.flags |= NodeCheckFlagsInCheckIdentifier
					parentType := c.getTypeForBindingElementParent(parent, CheckModeNormal)
					var parentTypeConstraint *Type
					if parentType != nil {
						parentTypeConstraint = c.mapType(parentType, c.getBaseConstraintOrType)
					}
					if parentTypeConstraint != nil && parentTypeConstraint.flags&TypeFlagsUnion != 0 && !(ast.IsParameterDeclaration(rootDeclaration) && c.isSomeSymbolAssigned(rootDeclaration)) {
						pattern := declaration.Parent
						narrowedType := c.getFlowTypeOfReferenceEx(pattern, parentTypeConstraint, parentTypeConstraint, nil /*flowContainer*/, getFlowNodeOfNode(location))
						if narrowedType.flags&TypeFlagsNever != 0 {
							t = c.neverType
						} else {
							// Destructurings are validated against the parent type elsewhere. Here we disable tuple bounds
							// checks because the narrowed type may have lower arity than the full parent type. For example,
							// for the declaration [x, y]: [1, 2] | [3], we may have narrowed the parent type to just [3].
							t = c.getBindingElementTypeFromParentType(declaration, narrowedType, true /*noTupleBoundsCheck*/)
						}
					}
					links.flags &^= NodeCheckFlagsInCheckIdentifier
				}
			}
		// If we have a const-like parameter with no type annotation or initializer, and if the parameter is contextually
		// typed by a signature with a single rest parameter of a union of tuple types, one or more of the parameters may
		// represent discriminant tuple elements, and we want the effects of conditional checks on such discriminants to
		// affect the types of other parameters in the same parameter list. Consider:
		//
		//   type Action = [kind: 'A', payload: number] | [kind: 'B', payload: string];
		//
		//   const f: (...args: Action) => void = (kind, payload) => {
		//       if (kind === 'A') {
		//           payload.toFixed();
		//       }
		//       if (kind === 'B') {
		//           payload.toUpperCase();
		//       }
		//   }
		//
		// Above, we want the conditional checks on 'kind' to affect the type of 'payload'. To facilitate this, we use
		// the arrow function AST node for '(kind, payload) => ...' as a pseudo-reference and narrow this reference as
		// if it occurred in the specified location. We then recompute the narrowed parameter type by indexing into the
		// narrowed tuple type.
		case ast.IsParameterDeclaration(declaration) && declaration.Type() == nil && declaration.Initializer() == nil && !hasDotDotDotToken(declaration):
			fn := declaration.Parent
			if len(fn.Parameters()) >= 2 && c.isContextSensitiveFunctionOrObjectLiteralMethod(fn) {
				contextualSignature := c.getContextualSignature(fn)
				if contextualSignature != nil && len(contextualSignature.parameters) == 1 && signatureHasRestParameter(contextualSignature) {
					var mapper *TypeMapper
					context := c.getInferenceContext(fn)
					if context != nil {
						mapper = context.nonFixingMapper
					}
					restType := c.getReducedApparentType(c.instantiateType(c.getTypeOfSymbol(contextualSignature.parameters[0]), mapper))
					if restType.flags&TypeFlagsUnion != 0 && everyType(restType, isTupleType) && !core.Some(fn.Parameters(), c.isSomeSymbolAssigned) {
						narrowedType := c.getFlowTypeOfReferenceEx(fn, restType, restType, nil /*flowContainer*/, getFlowNodeOfNode(location))
						index := slices.Index(fn.Parameters(), declaration) - core.IfElse(ast.GetThisParameter(fn) != nil, 1, 0)
						t = c.getIndexedAccessType(narrowedType, c.getNumberLiteralType(jsnum.Number(index)))
					}
				}
			}
		}
	}
	return t
}

func (c *Checker) isReadonlyAssignmentDeclaration(node *ast.Node) bool {
	if !ast.IsCallExpression(node) {
		return false
	}
	propertyDescriptorType := c.checkExpressionCached(node.Arguments()[2])
	if valueType := c.getTypeOfPropertyOfType(propertyDescriptorType, "value"); valueType != nil {
		if writableProp := c.getPropertyOfType(propertyDescriptorType, "writable"); writableProp != nil {
			var writableType *Type
			if writableProp.ValueDeclaration != nil && ast.IsPropertyAssignment(writableProp.ValueDeclaration) {
				writableType = c.checkExpression(writableProp.ValueDeclaration.Initializer())
			} else {
				writableType = c.getTypeOfSymbol(writableProp)
			}
			return writableType.flags&TypeFlagsBooleanLiteral != 0 && getBooleanLiteralValue(writableType) == false
		}
		return true
	}
	return c.getTypeOfPropertyOfType(propertyDescriptorType, "set") == nil
}

func (c *Checker) isReadonlySymbol(symbol *ast.Symbol) bool {
	// The following symbols are considered read-only:
	// Properties with a 'readonly' modifier
	// Variables declared with 'const'
	// Get accessors without matching set accessors
	// Enum members
	// Object.defineProperty assignments with writable false or no setter
	// Unions and intersections of the above (unions and intersections eagerly set isReadonly on creation)
	return symbol.CheckFlags&ast.CheckFlagsReadonly != 0 ||
		symbol.Flags&ast.SymbolFlagsProperty != 0 && getDeclarationModifierFlagsFromSymbol(symbol)&ast.ModifierFlagsReadonly != 0 ||
		symbol.Flags&ast.SymbolFlagsVariable != 0 && c.getDeclarationNodeFlagsFromSymbol(symbol)&ast.NodeFlagsConstant != 0 ||
		symbol.Flags&ast.SymbolFlagsAccessor != 0 && symbol.Flags&ast.SymbolFlagsSetAccessor == 0 ||
		symbol.Flags&ast.SymbolFlagsEnumMember != 0 ||
		core.Some(symbol.Declarations, c.isReadonlyAssignmentDeclaration)
}

func (c *Checker) checkObjectLiteralMethod(node *ast.Node, checkMode CheckMode) *Type {
	// Grammar checking
	c.checkGrammarMethod(node)
	// Do not use hasDynamicName here, because that returns false for well known symbols.
	// We want to perform checkComputedPropertyName for all computed properties, including
	// well known symbols.
	if ast.IsComputedPropertyName(node.Name()) {
		c.checkComputedPropertyName(node.Name())
	}
	uninstantiatedType := c.checkFunctionExpressionOrObjectLiteralMethod(node, checkMode)
	return c.instantiateTypeWithSingleGenericCallSignature(node, uninstantiatedType, checkMode)
}

func (c *Checker) checkExpressionForMutableLocation(node *ast.Node, checkMode CheckMode) *Type {
	t := c.checkExpressionEx(node, checkMode)
	switch {
	case c.isConstContext(node):
		return c.getRegularTypeOfLiteralType(t)
	case isTypeAssertion(node):
		return t
	default:
		return c.getWidenedLiteralLikeTypeForContextualType(t, c.instantiateContextualType(c.getContextualType(node, ContextFlagsNone), node, ContextFlagsNone))
	}
}

func (c *Checker) getResolvedSymbol(node *ast.Node) *ast.Symbol {
	links := c.symbolNodeLinks.Get(node)
	if links.resolvedSymbol == nil {
		var symbol *ast.Symbol
		if !ast.NodeIsMissing(node) {
			symbol = c.resolveName(node, node.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsExportValue,
				c.getCannotFindNameDiagnosticForName(node), !ast.IsWriteOnlyAccess(node), false /*excludeGlobals*/)
		}
		links.resolvedSymbol = core.OrElse(symbol, c.unknownSymbol)
	}
	return links.resolvedSymbol
}

func (c *Checker) getResolvedSymbolOrNil(node *ast.Node) *ast.Symbol {
	return c.symbolNodeLinks.Get(node).resolvedSymbol
}

func (c *Checker) getReferencedValueOrAliasSymbol(reference *ast.Node) *ast.Symbol {
	resolvedSymbol := c.symbolNodeLinks.Get(reference).resolvedSymbol
	if resolvedSymbol != nil && resolvedSymbol != c.unknownSymbol {
		return resolvedSymbol
	}
	return c.resolveName(reference, reference.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsExportValue|ast.SymbolFlagsAlias, nil, true /*isUse*/, false /*excludeGlobals*/)
}

func (c *Checker) getCannotFindNameDiagnosticForName(node *ast.Node) *diagnostics.Message {
	switch node.Text() {
	case "document", "console":
		return diagnostics.Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_include_dom
	case "$":
		return core.IfElse(c.compilerOptions.UsesWildcardTypes(),
			diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_save_dev_types_Slashjquery,
			diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_save_dev_types_Slashjquery_and_then_add_jquery_to_the_types_field_in_your_tsconfig)
	case "beforeEach", "describe", "suite", "it", "test":
		return core.IfElse(c.compilerOptions.UsesWildcardTypes(),
			diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_save_dev_types_Slashjest_or_npm_i_save_dev_types_Slashmocha,
			diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_a_test_runner_Try_npm_i_save_dev_types_Slashjest_or_npm_i_save_dev_types_Slashmocha_and_then_add_jest_or_mocha_to_the_types_field_in_your_tsconfig)
	case "process", "require", "Buffer", "module", "NodeJS":
		return core.IfElse(c.compilerOptions.UsesWildcardTypes(),
			diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_save_dev_types_Slashnode,
			diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_save_dev_types_Slashnode_and_then_add_node_to_the_types_field_in_your_tsconfig)
	case "Bun":
		return core.IfElse(c.compilerOptions.UsesWildcardTypes(),
			diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_Bun_Try_npm_i_save_dev_types_Slashbun,
			diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_Bun_Try_npm_i_save_dev_types_Slashbun_and_then_add_bun_to_the_types_field_in_your_tsconfig)
	case "Map", "Set", "Promise", "ast.Symbol", "WeakMap", "WeakSet", "Iterator", "AsyncIterator", "SharedArrayBuffer", "Atomics", "AsyncIterable",
		"AsyncIterableIterator", "AsyncGenerator", "AsyncGeneratorFunction", "BigInt", "Reflect", "BigInt64Array", "BigUint64Array":
		return diagnostics.Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_1_or_later
	case "await":
		if ast.IsCallExpression(node.Parent) {
			return diagnostics.Cannot_find_name_0_Did_you_mean_to_write_this_in_an_async_function
		}
		fallthrough
	default:
		if node.Parent.Kind == ast.KindShorthandPropertyAssignment {
			return diagnostics.No_value_exists_in_scope_for_the_shorthand_property_0_Either_declare_one_or_provide_an_initializer
		}
		return diagnostics.Cannot_find_name_0
	}
}

func (c *Checker) GetDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic {
	return c.getDiagnostics(ctx, sourceFile, &c.diagnostics)
}

func (c *Checker) GetSuggestionDiagnostics(ctx context.Context, sourceFile *ast.SourceFile) []*ast.Diagnostic {
	return c.getDiagnostics(ctx, sourceFile, &c.suggestionDiagnostics)
}

func (c *Checker) getDiagnostics(ctx context.Context, sourceFile *ast.SourceFile, collection *ast.DiagnosticsCollection) []*ast.Diagnostic {
	c.checkNotCanceled()
	checkUnused := c.compilerOptions.NoUnusedLocals.IsTrue() || c.compilerOptions.NoUnusedParameters.IsTrue() || collection == &c.suggestionDiagnostics
	c.checkSourceFile(ctx, sourceFile, checkUnused)
	if c.wasCanceled {
		return nil
	}
	return collection.GetDiagnosticsForFile(sourceFile.FileName())
}

func (c *Checker) GetGlobalDiagnostics() []*ast.Diagnostic {
	c.checkNotCanceled()
	return c.diagnostics.GetGlobalDiagnostics()
}

func (c *Checker) addDeferredDiagnostic(callback func()) {
	if c.saveDeferredDiagnostics {
		c.deferredDiagnosticCallbacks = append(c.deferredDiagnosticCallbacks, callback)
	}
}

func (c *Checker) produceDeferredDiagnostics() {
	for _, cb := range c.deferredDiagnosticCallbacks {
		cb()
	}
	c.deferredDiagnosticCallbacks = nil
}

func (c *Checker) addDiagnostic(diagnostic *ast.Diagnostic) {
	// Discard diagnostics created while at the maximum number of recursive TypeToString invocations.
	if c.serializationLevel < maxSerializationLevel {
		c.diagnostics.Add(diagnostic)
	}
}

func (c *Checker) addSuggestionDiagnostic(diagnostic *ast.Diagnostic) {
	// Discard diagnostics created while at the maximum number of recursive TypeToString invocations.
	if c.serializationLevel < maxSerializationLevel {
		c.suggestionDiagnostics.Add(diagnostic)
	}
}

func (c *Checker) error(location *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic {
	diagnostic := NewDiagnosticForNode(location, message, args...)
	c.addDiagnostic(diagnostic)
	return diagnostic
}

func (c *Checker) errorSkippedOnNoEmit(location *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic {
	diagnostic := c.error(location, message, args...)
	diagnostic.SetSkippedOnNoEmit()
	return diagnostic
}

func (c *Checker) errorOrSuggestion(isError bool, location *ast.Node, message *diagnostics.Message, args ...any) {
	c.addErrorOrSuggestion(isError, NewDiagnosticForNode(location, message, args...))
}

func (c *Checker) errorAndMaybeSuggestAwait(location *ast.Node, maybeMissingAwait bool, message *diagnostics.Message, args ...any) *ast.Diagnostic {
	diagnostic := c.error(location, message, args...)
	if maybeMissingAwait {
		diagnostic.AddRelatedInfo(createDiagnosticForNode(location, diagnostics.Did_you_forget_to_use_await))
	}
	return diagnostic
}

func (c *Checker) addErrorOrSuggestion(isError bool, diagnostic *ast.Diagnostic) {
	if isError {
		c.addDiagnostic(diagnostic)
	} else {
		suggestion := *diagnostic
		suggestion.SetCategory(diagnostics.CategorySuggestion)
		c.addSuggestionDiagnostic(&suggestion)
	}
}

func (c *Checker) IsDeprecatedDeclaration(declaration *ast.Node) bool {
	return ast.IsDeprecatedDeclarationWithCachedFlags(declaration, c.getCombinedNodeFlagsCached(declaration))
}

func (c *Checker) addDeprecatedSuggestion(location *ast.Node, declarations []*ast.Node, deprecatedEntity string) *ast.Diagnostic {
	diagnostic := NewDiagnosticForNode(location, diagnostics.X_0_is_deprecated, deprecatedEntity)
	return c.addDeprecatedSuggestionWorker(declarations, diagnostic)
}

func (c *Checker) addDeprecatedSuggestionWorker(declarations []*ast.Node, diagnostic *ast.Diagnostic) *ast.Diagnostic {
	for _, declaration := range declarations {
		deprecatedTag := ast.GetJSDocDeprecatedTag(declaration)
		if deprecatedTag != nil {
			diagnostic.AddRelatedInfo(NewDiagnosticForNode(deprecatedTag, diagnostics.The_declaration_was_marked_as_deprecated_here))
			break
		}
	}
	c.addSuggestionDiagnostic(diagnostic)
	return diagnostic
}

func (c *Checker) isDeprecatedSymbol(symbol *ast.Symbol) bool {
	parentSymbol := c.getParentOfSymbol(symbol)
	if parentSymbol != nil && len(symbol.Declarations) > 1 {
		if parentSymbol.Flags&ast.SymbolFlagsInterface != 0 {
			return core.Some(symbol.Declarations, c.IsDeprecatedDeclaration)
		} else {
			return core.Every(symbol.Declarations, c.IsDeprecatedDeclaration)
		}
	}
	return symbol.ValueDeclaration != nil && c.IsDeprecatedDeclaration(symbol.ValueDeclaration) || len(symbol.Declarations) != 0 && core.Every(symbol.Declarations, c.IsDeprecatedDeclaration)
}

func (c *Checker) hasParseDiagnostics(sourceFile *ast.SourceFile) bool {
	return len(sourceFile.Diagnostics()) > 0
}

func (c *Checker) newSymbol(flags ast.SymbolFlags, name string) *ast.Symbol {
	c.SymbolCount++
	result := c.symbolArena.New()
	result.Flags = flags | ast.SymbolFlagsTransient
	result.Name = name
	return result
}

func (c *Checker) newSymbolEx(flags ast.SymbolFlags, name string, checkFlags ast.CheckFlags) *ast.Symbol {
	result := c.newSymbol(flags, name)
	result.CheckFlags = checkFlags
	return result
}

func (c *Checker) newParameter(name string, t *Type) *ast.Symbol {
	symbol := c.newSymbol(ast.SymbolFlagsFunctionScopedVariable, name)
	c.valueSymbolLinks.Get(symbol).resolvedType = t
	return symbol
}

func (c *Checker) newProperty(name string, t *Type) *ast.Symbol {
	symbol := c.newSymbol(ast.SymbolFlagsProperty, name)
	c.valueSymbolLinks.Get(symbol).resolvedType = t
	return symbol
}

func (c *Checker) combineSymbolTables(first ast.SymbolTable, second ast.SymbolTable) ast.SymbolTable {
	if len(first) == 0 {
		return second
	}
	if len(second) == 0 {
		return first
	}
	combined := make(ast.SymbolTable)
	c.mergeSymbolTable(combined, first, false, nil)
	c.mergeSymbolTable(combined, second, false, nil)
	return combined
}

func (c *Checker) mergeSymbolTable(target ast.SymbolTable, source ast.SymbolTable, unidirectional bool, mergedParent *ast.Symbol) {
	for id, sourceSymbol := range source {
		targetSymbol := target[id]
		var merged *ast.Symbol
		if targetSymbol != nil {
			merged = c.mergeSymbol(targetSymbol, sourceSymbol, unidirectional)
		} else {
			merged = c.getMergedSymbol(sourceSymbol)
		}
		if mergedParent != nil && targetSymbol != nil {
			// If a merge was performed on the target symbol, set its parent to the merged parent that initiated the merge
			// of its exports. Otherwise, `merged` came only from `sourceSymbol` and can keep its parent:
			//
			// // a.ts
			// export interface A { x: number; }
			//
			// // b.ts
			// declare module "./a" {
			//   interface A { y: number; }
			//   interface B {}
			// }
			//
			// When merging the module augmentation into a.ts, the symbol for `A` will itself be merged, so its parent
			// should be the merged module symbol. But the symbol for `B` has only one declaration, so its parent should
			// be the module augmentation symbol, which contains its only declaration.
			if merged.Flags&ast.SymbolFlagsTransient != 0 {
				merged.Parent = mergedParent
			}
		}
		target[id] = merged
	}
}

/**
 * Note: if target is transient, then it is mutable, and mergeSymbol with both mutate and return it.
 * If target is not transient, mergeSymbol will produce a transient clone, mutate that and return it.
 */
func (c *Checker) mergeSymbol(target *ast.Symbol, source *ast.Symbol, unidirectional bool) *ast.Symbol {
	if target.Flags&getExcludedSymbolFlags(source.Flags) == 0 || (source.Flags|target.Flags)&ast.SymbolFlagsAssignment != 0 {
		if source == target {
			// This can happen when an export assigned namespace exports something also erroneously exported at the top level
			// See `declarationFileNoCrashOnExtraExportModifier` for an example
			return target
		}
		if target.Flags&ast.SymbolFlagsTransient == 0 {
			resolvedTarget := c.resolveSymbol(target)
			if resolvedTarget == c.unknownSymbol {
				return source
			}
			if resolvedTarget.Flags&getExcludedSymbolFlags(source.Flags) == 0 || (source.Flags|resolvedTarget.Flags)&ast.SymbolFlagsAssignment != 0 {
				target = c.cloneSymbol(resolvedTarget)
			} else {
				c.reportMergeSymbolError(target, source)
				return source
			}
		}
		// Javascript static-property-assignment declarations always merge, even though they are also values
		if source.Flags&ast.SymbolFlagsValueModule != 0 && target.Flags&ast.SymbolFlagsValueModule != 0 && target.Flags&ast.SymbolFlagsConstEnumOnlyModule != 0 && source.Flags&ast.SymbolFlagsConstEnumOnlyModule == 0 {
			// reset flag when merging instantiated module into value module that has only const enums
			target.Flags &^= ast.SymbolFlagsConstEnumOnlyModule
		}
		sourceFlags := source.Flags
		if target.Flags&ast.SymbolFlagsConstEnumOnlyModule == 0 {
			sourceFlags &^= ast.SymbolFlagsConstEnumOnlyModule
		}
		target.Flags |= sourceFlags
		if source.ValueDeclaration != nil {
			binder.SetValueDeclaration(target, source.ValueDeclaration)
		}
		target.Declarations = append(target.Declarations, source.Declarations...)
		if source.Members != nil {
			c.mergeSymbolTable(ast.GetSymbolTable(&target.Members), source.Members, unidirectional, nil)
		}
		if source.Exports != nil {
			c.mergeSymbolTable(ast.GetSymbolTable(&target.Exports), source.Exports, unidirectional, target)
		}
		if !unidirectional {
			c.recordMergedSymbol(target, source)
		}
	} else if target.Flags&ast.SymbolFlagsNamespaceModule != 0 {
		// Do not report an error when merging `var globalThis` with the built-in `globalThis`,
		// as we will already report a "Declaration name conflicts..." error, and this error
		// won't make much sense.
		if target != c.globalThisSymbol {
			c.error(ast.GetNameOfDeclaration(getFirstDeclaration(source)), diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, c.symbolToString(target))
		}
	} else {
		c.reportMergeSymbolError(target, source)
	}
	return target
}

func (c *Checker) reportMergeSymbolError(target *ast.Symbol, source *ast.Symbol) {
	isEitherEnum := target.Flags&ast.SymbolFlagsEnum != 0 || source.Flags&ast.SymbolFlagsEnum != 0
	isEitherBlockScoped := target.Flags&ast.SymbolFlagsBlockScopedVariable != 0 || source.Flags&ast.SymbolFlagsBlockScopedVariable != 0
	var message *diagnostics.Message
	switch {
	case isEitherEnum:
		message = diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations
	case isEitherBlockScoped:
		message = diagnostics.Cannot_redeclare_block_scoped_variable_0
	default:
		message = diagnostics.Duplicate_identifier_0
	}
	sourceSymbolFile := ast.GetSourceFileOfNode(getFirstDeclaration(source))
	targetSymbolFile := ast.GetSourceFileOfNode(getFirstDeclaration(target))
	isSourcePlainJS := ast.IsPlainJSFile(sourceSymbolFile, c.compilerOptions.CheckJs)
	isTargetPlainJS := ast.IsPlainJSFile(targetSymbolFile, c.compilerOptions.CheckJs)
	symbolName := c.symbolToString(source)
	if !isSourcePlainJS {
		c.addDuplicateDeclarationErrorsForSymbols(source, message, symbolName, target)
	}
	if !isTargetPlainJS {
		c.addDuplicateDeclarationErrorsForSymbols(target, message, symbolName, source)
	}
}

func (c *Checker) addDuplicateDeclarationErrorsForSymbols(target *ast.Symbol, message *diagnostics.Message, symbolName string, source *ast.Symbol) {
	for _, node := range target.Declarations {
		c.addDuplicateDeclarationError(node, message, symbolName, source.Declarations)
	}
}

func (c *Checker) addDuplicateDeclarationError(node *ast.Node, message *diagnostics.Message, symbolName string, relatedNodes []*ast.Node) {
	errorNode := getAdjustedNodeForError(node)
	if errorNode == nil {
		errorNode = node
	}
	err := c.lookupOrIssueError(errorNode, message, symbolName)
	for _, relatedNode := range relatedNodes {
		adjustedNode := getAdjustedNodeForError(relatedNode)
		if adjustedNode == errorNode {
			continue
		}
		leadingMessage := createDiagnosticForNode(adjustedNode, diagnostics.X_0_was_also_declared_here, symbolName)
		followOnMessage := createDiagnosticForNode(adjustedNode, diagnostics.X_and_here)
		if len(err.RelatedInformation()) >= 5 || core.Some(err.RelatedInformation(), func(d *ast.Diagnostic) bool {
			return ast.CompareDiagnostics(d, followOnMessage) == 0 || ast.CompareDiagnostics(d, leadingMessage) == 0
		}) {
			continue
		}
		if len(err.RelatedInformation()) == 0 {
			err.AddRelatedInfo(leadingMessage)
		} else {
			err.AddRelatedInfo(followOnMessage)
		}
	}
}

func createDiagnosticForNode(node *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic {
	return NewDiagnosticForNode(node, message, args...)
}

func getAdjustedNodeForError(node *ast.Node) *ast.Node {
	name := ast.GetNameOfDeclaration(node)
	if name != nil {
		return name
	}
	return node
}

func (c *Checker) lookupOrIssueError(location *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic {
	diagnostic := NewDiagnosticForNode(location, message, args...)
	existing := c.diagnostics.Lookup(diagnostic)
	if existing != nil {
		return existing
	}
	c.addDiagnostic(diagnostic)
	return diagnostic
}

func getFirstDeclaration(symbol *ast.Symbol) *ast.Node {
	if len(symbol.Declarations) > 0 {
		return symbol.Declarations[0]
	}
	return nil
}

func getExcludedSymbolFlags(flags ast.SymbolFlags) ast.SymbolFlags {
	var result ast.SymbolFlags
	if flags&ast.SymbolFlagsBlockScopedVariable != 0 {
		result |= ast.SymbolFlagsBlockScopedVariableExcludes
	}
	if flags&ast.SymbolFlagsFunctionScopedVariable != 0 {
		result |= ast.SymbolFlagsFunctionScopedVariableExcludes
	}
	if flags&ast.SymbolFlagsProperty != 0 {
		result |= ast.SymbolFlagsPropertyExcludes
	}
	if flags&ast.SymbolFlagsEnumMember != 0 {
		result |= ast.SymbolFlagsEnumMemberExcludes
	}
	if flags&ast.SymbolFlagsFunction != 0 {
		result |= ast.SymbolFlagsFunctionExcludes
	}
	if flags&ast.SymbolFlagsClass != 0 {
		result |= ast.SymbolFlagsClassExcludes
	}
	if flags&ast.SymbolFlagsInterface != 0 {
		result |= ast.SymbolFlagsInterfaceExcludes
	}
	if flags&ast.SymbolFlagsRegularEnum != 0 {
		result |= ast.SymbolFlagsRegularEnumExcludes
	}
	if flags&ast.SymbolFlagsConstEnum != 0 {
		result |= ast.SymbolFlagsConstEnumExcludes
	}
	if flags&ast.SymbolFlagsValueModule != 0 {
		result |= ast.SymbolFlagsValueModuleExcludes
	}
	if flags&ast.SymbolFlagsMethod != 0 {
		result |= ast.SymbolFlagsMethodExcludes
	}
	if flags&ast.SymbolFlagsGetAccessor != 0 {
		result |= ast.SymbolFlagsGetAccessorExcludes
	}
	if flags&ast.SymbolFlagsSetAccessor != 0 {
		result |= ast.SymbolFlagsSetAccessorExcludes
	}
	if flags&ast.SymbolFlagsTypeParameter != 0 {
		result |= ast.SymbolFlagsTypeParameterExcludes
	}
	if flags&ast.SymbolFlagsTypeAlias != 0 {
		result |= ast.SymbolFlagsTypeAliasExcludes
	}
	if flags&ast.SymbolFlagsAlias != 0 {
		result |= ast.SymbolFlagsAliasExcludes
	}
	if flags&ast.SymbolFlagsReplaceableByMethod != 0 {
		result &^= ast.SymbolFlagsMethod
	}
	return result
}

func (c *Checker) cloneSymbol(symbol *ast.Symbol) *ast.Symbol {
	result := c.newSymbol(symbol.Flags, symbol.Name)
	// Force reallocation if anything is ever appended to declarations
	result.Declarations = symbol.Declarations[0:len(symbol.Declarations):len(symbol.Declarations)]
	result.Parent = symbol.Parent
	result.ValueDeclaration = symbol.ValueDeclaration
	result.Members = maps.Clone(symbol.Members)
	result.Exports = maps.Clone(symbol.Exports)
	c.recordMergedSymbol(result, symbol)
	return result
}

func (c *Checker) getMergedSymbol(symbol *ast.Symbol) *ast.Symbol {
	if symbol != nil {
		merged := c.mergedSymbols[symbol]
		if merged != nil {
			return merged
		}
	}
	return symbol
}

func (c *Checker) getParentOfSymbol(symbol *ast.Symbol) *ast.Symbol {
	if symbol.Parent != nil {
		return c.getMergedSymbol(c.getLateBoundSymbol(symbol.Parent))
	}
	return nil
}

func (c *Checker) recordMergedSymbol(target *ast.Symbol, source *ast.Symbol) {
	c.mergedSymbols[source] = target
}

func (c *Checker) getSymbolIfSameReference(s1 *ast.Symbol, s2 *ast.Symbol) *ast.Symbol {
	if c.getMergedSymbol(c.resolveSymbol(c.getMergedSymbol(s1))) == c.getMergedSymbol(c.resolveSymbol(c.getMergedSymbol(s2))) {
		return s1
	}
	return nil
}

func (c *Checker) getExportSymbolOfValueSymbolIfExported(symbol *ast.Symbol) *ast.Symbol {
	if symbol != nil && symbol.Flags&ast.SymbolFlagsExportValue != 0 && symbol.ExportSymbol != nil {
		symbol = symbol.ExportSymbol
	}
	return c.getMergedSymbol(symbol)
}

func (c *Checker) getSymbolOfDeclaration(node *ast.Node) *ast.Symbol {
	symbol := node.Symbol()
	if symbol != nil {
		return c.getMergedSymbol(c.getLateBoundSymbol(symbol))
	}
	return nil
}

// Get the merged symbol for a node. If you know the node is a `Declaration`, it is more type safe to
// use use `getSymbolOfDeclaration` instead.
func (c *Checker) getSymbolOfNode(node *ast.Node) *ast.Symbol {
	data := node.DeclarationData()
	if data != nil && data.Symbol != nil {
		return c.getMergedSymbol(c.getLateBoundSymbol(data.Symbol))
	}
	return nil
}

func (c *Checker) getLateBoundSymbol(symbol *ast.Symbol) *ast.Symbol {
	if symbol.Flags&ast.SymbolFlagsClassMember == 0 || symbol.Name != ast.InternalSymbolNameComputed {
		return symbol
	}
	links := c.lateBoundLinks.Get(symbol)
	if links.lateSymbol == nil && core.Some(symbol.Declarations, c.hasLateBindableName) {
		// force late binding of members/exports. This will set the late-bound symbol
		parent := c.getMergedSymbol(symbol.Parent)
		if core.Some(symbol.Declarations, ast.HasStaticModifier) {
			c.getExportsOfSymbol(parent)
		} else {
			c.getMembersOfSymbol(parent)
		}
	}
	if links.lateSymbol == nil {
		links.lateSymbol = symbol
	}
	return links.lateSymbol
}

func (c *Checker) resolveSymbol(symbol *ast.Symbol) *ast.Symbol {
	return c.resolveSymbolEx(symbol, false /*dontResolveAlias*/)
}

func (c *Checker) resolveSymbolEx(symbol *ast.Symbol, dontResolveAlias bool) *ast.Symbol {
	if !dontResolveAlias && ast.IsNonLocalAlias(symbol, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace) {
		return c.resolveAlias(symbol)
	}
	return symbol
}

func (c *Checker) getTargetOfImportEqualsDeclaration(node *ast.Node) *ast.Symbol {
	// Node is ImportEqualsDeclaration | VariableDeclaration
	if ast.IsVariableDeclaration(node) || node.AsImportEqualsDeclaration().ModuleReference.Kind == ast.KindExternalModuleReference {
		moduleReference := getExternalModuleRequireArgument(node)
		if moduleReference == nil {
			moduleReference = ast.GetExternalModuleImportEqualsDeclarationExpression(node)
		}
		immediate := c.resolveExternalModuleName(node, moduleReference, false /*ignoreErrors*/)
		resolved := c.resolveExternalModuleSymbol(immediate, true /*dontResolveAlias*/)
		if resolved != nil && core.ModuleKindNode20 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext {
			moduleExports := c.getExportOfModule(resolved, ast.InternalSymbolNameModuleExports, node, true /*dontResolveAlias*/)
			if moduleExports != nil {
				return moduleExports
			}
		}
		c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil)
		return resolved
	}
	resolved := c.getSymbolOfPartOfRightHandSideOfImportEquals(node.AsImportEqualsDeclaration().ModuleReference)
	c.checkAndReportErrorForResolvingImportAliasToTypeOnlySymbol(node, resolved)
	return resolved
}

func (c *Checker) resolveExternalModuleTypeByLiteral(name *ast.Node) *Type {
	moduleSym := c.resolveExternalModuleName(name, name, false /*ignoreErrors*/)
	if moduleSym != nil {
		resolvedModuleSymbol := c.resolveExternalModuleSymbol(moduleSym, false /*dontResolveAlias*/)
		if resolvedModuleSymbol != nil {
			return c.getTypeOfSymbol(resolvedModuleSymbol)
		}
	}
	return c.anyType
}

// This function is only for imports with entity names
func (c *Checker) getSymbolOfPartOfRightHandSideOfImportEquals(entityName *ast.Node) *ast.Symbol {
	// There are three things we might try to look for. In the following examples,
	// the search term is enclosed in |...|:
	//
	//     import a = |b|; // Namespace
	//     import a = |b.c|; // Value, type, namespace
	//     import a = |b.c|.d; // Namespace
	if entityName.Kind == ast.KindIdentifier && ast.IsRightSideOfQualifiedNameOrPropertyAccess(entityName) {
		entityName = entityName.Parent // QualifiedName
	}
	// Check for case 1 and 3 in the above example
	if entityName.Kind == ast.KindIdentifier || entityName.Parent.Kind == ast.KindQualifiedName {
		return c.resolveEntityName(entityName, ast.SymbolFlagsNamespace, false /*ignoreErrors*/, true /*dontResolveAlias*/, nil /*location*/)
	}
	// Case 2 in above example
	// entityName.kind could be a QualifiedName or a Missing identifier
	debug.Assert(entityName.Parent.Kind == ast.KindImportEqualsDeclaration)
	return c.resolveEntityName(entityName, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, false /*ignoreErrors*/, true /*dontResolveAlias*/, nil /*location*/)
}

func (c *Checker) checkAndReportErrorForResolvingImportAliasToTypeOnlySymbol(node *ast.Node, resolved *ast.Symbol) {
	decl := node.AsImportEqualsDeclaration()
	name := decl.ModuleReference
	for {
		if typeOnlyDeclaration := c.getTypeOnlyDeclarationOfEntityName(name); typeOnlyDeclaration != nil {
			isExport := ast.NodeKindIs(typeOnlyDeclaration, ast.KindExportSpecifier, ast.KindExportDeclaration)
			message := core.IfElse(isExport,
				diagnostics.An_import_alias_cannot_reference_a_declaration_that_was_exported_using_export_type,
				diagnostics.An_import_alias_cannot_reference_a_declaration_that_was_imported_using_import_type)
			relatedMessage := core.IfElse(isExport,
				diagnostics.X_0_was_exported_here,
				diagnostics.X_0_was_imported_here)
			// TODO: how to get name for export *?
			name := "*"
			if !ast.IsExportDeclaration(typeOnlyDeclaration) {
				name = typeOnlyDeclaration.Name().Text()
			}
			c.error(decl.ModuleReference, message).AddRelatedInfo(createDiagnosticForNode(typeOnlyDeclaration, relatedMessage, name))
			break
		}
		if ast.IsIdentifier(name) {
			break
		}
		name = name.AsQualifiedName().Left
	}
}

func (c *Checker) getTypeOnlyDeclarationOfEntityName(name *ast.Node) *ast.Node {
	if symbol := c.resolveEntityName(name, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, true /*ignoreErrors*/, true /*dontResolveAlias*/, nil /*location*/); symbol != nil {
		return c.getTypeOnlyAliasDeclaration(symbol)
	}
	return nil
}

func (c *Checker) getTargetOfImportClause(node *ast.Node) *ast.Symbol {
	moduleSymbol := c.resolveExternalModuleName(node, getModuleSpecifierFromNode(node.Parent), false /*ignoreErrors*/)
	if moduleSymbol != nil {
		return c.getTargetOfModuleDefault(moduleSymbol, node, true /*dontResolveAlias*/)
	}
	return nil
}

func (c *Checker) getTargetOfModuleDefault(moduleSymbol *ast.Symbol, node *ast.Node, dontResolveAlias bool) *ast.Symbol {
	file := core.Find(moduleSymbol.Declarations, ast.IsSourceFile)
	specifier := c.getModuleSpecifierForImportOrExport(node)
	var exportDefaultSymbol *ast.Symbol
	var exportModuleDotExportsSymbol *ast.Symbol
	if isShorthandAmbientModuleSymbol(moduleSymbol) {
		// !!! exportDefaultSymbol = moduleSymbol
		// Does nothing?
	} else if file != nil && specifier != nil &&
		core.ModuleKindNode20 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext &&
		c.getEmitSyntaxForModuleSpecifierExpression(specifier) == core.ModuleKindCommonJS &&
		c.program.GetImpliedNodeFormatForEmit(file.AsSourceFile()) == core.ModuleKindESNext {
		exportModuleDotExportsSymbol = c.resolveExportByName(moduleSymbol, ast.InternalSymbolNameModuleExports, node, dontResolveAlias)
	}
	if exportModuleDotExportsSymbol != nil {
		// We have a transpiled default import where the `require` resolves to an ES module with a `module.exports` named
		// export. With `esModuleInterop` (always enabled), this will work:
		//
		// const dep_1 = __importDefault(require("./dep.mjs")); // wraps like { default: require("./dep.mjs") }
		// dep_1.default; // require("./dep.mjs") -> the `module.exports` export value
		c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil)
		return exportModuleDotExportsSymbol
	} else {
		exportDefaultSymbol = c.resolveExportByName(moduleSymbol, ast.InternalSymbolNameDefault, node, dontResolveAlias)
	}
	if specifier == nil {
		return exportDefaultSymbol
	}
	hasDefaultOnly := c.isOnlyImportableAsDefault(specifier, moduleSymbol)
	hasSyntheticDefault := c.canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias, specifier)
	if exportDefaultSymbol == nil && !hasSyntheticDefault && !hasDefaultOnly {
		if ast.IsImportClause(node) {
			c.reportNonDefaultExport(moduleSymbol, node)
		} else {
			var name *ast.Node
			if ast.IsImportOrExportSpecifier(node) {
				name = node.PropertyNameOrName()
			} else {
				name = node.Name()
			}
			c.errorNoModuleMemberSymbol(moduleSymbol, moduleSymbol, node, name)
		}
	} else if hasSyntheticDefault || hasDefaultOnly {
		// per emit behavior, a synthetic default overrides a "real" .default member if `__esModule` is not present
		resolved := c.resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias)
		if resolved == nil {
			resolved = c.resolveSymbolEx(moduleSymbol, dontResolveAlias)
		}
		c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil)
		return resolved
	}
	c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil)
	return exportDefaultSymbol
}

func (c *Checker) reportNonDefaultExport(moduleSymbol *ast.Symbol, node *ast.Node) {
	if moduleSymbol.Exports != nil && moduleSymbol.Exports[node.Symbol().Name] != nil {
		c.error(node, diagnostics.Module_0_has_no_default_export_Did_you_mean_to_use_import_1_from_0_instead, c.symbolToString(moduleSymbol), c.symbolToString(node.Symbol()))
	} else {
		diagnostic := c.error(node.Name(), diagnostics.Module_0_has_no_default_export, c.symbolToString(moduleSymbol))
		var exportStar *ast.Symbol
		if moduleSymbol.Exports != nil {
			exportStar = moduleSymbol.Exports[ast.InternalSymbolNameExportStar]
		}
		if exportStar != nil {
			defaultExport := core.Find(exportStar.Declarations, func(decl *ast.Declaration) bool {
				if !(ast.IsExportDeclaration(decl) && decl.ModuleSpecifier() != nil) {
					return false
				}
				resolvedExternalModuleName := c.resolveExternalModuleName(decl, decl.ModuleSpecifier(), false /*ignoreErrors*/)
				return resolvedExternalModuleName != nil && resolvedExternalModuleName.Exports[ast.InternalSymbolNameDefault] != nil
			})
			if defaultExport != nil {
				diagnostic.AddRelatedInfo(createDiagnosticForNode(defaultExport, diagnostics.X_export_Asterisk_does_not_re_export_a_default))
			}
		}
	}
}

func (c *Checker) resolveExportByName(moduleSymbol *ast.Symbol, name string, sourceNode *ast.Node, dontResolveAlias bool) *ast.Symbol {
	exportValue := moduleSymbol.Exports[ast.InternalSymbolNameExportEquals]
	var exportSymbol *ast.Symbol
	if exportValue != nil {
		exportSymbol = c.getPropertyOfTypeEx(c.getTypeOfSymbol(exportValue), name, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/)
	} else {
		exportSymbol = moduleSymbol.Exports[name]
	}
	resolved := c.resolveSymbolEx(exportSymbol, dontResolveAlias)
	c.markSymbolOfAliasDeclarationIfTypeOnly(sourceNode, nil)
	return resolved
}

func (c *Checker) getTargetOfNamespaceImport(node *ast.Node) *ast.Symbol {
	moduleSpecifier := c.getModuleSpecifierForImportOrExport(node)
	immediate := c.resolveExternalModuleName(node, moduleSpecifier, false /*ignoreErrors*/)
	resolved := c.resolveESModuleSymbol(immediate, node, moduleSpecifier)
	c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil)
	return resolved
}

func (c *Checker) getTargetOfNamespaceExport(node *ast.Node) *ast.Symbol {
	moduleSpecifier := c.getModuleSpecifierForImportOrExport(node)
	if moduleSpecifier != nil {
		immediate := c.resolveExternalModuleName(node, moduleSpecifier, false /*ignoreErrors*/)
		resolved := c.resolveESModuleSymbol(immediate, node, moduleSpecifier)
		c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil)
		return resolved
	}
	return nil
}

func (c *Checker) getTargetOfImportSpecifier(node *ast.Node) *ast.Symbol {
	name := node.PropertyNameOrName()
	if ast.IsImportSpecifier(node) && ast.ModuleExportNameIsDefault(name) {
		specifier := c.getModuleSpecifierForImportOrExport(node)
		if specifier != nil {
			moduleSymbol := c.resolveExternalModuleName(node, specifier, false /*ignoreErrors*/)
			if moduleSymbol != nil {
				return c.getTargetOfModuleDefault(moduleSymbol, node, true /*dontResolveAlias*/)
			}
		}
	}
	root := node.Parent.Parent.Parent // ImportDeclaration
	if ast.IsBindingElement(node) {
		root = ast.GetRootDeclaration(node)
	}
	resolved := c.getExternalModuleMember(root, node, true /*dontResolveAlias*/)
	c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil)
	return resolved
}

func (c *Checker) getExternalModuleMember(node *ast.Node, specifier *ast.Node, dontResolveAlias bool) *ast.Symbol {
	// node is ImportDeclaration | ExportDeclaration | VariableDeclaration
	// specifier is ImportSpecifier | ExportSpecifier | BindingElement | PropertyAccessExpression
	moduleSpecifier := getExternalModuleRequireArgument(node)
	if moduleSpecifier == nil {
		moduleSpecifier = ast.GetExternalModuleName(node)
	}
	moduleSymbol := c.resolveExternalModuleName(node, moduleSpecifier, false /*ignoreErrors*/)
	var name *ast.Node
	if !ast.IsPropertyAccessExpression(specifier) {
		name = specifier.PropertyNameOrName()
	} else {
		name = specifier.Name()
	}
	if !ast.IsIdentifier(name) && !ast.IsStringLiteral(name) {
		return nil
	}
	nameText := name.Text()
	targetSymbol := c.resolveESModuleSymbol(moduleSymbol, specifier, moduleSpecifier)
	if targetSymbol != nil {
		// Note: The empty string is a valid module export name:
		//
		//   import { "" as foo } from "./foo";
		//   export { foo as "" };
		//
		if nameText != "" || name.Kind == ast.KindStringLiteral {
			if isShorthandAmbientModuleSymbol(moduleSymbol) {
				return moduleSymbol
			}
			var symbolFromVariable *ast.Symbol
			// First check if module was specified with "export=". If so, get the member from the resolved type
			if moduleSymbol != nil && moduleSymbol.Exports[ast.InternalSymbolNameExportEquals] != nil {
				symbolFromVariable = c.getPropertyOfTypeEx(c.getTypeOfSymbol(targetSymbol), nameText, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/)
			} else {
				symbolFromVariable = c.getPropertyOfVariable(targetSymbol, nameText)
			}
			// if symbolFromVariable is export - get its final target
			symbolFromVariable = c.resolveSymbolEx(symbolFromVariable, dontResolveAlias)
			exportContainer := targetSymbol
			if moduleSymbol != nil && moduleSymbol.Exports[ast.InternalSymbolNameExportEquals] != nil {
				// For `export =` modules, supplemental type/namespace exports live on the original module symbol.
				exportContainer = moduleSymbol
			}
			symbolFromModule := c.getExportOfModule(exportContainer, nameText, specifier, dontResolveAlias)
			if symbolFromModule == nil && nameText == ast.InternalSymbolNameDefault {
				file := core.Find(moduleSymbol.Declarations, ast.IsSourceFile)
				if c.isOnlyImportableAsDefault(moduleSpecifier, moduleSymbol) || c.canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias, moduleSpecifier) {
					symbolFromModule = c.resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias)
					if symbolFromModule == nil {
						symbolFromModule = c.resolveSymbolEx(moduleSymbol, dontResolveAlias)
					}
				}
			}
			symbol := symbolFromVariable
			if symbolFromModule != nil {
				symbol = symbolFromModule
				if symbolFromVariable != nil {
					symbol = c.combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule)
				}
			}
			if ast.IsImportOrExportSpecifier(specifier) && c.isOnlyImportableAsDefault(moduleSpecifier, moduleSymbol) && nameText != ast.InternalSymbolNameDefault {
				c.error(name, diagnostics.Named_imports_from_a_JSON_file_into_an_ECMAScript_module_are_not_allowed_when_module_is_set_to_0, c.moduleKind.String())
			} else if symbol == nil {
				c.errorNoModuleMemberSymbol(moduleSymbol, targetSymbol, node, name)
			}
			return symbol
		}
	}
	return nil
}

func (c *Checker) getPropertyOfVariable(symbol *ast.Symbol, name string) *ast.Symbol {
	if symbol.Flags&ast.SymbolFlagsVariable != 0 {
		typeAnnotation := symbol.ValueDeclaration.Type()
		if typeAnnotation != nil {
			return c.resolveSymbol(c.getPropertyOfType(c.getTypeFromTypeNode(typeAnnotation), name))
		}
	}
	return nil
}

// This function creates a synthetic symbol that combines the value side of one symbol with the
// type/namespace side of another symbol. Consider this example:
//
//	declare module graphics {
//	    interface Point {
//	        x: number;
//	        y: number;
//	    }
//	}
//	declare var graphics: {
//	    Point: new (x: number, y: number) => graphics.Point;
//	}
//	declare module "graphics" {
//	    export = graphics;
//	}
//
// An 'import { Point } from "graphics"' needs to create a symbol that combines the value side 'Point'
// property with the type/namespace side interface 'Point'.
func (c *Checker) combineValueAndTypeSymbols(valueSymbol *ast.Symbol, typeSymbol *ast.Symbol) *ast.Symbol {
	if valueSymbol == c.unknownSymbol && typeSymbol == c.unknownSymbol {
		return c.unknownSymbol
	}
	if typeSymbol.Flags&ast.SymbolFlagsValue != 0 {
		return typeSymbol
	}
	if valueSymbol.Flags&(ast.SymbolFlagsType|ast.SymbolFlagsNamespace) != 0 {
		return valueSymbol
	}
	result := c.newSymbol(valueSymbol.Flags|typeSymbol.Flags, valueSymbol.Name)
	debug.Assert(len(valueSymbol.Declarations) > 0 || len(typeSymbol.Declarations) > 0)
	result.Declarations = slices.Compact(slices.Concat(valueSymbol.Declarations, typeSymbol.Declarations))
	result.Parent = valueSymbol.Parent
	if result.Parent == nil {
		result.Parent = typeSymbol.Parent
	}
	result.ValueDeclaration = valueSymbol.ValueDeclaration
	result.Members = maps.Clone(typeSymbol.Members)
	result.Exports = maps.Clone(valueSymbol.Exports)
	return result
}

func (c *Checker) getExportOfModule(symbol *ast.Symbol, nameText string, specifier *ast.Node, dontResolveAlias bool) *ast.Symbol {
	if symbol.Flags&ast.SymbolFlagsModule != 0 {
		exportSymbol := c.getExportsOfSymbol(symbol)[nameText]
		resolved := c.resolveSymbolEx(exportSymbol, dontResolveAlias)
		exportStarDeclaration := c.moduleSymbolLinks.Get(symbol).typeOnlyExportStarMap[nameText]
		c.markSymbolOfAliasDeclarationIfTypeOnly(specifier, exportStarDeclaration)
		return resolved
	}
	return nil
}

func (c *Checker) isOnlyImportableAsDefault(usage *ast.Node, resolvedModule *ast.Symbol) bool {
	// In Node.js, JSON modules don't get named exports
	if core.ModuleKindNode16 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext {
		usageMode := c.getEmitSyntaxForModuleSpecifierExpression(usage)
		if usageMode == core.ModuleKindESNext {
			if resolvedModule == nil {
				resolvedModule = c.resolveExternalModuleName(usage, usage, true /*ignoreErrors*/)
			}
			var targetFile *ast.SourceFile
			if resolvedModule != nil {
				targetFile = ast.GetSourceFileOfModule(resolvedModule)
			}
			return targetFile != nil && (ast.IsJsonSourceFile(targetFile) || tspath.GetDeclarationFileExtension(targetFile.FileName()) == ".d.json.ts")
		}
	}
	return false
}

func (c *Checker) canHaveSyntheticDefault(file *ast.Node, moduleSymbol *ast.Symbol, dontResolveAlias bool, usage *ast.Node) bool {
	var usageMode core.ResolutionMode
	if file != nil {
		usageMode = c.getEmitSyntaxForModuleSpecifierExpression(usage)
	}
	if file != nil && usageMode != core.ModuleKindNone {
		targetMode := c.program.GetImpliedNodeFormatForEmit(file.AsSourceFile())
		if usageMode == core.ModuleKindESNext && targetMode == core.ModuleKindCommonJS && core.ModuleKindNode16 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext {
			// In Node.js, CommonJS modules always have a synthetic default when imported into ESM
			return true
		}
		if usageMode == core.ModuleKindESNext && targetMode == core.ModuleKindESNext {
			// No matter what the `module` setting is, if we're confident that both files
			// are ESM, there cannot be a synthetic default.
			return false
		}
		// For other files (not node16/nodenext with impliedNodeFormat), check if we can determine
		// the module format from project references
		if targetMode == core.ModuleKindNone && file.AsSourceFile().IsDeclarationFile {
			// Try to get the project reference - try both source file mapping and output file mapping
			// since declaration files can be mapped either way depending on how they're resolved
			if c.program.GetRedirectForResolution(file.AsSourceFile()) != nil || c.program.GetProjectReferenceFromOutputDts(file.AsSourceFile().Path()) != nil {
				// This is a declaration file from a project reference, so we can determine
				// its module format from the referenced project's options
				targetModuleKind := c.program.GetEmitModuleFormatOfFile(file.AsSourceFile())
				if usageMode == core.ModuleKindESNext && core.ModuleKindES2015 <= targetModuleKind && targetModuleKind <= core.ModuleKindESNext {
					return false
				}
			}
		}
	}
	// Declaration files (and ambient modules)
	if file == nil || file.AsSourceFile().IsDeclarationFile {
		// Definitely cannot have a synthetic default if they have a syntactic default member specified
		defaultExportSymbol := c.resolveExportByName(moduleSymbol, ast.InternalSymbolNameDefault /*sourceNode*/, nil /*dontResolveAlias*/, true) // Dont resolve alias because we want the immediately exported symbol's declaration
		if defaultExportSymbol != nil && core.Some(defaultExportSymbol.Declarations, isSyntacticDefault) {
			return false
		}
		// It _might_ still be incorrect to assume there is no __esModule marker on the import at runtime, even if there is no `default` member
		// So we check a bit more,
		if c.resolveExportByName(moduleSymbol, "__esModule", nil /*sourceNode*/, dontResolveAlias) != nil {
			// If there is an `__esModule` specified in the declaration (meaning someone explicitly added it or wrote it in their code),
			// it definitely is a module and does not have a synthetic default
			return false
		}
		// There are _many_ declaration files not written with esmodules in mind that still get compiled into a format with __esModule set
		// Meaning there may be no default at runtime - however to be on the permissive side, we allow access to a synthetic default member
		// as there is no marker to indicate if the accompanying JS has `__esModule` or not, or is even native esm
		return true
	}
	// TypeScript files never have a synthetic default (as they are always emitted with an __esModule marker) _unless_ they contain an export= statement
	if !ast.IsInJSFile(file) {
		return hasExportAssignmentSymbol(moduleSymbol)
	}

	// JS files have a synthetic default if they do not contain ES2015+ module syntax (export = is not valid in js) _and_ do not have an __esModule marker
	return (file.AsSourceFile().ExternalModuleIndicator == nil || file.AsSourceFile().ExternalModuleIndicator == file) && c.resolveExportByName(moduleSymbol, "__esModule", nil /*sourceNode*/, dontResolveAlias) == nil
}

func (c *Checker) getEmitSyntaxForModuleSpecifierExpression(usage *ast.Node) core.ResolutionMode {
	if ast.IsStringLiteralLike(usage) {
		return c.program.GetEmitSyntaxForUsageLocation(ast.GetSourceFileOfNode(usage), usage)
	}
	return core.ModuleKindNone
}

func (c *Checker) errorNoModuleMemberSymbol(moduleSymbol *ast.Symbol, targetSymbol *ast.Symbol, node *ast.Node, name *ast.Node) {
	if c.compilerOptions.NoCheck.IsTrue() {
		return
	}
	moduleName := c.getFullyQualifiedName(moduleSymbol, node)
	declarationName := scanner.DeclarationNameToString(name)
	var suggestion *ast.Symbol
	if ast.IsIdentifier(name) {
		suggestion = c.getSuggestedSymbolForNonexistentModule(name, targetSymbol)
	}
	if suggestion != nil {
		suggestionName := c.symbolToString(suggestion)
		diagnostic := c.error(name, diagnostics.X_0_has_no_exported_member_named_1_Did_you_mean_2, moduleName, declarationName, suggestionName)
		if suggestion.ValueDeclaration != nil {
			diagnostic.AddRelatedInfo(createDiagnosticForNode(suggestion.ValueDeclaration, diagnostics.X_0_is_declared_here, suggestionName))
		}
	} else {
		if moduleSymbol.Exports[ast.InternalSymbolNameDefault] != nil {
			c.error(name, diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_to_use_import_1_from_0_instead, moduleName, declarationName)
		} else {
			c.reportNonExportedMember(name, declarationName, moduleSymbol, moduleName)
		}
	}
}

func (c *Checker) reportNonExportedMember(name *ast.Node, declarationName string, moduleSymbol *ast.Symbol, moduleName string) {
	var localSymbol *ast.Symbol
	if locals := moduleSymbol.ValueDeclaration.Locals(); locals != nil {
		localSymbol = locals[name.Text()]
	}
	exports := moduleSymbol.Exports
	if localSymbol != nil {
		if exportedEqualsSymbol := exports[ast.InternalSymbolNameExportEquals]; exportedEqualsSymbol != nil {
			if c.getSymbolIfSameReference(exportedEqualsSymbol, localSymbol) != nil {
				c.reportInvalidImportEqualsExportMember(name, declarationName, moduleName)
			} else {
				c.error(name, diagnostics.Module_0_has_no_exported_member_1, moduleName, declarationName)
			}
		} else {
			exportedSymbol := findInMap(exports, func(symbol *ast.Symbol) bool {
				return c.getSymbolIfSameReference(symbol, localSymbol) != nil
			})
			var diagnostic *ast.Diagnostic
			if exportedSymbol != nil {
				diagnostic = c.error(name, diagnostics.Module_0_declares_1_locally_but_it_is_exported_as_2, moduleName, declarationName, c.symbolToString(exportedSymbol))
			} else {
				diagnostic = c.error(name, diagnostics.Module_0_declares_1_locally_but_it_is_not_exported, moduleName, declarationName)
			}
			for i, decl := range localSymbol.Declarations {
				diagnostic.AddRelatedInfo(createDiagnosticForNode(decl, core.IfElse(i == 0, diagnostics.X_0_is_declared_here, diagnostics.X_and_here), declarationName))
			}
		}
	} else {
		c.error(name, diagnostics.Module_0_has_no_exported_member_1, moduleName, declarationName)
	}
}

func (c *Checker) reportInvalidImportEqualsExportMember(name *ast.Node, declarationName string, moduleName string) {
	if c.moduleKind >= core.ModuleKindES2015 {
		c.error(name, diagnostics.X_0_can_only_be_imported_by_using_a_default_import, declarationName)
	} else if ast.IsInJSFile(name) {
		c.error(name, diagnostics.X_0_can_only_be_imported_by_using_a_require_call_or_by_using_a_default_import, declarationName)
	} else {
		c.error(name, diagnostics.X_0_can_only_be_imported_by_using_import_1_require_2_or_a_default_import, declarationName, declarationName, moduleName)
	}
}

func (c *Checker) getTargetOfExportSpecifier(node *ast.Node, meaning ast.SymbolFlags, dontResolveAlias bool) *ast.Symbol {
	name := node.PropertyNameOrName()
	if ast.ModuleExportNameIsDefault(name) {
		specifier := c.getModuleSpecifierForImportOrExport(node)
		if specifier != nil {
			moduleSymbol := c.resolveExternalModuleName(node, specifier, false /*ignoreErrors*/)
			if moduleSymbol != nil {
				return c.getTargetOfModuleDefault(moduleSymbol, node, dontResolveAlias)
			}
		}
	}
	exportDeclaration := node.Parent.Parent
	var resolved *ast.Symbol
	switch {
	case exportDeclaration.ModuleSpecifier() != nil:
		resolved = c.getExternalModuleMember(exportDeclaration, node, dontResolveAlias)
	case ast.IsStringLiteral(name):
		resolved = nil
	default:
		resolved = c.resolveEntityName(name, meaning, false /*ignoreErrors*/, dontResolveAlias, nil /*location*/)
	}
	c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil)
	return resolved
}

func (c *Checker) getTargetOfExportAssignment(node *ast.Node) *ast.Symbol {
	resolved := c.getTargetOfAliasLikeExpression(node.Expression())
	c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil)
	return resolved
}

func (c *Checker) getTargetOfBinaryExpression(node *ast.Node) *ast.Symbol {
	resolved := c.getTargetOfAliasLikeExpression(node.AsBinaryExpression().Right)
	c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil)
	return resolved
}

func (c *Checker) getTargetOfAliasLikeExpression(expression *ast.Node) *ast.Symbol {
	if ast.IsClassExpression(expression) {
		return c.checkExpressionCached(expression).symbol
	}
	if !ast.IsEntityName(expression) && !ast.IsEntityNameExpression(expression) {
		return nil
	}
	aliasLike := c.resolveEntityName(expression, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, true /*ignoreErrors*/, true /*dontResolveAlias*/, nil /*location*/)
	if aliasLike != nil {
		return aliasLike
	}
	c.checkExpressionCached(expression)
	return c.getResolvedSymbolOrNil(expression)
}

func (c *Checker) getTargetOfNamespaceExportDeclaration(node *ast.Node) *ast.Symbol {
	if ast.CanHaveSymbol(node.Parent) {
		resolved := c.resolveExternalModuleSymbol(node.Parent.Symbol(), true /*dontResolveAlias*/)
		c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil)
		return resolved
	}
	return nil
}

func (c *Checker) getTargetOfAccessExpression(node *ast.Node) *ast.Symbol {
	if ast.IsBinaryExpression(node.Parent) {
		expr := node.Parent.AsBinaryExpression()
		if expr.Left == node && expr.OperatorToken.Kind == ast.KindEqualsToken {
			return c.getTargetOfAliasLikeExpression(expr.Right)
		}
	}
	return nil
}

func (c *Checker) getModuleSpecifierForImportOrExport(node *ast.Node) *ast.Node {
	switch node.Kind {
	case ast.KindImportClause:
		return getModuleSpecifierFromNode(node.Parent)
	case ast.KindImportEqualsDeclaration:
		if ast.IsExternalModuleReference(node.AsImportEqualsDeclaration().ModuleReference) {
			return node.AsImportEqualsDeclaration().ModuleReference.Expression()
		} else {
			return nil
		}
	case ast.KindNamespaceImport:
		return getModuleSpecifierFromNode(node.Parent.Parent)
	case ast.KindImportSpecifier:
		return getModuleSpecifierFromNode(node.Parent.Parent.Parent)
	case ast.KindNamespaceExport:
		return getModuleSpecifierFromNode(node.Parent)
	case ast.KindExportSpecifier:
		return getModuleSpecifierFromNode(node.Parent.Parent)
	}
	panic("Unhandled case in getModuleSpecifierForImportOrExport")
}

func getModuleSpecifierFromNode(node *ast.Node) *ast.Node {
	switch node.Kind {
	case ast.KindImportDeclaration, ast.KindJSImportDeclaration:
		return node.ModuleSpecifier()
	case ast.KindExportDeclaration:
		return node.ModuleSpecifier()
	}
	panic("Unhandled case in getModuleSpecifierFromNode")
}

/**
 * Marks a symbol as type-only if its declaration is syntactically type-only.
 * If it is not itself marked type-only, but resolves to a type-only alias
 * somewhere in its resolution chain, save a reference to the type-only alias declaration
 * so the alias _not_ marked type-only can be identified as _transitively_ type-only.
 *
 * This function is called on each alias declaration that could be type-only or resolve to
 * another type-only alias during `resolveAlias`, so that later, when an alias is used in a
 * JS-emitting expression, we can quickly determine if that symbol is effectively type-only
 * and issue an error if so.
 *
 * @param aliasDeclaration The alias declaration not marked as type-only
 * @param immediateTarget The symbol to which the alias declaration immediately resolves
 * @param finalTarget The symbol to which the alias declaration ultimately resolves
 * @param overwriteEmpty Checks `resolvesToSymbol` for type-only declarations even if `aliasDeclaration`
 * has already been marked as not resolving to a type-only alias. Used when recursively resolving qualified
 * names of import aliases, e.g. `import C = a.b.C`. If namespace `a` is not found to be type-only, the
 * import declaration will initially be marked as not resolving to a type-only symbol. But, namespace `b`
 * must still be checked for a type-only marker, overwriting the previous negative result if found.
 */

func (c *Checker) markSymbolOfAliasDeclarationIfTypeOnly(aliasDeclaration *ast.Node, exportStarDeclaration *ast.Node) bool {
	if aliasDeclaration == nil || !ast.IsDeclarationNode(aliasDeclaration) {
		return false
	}
	// If the declaration itself is type-only, mark it and return. No need to check what it resolves to.
	sourceSymbol := c.getSymbolOfDeclaration(aliasDeclaration)
	links := c.aliasSymbolLinks.Get(sourceSymbol)
	if links.typeOnlyDeclaration == nil && ast.IsTypeOnlyImportOrExportDeclaration(aliasDeclaration) {
		links.typeOnlyDeclaration = aliasDeclaration
		return true
	}
	if links.typeOnlyDeclaration == nil && exportStarDeclaration != nil {
		links.typeOnlyDeclaration = exportStarDeclaration
		return true
	}
	return links.typeOnlyDeclaration != nil
}

func (c *Checker) resolveExternalModuleName(location *ast.Node, moduleReferenceExpression *ast.Node, ignoreErrors bool) *ast.Symbol {
	errorMessage := c.getCannotResolveModuleNameErrorForSpecificModule(moduleReferenceExpression)
	if errorMessage == nil {
		errorMessage = diagnostics.Cannot_find_module_0_or_its_corresponding_type_declarations
	}
	ignoreErrors = ignoreErrors || c.compilerOptions.NoCheck.IsTrue()
	return c.resolveExternalModuleNameWorker(location, moduleReferenceExpression, core.IfElse(ignoreErrors, nil, errorMessage), ignoreErrors, false /*isForAugmentation*/)
}

func (c *Checker) getCannotResolveModuleNameErrorForSpecificModule(moduleName *ast.Node) *diagnostics.Message {
	if ast.IsStringLiteral(moduleName) {
		if core.NodeCoreModules()[moduleName.Text()] {
			if c.compilerOptions.UsesWildcardTypes() {
				return diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_save_dev_types_Slashnode
			}
			return diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_save_dev_types_Slashnode_and_then_add_node_to_the_types_field_in_your_tsconfig
		}
	}
	return nil
}

func (c *Checker) resolveExternalModuleNameWorker(location *ast.Node, moduleReferenceExpression *ast.Node, moduleNotFoundError *diagnostics.Message, ignoreErrors bool, isForAugmentation bool) *ast.Symbol {
	if ast.IsStringLiteralLike(moduleReferenceExpression) {
		return c.resolveExternalModule(location, moduleReferenceExpression.Text(), moduleNotFoundError, core.IfElse(!ignoreErrors, moduleReferenceExpression, nil), isForAugmentation)
	}
	return nil
}

func (c *Checker) getExternalModuleFileFromDeclaration(declaration *ast.Node) *ast.SourceFile {
	var specifier *ast.Node
	if declaration.Kind == ast.KindModuleDeclaration {
		if ast.IsStringLiteral(declaration.Name()) {
			specifier = declaration.Name()
		}
	} else {
		specifier = ast.GetExternalModuleName(declaration)
	}
	moduleSymbol := c.resolveExternalModuleNameWorker(specifier, specifier /*moduleNotFoundError*/, nil, false, false) // TODO: GH#18217
	if moduleSymbol == nil {
		return nil
	}
	decl := ast.GetDeclarationOfKind(moduleSymbol, ast.KindSourceFile)
	if decl == nil {
		return nil
	}
	return decl.AsSourceFile()
}

func (c *Checker) resolveExternalModule(location *ast.Node, moduleReference string, moduleNotFoundError *diagnostics.Message, errorNode *ast.Node, isForAugmentation bool) *ast.Symbol {
	if errorNode != nil && strings.HasPrefix(moduleReference, "@types/") {
		withoutAtTypePrefix := moduleReference[len("@types/"):]
		c.error(errorNode, diagnostics.Cannot_import_type_declaration_files_Consider_importing_0_instead_of_1, withoutAtTypePrefix, moduleReference)
	}
	ambientModule := c.tryFindAmbientModule(moduleReference, true /*withAugmentations*/)
	if ambientModule != nil {
		return ambientModule
	}

	importingSourceFile := ast.GetSourceFileOfNode(location)
	var (
		contextSpecifier *ast.Node
		mode             core.ResolutionMode
	)

	if ast.IsStringLiteralLike(location) || location.Parent != nil && ast.IsModuleDeclaration(location.Parent) && location.Parent.AsModuleDeclaration().Name() == location {
		contextSpecifier = location
	} else if ast.IsModuleDeclaration(location) {
		contextSpecifier = location.AsModuleDeclaration().Name()
	} else if ast.IsLiteralImportTypeNode(location) {
		contextSpecifier = location.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal
	} else if ast.IsVariableDeclarationInitializedToBareOrAccessedRequire(location) {
		contextSpecifier = ast.GetModuleSpecifierOfBareOrAccessedRequire(location)
	} else {
		ancestor := ast.FindAncestor(location, ast.IsImportCall)
		if ancestor != nil {
			contextSpecifier = ancestor.Arguments()[0]
		}

		if ancestor == nil {
			ancestor = ast.FindAncestor(location, ast.IsImportDeclarationOrJSImportDeclaration)
			if ancestor != nil {
				contextSpecifier = ancestor.ModuleSpecifier()
			}
		}
		if ancestor == nil {
			ancestor = ast.FindAncestor(location, ast.IsExportDeclaration)
			if ancestor != nil {
				contextSpecifier = ancestor.ModuleSpecifier()
			}
		}
		if ancestor == nil {
			ancestor = ast.FindAncestor(location, ast.IsImportEqualsDeclaration)
			if ancestor != nil {
				if moduleRefrence := ancestor.AsImportEqualsDeclaration().ModuleReference; moduleRefrence.Kind == ast.KindExternalModuleReference {
					contextSpecifier = moduleRefrence.Expression()
				}
			}
		}
	}

	if contextSpecifier != nil && ast.IsStringLiteralLike(contextSpecifier) {
		mode = c.program.GetModeForUsageLocation(importingSourceFile, contextSpecifier)
	} else {
		mode = c.program.GetDefaultResolutionModeForFile(importingSourceFile)
	}

	resolvedModule := c.program.GetResolvedModule(importingSourceFile, moduleReference, mode)

	var resolutionDiagnostic *diagnostics.Message
	if errorNode != nil && resolvedModule.IsResolved() {
		resolutionDiagnostic = module.GetResolutionDiagnostic(c.compilerOptions, resolvedModule, importingSourceFile)
	}

	var sourceFile *ast.SourceFile
	if resolvedModule.IsResolved() && (resolutionDiagnostic == nil || resolutionDiagnostic == diagnostics.Module_0_was_resolved_to_1_but_jsx_is_not_set) {
		sourceFile = c.program.GetSourceFileForResolvedModule(resolvedModule.ResolvedFileName)
	}

	if sourceFile != nil {
		// If there's a resolutionDiagnostic we need to report it even if a sourceFile is found.
		if resolutionDiagnostic != nil {
			c.error(errorNode, resolutionDiagnostic, moduleReference, resolvedModule.ResolvedFileName)
		}

		if errorNode != nil {
			if resolvedModule.ResolvedUsingTsExtension && tspath.IsDeclarationFileName(moduleReference) {
				if ast.FindAncestor(location, ast.IsEmittableImport) != nil {
					tsExtension := tspath.TryExtractTSExtension(moduleReference)
					if tsExtension == "" {
						panic("should be able to extract TS extension from string that passes IsDeclarationFileName")
					}
					c.error(
						errorNode,
						diagnostics.A_declaration_file_cannot_be_imported_without_import_type_Did_you_mean_to_import_an_implementation_file_0_instead,
						c.getSuggestedImportSource(moduleReference, tsExtension, mode),
					)
				}
			} else if resolvedModule.ResolvedUsingTsExtension && !c.compilerOptions.AllowImportingTsExtensionsFrom(importingSourceFile.FileName()) {
				if ast.FindAncestor(location, ast.IsEmittableImport) != nil {
					tsExtension := tspath.TryExtractTSExtension(moduleReference)
					if tsExtension == "" {
						// Fallback: do a best-effort extraction using strings.Contains.
						// This handles cases where a wildcard pattern matches a TS extension that's
						// not at the end of the module specifier, e.g., "#/foo.ts.omg" through "#/*.omg": "./src/*"
						for _, ext := range tspath.SupportedTSExtensionsFlat {
							if strings.Contains(moduleReference, ext) {
								tsExtension = ext
								break
							}
						}
					}
					if tsExtension == "" {
						panic("should be able to extract TS extension from string when resolvedUsingTsExtension is true")
					}
					c.error(
						errorNode,
						diagnostics.An_import_path_can_only_end_with_a_0_extension_when_allowImportingTsExtensions_is_enabled,
						tsExtension,
					)
				}
			} else if c.compilerOptions.RewriteRelativeImportExtensions.IsTrue() &&
				location.Flags&ast.NodeFlagsAmbient == 0 &&
				!tspath.IsDeclarationFileName(moduleReference) &&
				!ast.IsLiteralImportTypeNode(location) &&
				!ast.IsPartOfTypeOnlyImportOrExportDeclaration(location) {
				shouldRewrite := core.ShouldRewriteModuleSpecifier(moduleReference, c.compilerOptions)
				if !resolvedModule.ResolvedUsingTsExtension && shouldRewrite {
					relativeToSourceFile := tspath.GetRelativePathFromFile(
						tspath.GetNormalizedAbsolutePath(importingSourceFile.FileName(), c.program.GetCurrentDirectory()),
						resolvedModule.ResolvedFileName,
						tspath.ComparePathsOptions{
							UseCaseSensitiveFileNames: c.program.UseCaseSensitiveFileNames(),
							CurrentDirectory:          c.program.GetCurrentDirectory(),
						},
					)
					c.error(
						errorNode,
						diagnostics.This_relative_import_path_is_unsafe_to_rewrite_because_it_looks_like_a_file_name_but_actually_resolves_to_0,
						relativeToSourceFile,
					)
				} else if resolvedModule.ResolvedUsingTsExtension && !shouldRewrite && c.program.SourceFileMayBeEmitted(sourceFile, false) {
					c.error(
						errorNode,
						diagnostics.This_import_uses_a_0_extension_to_resolve_to_an_input_TypeScript_file_but_will_not_be_rewritten_during_emit_because_it_is_not_a_relative_path,
						tspath.GetAnyExtensionFromPath(moduleReference, nil, false),
					)
				} else if resolvedModule.ResolvedUsingTsExtension && shouldRewrite {
					if redirect := c.program.GetRedirectForResolution(sourceFile); redirect != nil {
						ownRootDir := c.program.CommonSourceDirectory()
						otherRootDir := redirect.CommonSourceDirectory()

						compareOptions := tspath.ComparePathsOptions{
							UseCaseSensitiveFileNames: c.program.UseCaseSensitiveFileNames(),
							CurrentDirectory:          c.program.GetCurrentDirectory(),
						}

						rootDirPath := tspath.GetRelativePathFromDirectory(ownRootDir, otherRootDir, compareOptions)

						// Get outDir paths, defaulting to root directories if not specified
						ownOutDir := c.compilerOptions.OutDir
						if ownOutDir == "" {
							ownOutDir = ownRootDir
						}
						otherOutDir := redirect.CompilerOptions().OutDir
						if otherOutDir == "" {
							otherOutDir = otherRootDir
						}
						outDirPath := tspath.GetRelativePathFromDirectory(ownOutDir, otherOutDir, compareOptions)

						if rootDirPath != outDirPath {
							c.error(
								errorNode,
								diagnostics.This_import_path_is_unsafe_to_rewrite_because_it_resolves_to_another_project_and_the_relative_path_between_the_projects_output_files_is_not_the_same_as_the_relative_path_between_its_input_files,
							)
						}
					}
				}
			}
		}

		if sourceFile.Symbol != nil {
			if errorNode != nil {
				if resolvedModule.IsExternalLibraryImport && !resolutionExtensionIsTSOrJson(resolvedModule.Extension) {
					c.errorOnImplicitAnyModule(false /*isError*/, errorNode, mode, resolvedModule, moduleReference)
				}
				if c.moduleKind == core.ModuleKindNode16 || c.moduleKind == core.ModuleKindNode18 {
					isSyncImport := c.program.GetDefaultResolutionModeForFile(importingSourceFile) == core.ModuleKindCommonJS && ast.FindAncestor(location, ast.IsImportCall) == nil ||
						ast.FindAncestor(location, ast.IsImportEqualsDeclaration) != nil
					overrideHost := ast.FindAncestor(location, ast.IsResolutionModeOverrideHost)
					if isSyncImport && c.program.GetDefaultResolutionModeForFile(sourceFile) == core.ModuleKindESNext && !ast.HasResolutionModeOverride(overrideHost) {
						if ast.FindAncestorKind(location, ast.KindImportEqualsDeclaration) != nil {
							// ImportEquals in an ESM file resolving to another ESM file
							c.error(errorNode, diagnostics.Module_0_cannot_be_imported_using_this_construct_The_specifier_only_resolves_to_an_ES_module_which_cannot_be_imported_with_require_Use_an_ECMAScript_import_instead, moduleReference)
						} else {
							// CJS file resolving to an ESM file
							var diagnosticDetails *ast.Diagnostic
							ext := tspath.TryGetExtensionFromPath(importingSourceFile.FileName())
							if ext == tspath.ExtensionTs || ext == tspath.ExtensionJs || ext == tspath.ExtensionTsx || ext == tspath.ExtensionJsx {
								diagnosticDetails = c.createModeMismatchDetails(importingSourceFile, errorNode)
							}

							var message *diagnostics.Message
							if overrideHost != nil && overrideHost.Kind == ast.KindImportDeclaration && overrideHost.ImportClause() != nil && overrideHost.ImportClause().IsTypeOnly() {
								message = diagnostics.Type_only_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute
							} else if overrideHost != nil && overrideHost.Kind == ast.KindImportType {
								message = diagnostics.Type_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute
							} else {
								message = diagnostics.The_current_file_is_a_CommonJS_module_whose_imports_will_produce_require_calls_however_the_referenced_file_is_an_ECMAScript_module_and_cannot_be_imported_with_require_Consider_writing_a_dynamic_import_0_call_instead
							}

							c.addDiagnostic(NewDiagnosticChainForNode(diagnosticDetails, errorNode, message, moduleReference))
						}
					}
				}
			}
			return c.getMergedSymbol(sourceFile.Symbol)
		}
		if errorNode != nil && moduleNotFoundError != nil && !isSideEffectImport(errorNode) {
			c.error(errorNode, diagnostics.File_0_is_not_a_module, resolvedModule.ResolvedFileName)
		}
		return nil
	}

	if len(c.patternAmbientModules) != 0 {
		pattern := core.FindBestPatternMatch(c.patternAmbientModules, func(v *ast.PatternAmbientModule) core.Pattern { return v.Pattern }, moduleReference)
		if pattern != nil {
			augmentation := c.patternAmbientModuleAugmentations[moduleReference]
			if augmentation != nil {
				return c.getMergedSymbol(augmentation)
			}
			return c.getMergedSymbol(pattern.Symbol)
		}
	}

	if errorNode == nil {
		return nil
	}

	if resolvedModule.IsResolved() && !resolutionExtensionIsTSOrJson(resolvedModule.Extension) && resolutionDiagnostic == nil || resolutionDiagnostic == diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type {
		if isForAugmentation {
			c.error(
				errorNode,
				diagnostics.Invalid_module_name_in_augmentation_Module_0_resolves_to_an_untyped_module_at_1_which_cannot_be_augmented,
				moduleReference,
				resolvedModule.ResolvedFileName,
			)
		} else {
			c.errorOnImplicitAnyModule(c.noImplicitAny && moduleNotFoundError != nil, errorNode, mode, resolvedModule, moduleReference)
		}
		return nil
	}

	if moduleNotFoundError != nil {
		// See if this was possibly a projectReference redirect
		if resolvedModule.IsResolved() {
			redirect := c.program.GetProjectReferenceFromSource(tspath.ToPath(resolvedModule.ResolvedFileName, c.program.GetCurrentDirectory(), c.program.UseCaseSensitiveFileNames()))
			if redirect != nil && redirect.OutputDts != "" {
				c.error(
					errorNode,
					diagnostics.Output_file_0_has_not_been_built_from_source_file_1,
					redirect.OutputDts,
					resolvedModule.ResolvedFileName,
				)
				return nil
			}
		}

		if resolutionDiagnostic != nil {
			c.error(errorNode, resolutionDiagnostic, moduleReference, resolvedModule.ResolvedFileName)
		} else {
			isExtensionlessRelativePathImport := tspath.PathIsRelative(moduleReference) && !tspath.HasExtension(moduleReference)
			resolutionIsNode16OrNext := c.moduleResolutionKind == core.ModuleResolutionKindNode16 || c.moduleResolutionKind == core.ModuleResolutionKindNodeNext
			if !c.compilerOptions.GetResolveJsonModule() && tspath.FileExtensionIs(moduleReference, tspath.ExtensionJson) {
				c.error(errorNode, diagnostics.Cannot_find_module_0_Consider_using_resolveJsonModule_to_import_module_with_json_extension, moduleReference)
			} else if mode == core.ResolutionModeESM && resolutionIsNode16OrNext && isExtensionlessRelativePathImport {
				absoluteRef := tspath.GetNormalizedAbsolutePath(moduleReference, tspath.GetDirectoryPath(importingSourceFile.FileName()))
				if suggestedExt := c.getSuggestedImportExtension(absoluteRef); suggestedExt != "" {
					c.error(errorNode, diagnostics.Relative_import_paths_need_explicit_file_extensions_in_ECMAScript_imports_when_moduleResolution_is_node16_or_nodenext_Did_you_mean_0, moduleReference+suggestedExt)
				} else {
					c.error(errorNode, diagnostics.Relative_import_paths_need_explicit_file_extensions_in_ECMAScript_imports_when_moduleResolution_is_node16_or_nodenext_Consider_adding_an_extension_to_the_import_path)
				}
			} else if resolvedModule != nil && resolvedModule.AlternateResult != "" {
				errorInfo := c.createModuleNotFoundChain(resolvedModule, errorNode, moduleReference, mode, moduleReference)
				c.addDiagnostic(NewDiagnosticChainForNode(errorInfo, errorNode, moduleNotFoundError, moduleReference))
			} else {
				c.error(errorNode, moduleNotFoundError, moduleReference)
			}
		}
	}

	return nil
}

func resolutionExtensionIsTSOrJson(ext string) bool {
	return tspath.ExtensionIsTs(ext) || ext == tspath.ExtensionJson
}

func (c *Checker) getSuggestedImportSource(moduleReference string, tsExtension string, mode core.ResolutionMode) string {
	importSourceWithoutExtension := tspath.RemoveExtension(moduleReference, tsExtension)

	// Direct users to import source with .js extension if outputting an ES module.
	// @see https://github.com/microsoft/TypeScript/issues/42151
	if c.moduleKind.IsNonNodeESM() || mode == core.ModuleKindESNext {
		preferTs := tspath.IsDeclarationFileName(moduleReference) && c.compilerOptions.GetAllowImportingTsExtensions()
		var ext string
		switch {
		case tsExtension == tspath.ExtensionMts || tsExtension == tspath.ExtensionDmts:
			ext = core.IfElse(preferTs, ".mts", ".mjs")
		case tsExtension == tspath.ExtensionCts || tsExtension == tspath.ExtensionDcts:
			ext = core.IfElse(preferTs, ".cts", ".cjs")
		default:
			ext = core.IfElse(preferTs, ".ts", ".js")
		}

		return importSourceWithoutExtension + ext
	}

	return importSourceWithoutExtension
}

func (c *Checker) getSuggestedImportExtension(extensionlessImportPath string) string {
	switch true {
	case c.program.FileExists(extensionlessImportPath + ".mts"):
		return ".mjs"
	case c.program.FileExists(extensionlessImportPath + ".ts"):
		return ".js"
	case c.program.FileExists(extensionlessImportPath + ".cts"):
		return ".cjs"
	case c.program.FileExists(extensionlessImportPath + ".mjs"):
		return ".mjs"
	case c.program.FileExists(extensionlessImportPath + ".js"):
		return ".js"
	case c.program.FileExists(extensionlessImportPath + ".cjs"):
		return ".cjs"
	case c.program.FileExists(extensionlessImportPath + ".tsx"):
		return core.IfElse(c.compilerOptions.Jsx == core.JsxEmitPreserve, ".jsx", ".js")
	case c.program.FileExists(extensionlessImportPath + ".jsx"):
		return ".jsx"
	case c.program.FileExists(extensionlessImportPath + ".json"):
		return ".json"
	}
	return ""
}

func (c *Checker) errorOnImplicitAnyModule(isError bool, errorNode *ast.Node, mode core.ResolutionMode, resolvedModule *module.ResolvedModule, moduleReference string) {
	if isSideEffectImport(errorNode) {
		return
	}

	var errorInfo *ast.Diagnostic
	if !tspath.IsExternalModuleNameRelative(moduleReference) && resolvedModule.PackageId.Name != "" {
		errorInfo = c.createModuleNotFoundChain(resolvedModule, errorNode, moduleReference, mode, resolvedModule.PackageId.Name)
	}
	c.addErrorOrSuggestion(
		isError,
		NewDiagnosticChainForNode(
			errorInfo,
			errorNode,
			diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type,
			moduleReference,
			resolvedModule.ResolvedFileName,
		),
	)
}

func (c *Checker) createModuleNotFoundChain(resolvedModule *module.ResolvedModule, errorNode *ast.Node, moduleReference string, mode core.ResolutionMode, packageName string) *ast.Diagnostic {
	// Store the original packageName for repopulateInfo before any modifications
	storedPackageName := packageName
	if storedPackageName == moduleReference {
		storedPackageName = ""
	}

	details := CreateModuleNotFoundChain(c.program, ast.GetSourceFileOfNode(errorNode), moduleReference, mode, packageName)
	result := NewDiagnosticForNode(errorNode, details.Message, details.Args...)
	result.SetRepopulateInfo(&ast.RepopulateDiagnosticInfo{
		Kind:            ast.RepopulateModuleNotFound,
		ModuleReference: moduleReference,
		Mode:            mode,
		PackageName:     storedPackageName,
	})
	return result
}

func (c *Checker) createModeMismatchDetails(sourceFile *ast.SourceFile, errorNode *ast.Node) *ast.Diagnostic {
	details := CreateModeMismatchDetails(c.program, sourceFile)
	result := NewDiagnosticForNode(errorNode, details.Message, details.Args...)
	result.SetRepopulateInfo(&ast.RepopulateDiagnosticInfo{
		Kind: ast.RepopulateModeMismatch,
	})
	return result
}

func (c *Checker) tryFindAmbientModule(moduleName string, withAugmentations bool) *ast.Symbol {
	if tspath.IsExternalModuleNameRelative(moduleName) {
		return nil
	}
	symbol := c.getSymbol(c.globals, "\""+moduleName+"\"", ast.SymbolFlagsValueModule)
	// merged symbol is module declaration symbol combined with all augmentations
	if withAugmentations {
		return c.getMergedSymbol(symbol)
	}
	return symbol
}

func (c *Checker) GetAmbientModules() []*ast.Symbol {
	c.ambientModulesOnce.Do(func() {
		for sym, global := range c.globals {
			if strings.HasPrefix(sym, "\"") && strings.HasSuffix(sym, "\"") {
				c.ambientModules = append(c.ambientModules, global)
			}
		}
	})
	return c.ambientModules
}

func (c *Checker) resolveExternalModuleSymbol(moduleSymbol *ast.Symbol, dontResolveAlias bool) *ast.Symbol {
	if moduleSymbol != nil {
		exportEquals := c.resolveSymbolEx(moduleSymbol.Exports[ast.InternalSymbolNameExportEquals], dontResolveAlias)
		if exportEquals != nil {
			return c.getMergedSymbol(exportEquals)
		}
	}
	return moduleSymbol
}

// Resolves the given external module symbol, possibly removing call and construct signatures or creating a
// wrapper module with a synthetic default.
func (c *Checker) resolveESModuleSymbol(moduleSymbol *ast.Symbol, node *ast.Node, moduleSpecifier *ast.Node) *ast.Symbol {
	symbol := c.resolveExternalModuleSymbol(moduleSymbol, true /*dontResolveAlias*/)
	if ast.IsNonLocalAlias(symbol, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace) {
		// When the module has an export= with a pure alias, we transitively resolve and propagate any typeOnlyDeclaration
		symbol = c.getMergedSymbol(c.resolveIndirectionAlias(c.getSymbolOfDeclaration(node), symbol))
	}
	if symbol != nil {
		referenceParent := moduleSpecifier.Parent
		var namespaceImport *ast.Node
		if ast.IsImportDeclaration(referenceParent) {
			namespaceImport = ast.GetNamespaceDeclarationNode(referenceParent)
		}
		if namespaceImport != nil || ast.IsImportCall(referenceParent) {
			var reference *ast.Node
			if ast.IsImportCall(referenceParent) {
				reference = referenceParent.Arguments()[0]
			} else {
				reference = referenceParent.ModuleSpecifier()
			}
			typ := c.getTypeOfSymbol(symbol)
			defaultOnlyType := c.getTypeWithSyntheticDefaultOnly(typ, symbol, moduleSymbol, reference)
			if defaultOnlyType != nil {
				return c.cloneTypeAsModuleType(symbol, defaultOnlyType, referenceParent)
			}

			targetFile := core.Find(moduleSymbol.Declarations, ast.IsSourceFile)
			usageMode := c.getEmitSyntaxForModuleSpecifierExpression(reference)
			var exportModuleDotExportsSymbol *ast.Symbol
			if namespaceImport != nil && targetFile != nil &&
				core.ModuleKindNode20 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext &&
				usageMode == core.ModuleKindCommonJS &&
				c.program.GetImpliedNodeFormatForEmit(targetFile.AsSourceFile()) == core.ModuleKindESNext {
				exportModuleDotExportsSymbol = c.getExportOfModule(symbol, ast.InternalSymbolNameModuleExports, namespaceImport, true /*dontResolveAlias*/)
			}
			if exportModuleDotExportsSymbol != nil {
				if c.hasSignatures(typ) {
					return c.cloneTypeAsModuleType(exportModuleDotExportsSymbol, typ, referenceParent)
				}
				return exportModuleDotExportsSymbol
			}

			isEsmCjsRef := targetFile != nil && isESMFormatImportImportingCommonjsFormatFile(usageMode, c.program.GetImpliedNodeFormatForEmit(targetFile.AsSourceFile()))
			if c.hasSignatures(typ) || c.getPropertyOfTypeEx(typ, ast.InternalSymbolNameDefault, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/) != nil || isEsmCjsRef {
				var moduleType *Type
				if typ.Flags()&TypeFlagsStructuredType != 0 {
					moduleType = c.getTypeWithSyntheticDefaultImportType(typ, symbol, moduleSymbol, reference)
				} else {
					moduleType = c.createDefaultPropertyWrapperForModule(symbol, symbol.Parent, nil)
				}
				return c.cloneTypeAsModuleType(symbol, moduleType, referenceParent)
			}
		}
	}
	return symbol
}

func (c *Checker) hasSignatures(t *Type) bool {
	return len(c.getSignaturesOfStructuredType(t, SignatureKindCall)) > 0 || len(c.getSignaturesOfStructuredType(t, SignatureKindConstruct)) > 0
}

func isESMFormatImportImportingCommonjsFormatFile(usageMode core.ResolutionMode, targetMode core.ResolutionMode) bool {
	return usageMode == core.ModuleKindESNext && targetMode == core.ModuleKindCommonJS
}

func (c *Checker) getTypeWithSyntheticDefaultOnly(t *Type, symbol *ast.Symbol, originalSymbol *ast.Symbol, moduleSpecifier *ast.Node) *Type {
	hasDefaultOnly := c.isOnlyImportableAsDefault(moduleSpecifier, nil)
	if hasDefaultOnly && t != nil && !c.isErrorType(t) {
		key := CachedTypeKey{kind: CachedTypeKindDefaultOnlyType, typeId: t.id}
		if cached := c.cachedTypes[key]; cached != nil {
			return cached
		}
		result := c.createDefaultPropertyWrapperForModule(symbol, originalSymbol, nil)
		c.cachedTypes[key] = result
		return result
	}
	return nil
}

func (c *Checker) getTypeWithSyntheticDefaultImportType(t *Type, symbol *ast.Symbol, originalSymbol *ast.Symbol, moduleSpecifier *ast.Node) *Type {
	if t != nil && !c.isErrorType(t) {
		key := CachedTypeKey{kind: CachedTypeKindSyntheticType, typeId: t.id}
		if cached := c.cachedTypes[key]; cached != nil {
			return cached
		}
		file := core.Find(originalSymbol.Declarations, ast.IsSourceFile)
		hasSyntheticDefault := c.canHaveSyntheticDefault(file, originalSymbol, false /*dontResolveAlias*/, moduleSpecifier)
		var syntheticType *Type
		if hasSyntheticDefault {
			anonymousSymbol := c.newSymbol(ast.SymbolFlagsTypeLiteral, ast.InternalSymbolNameType)
			defaultContainingObject := c.createDefaultPropertyWrapperForModule(symbol, originalSymbol, anonymousSymbol)
			c.valueSymbolLinks.Get(anonymousSymbol).resolvedType = defaultContainingObject
			if c.isValidSpreadType(t) {
				syntheticType = c.getSpreadType(t, defaultContainingObject, anonymousSymbol, 0 /*objectFlags*/, false /*readonly*/)
			} else {
				syntheticType = defaultContainingObject
			}
		} else {
			syntheticType = t
		}
		c.cachedTypes[key] = syntheticType
		return syntheticType
	}
	return t
}

func (c *Checker) isCommonJSRequire(node *ast.Node) bool {
	if !ast.IsRequireCall(node, true /*requireStringLiteralLikeArgument*/) {
		return false
	}
	if !ast.IsIdentifier(node.Expression()) {
		panic("Expected identifier for require call")
	}
	// Make sure require is not a local function
	resolvedRequire := c.resolveName(node.Expression(), node.Expression().Text(), ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/)
	if resolvedRequire == c.requireSymbol {
		return true
	}
	// project includes symbol named 'require' - make sure that it is ambient and local non-alias
	if resolvedRequire == nil || resolvedRequire.Flags&ast.SymbolFlagsAlias != 0 {
		return false
	}

	var targetDeclarationKind ast.Kind
	if resolvedRequire.Flags&ast.SymbolFlagsFunction != 0 {
		targetDeclarationKind = ast.KindFunctionDeclaration
	} else if resolvedRequire.Flags&ast.SymbolFlagsVariable != 0 {
		targetDeclarationKind = ast.KindVariableDeclaration
	} else {
		targetDeclarationKind = ast.KindUnknown
	}
	if targetDeclarationKind != ast.KindUnknown {
		decl := ast.GetDeclarationOfKind(resolvedRequire, targetDeclarationKind)
		// function/variable declaration should be ambient
		return decl != nil && decl.Flags&ast.NodeFlagsAmbient != 0
	}
	return false
}

func (c *Checker) createDefaultPropertyWrapperForModule(symbol *ast.Symbol, originalSymbol *ast.Symbol, anonymousSymbol *ast.Symbol) *Type {
	memberTable := make(ast.SymbolTable)
	newSymbol := c.newSymbol(ast.SymbolFlagsAlias, ast.InternalSymbolNameDefault)
	newSymbol.Parent = originalSymbol
	c.valueSymbolLinks.Get(newSymbol).nameType = c.getStringLiteralType("default")
	c.aliasSymbolLinks.Get(newSymbol).aliasTarget = c.resolveSymbol(symbol)
	memberTable[ast.InternalSymbolNameDefault] = newSymbol
	return c.newAnonymousType(anonymousSymbol, memberTable, nil, nil, nil)
}

func (c *Checker) cloneTypeAsModuleType(symbol *ast.Symbol, moduleType *Type, referenceParent *ast.Node) *ast.Symbol {
	result := c.newSymbol(symbol.Flags, symbol.Name)
	result.Declarations = slices.Clone(symbol.Declarations)
	result.ValueDeclaration = symbol.ValueDeclaration
	result.Members = maps.Clone(symbol.Members)
	result.Exports = maps.Clone(symbol.Exports)
	result.Parent = symbol.Parent
	links := c.exportTypeLinks.Get(result)
	links.target = symbol
	links.originatingImport = referenceParent
	resolvedModuleType := c.resolveStructuredTypeMembers(moduleType)
	c.valueSymbolLinks.Get(result).resolvedType = c.newAnonymousType(result, resolvedModuleType.members, nil, nil, resolvedModuleType.indexInfos)
	return result
}

func (c *Checker) getTargetOfAliasDeclaration(node *ast.Node) *ast.Symbol {
	if node == nil {
		return nil
	}
	switch node.Kind {
	case ast.KindImportEqualsDeclaration, ast.KindVariableDeclaration:
		return c.getTargetOfImportEqualsDeclaration(node)
	case ast.KindImportClause:
		return c.getTargetOfImportClause(node)
	case ast.KindNamespaceImport:
		return c.getTargetOfNamespaceImport(node)
	case ast.KindNamespaceExport:
		return c.getTargetOfNamespaceExport(node)
	case ast.KindImportSpecifier, ast.KindBindingElement:
		return c.getTargetOfImportSpecifier(node)
	case ast.KindExportSpecifier:
		return c.getTargetOfExportSpecifier(node, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, true /*dontRecursivelyResolve*/)
	case ast.KindExportAssignment:
		return c.getTargetOfExportAssignment(node)
	case ast.KindBinaryExpression:
		return c.getTargetOfBinaryExpression(node)
	case ast.KindNamespaceExportDeclaration:
		return c.getTargetOfNamespaceExportDeclaration(node)
	case ast.KindShorthandPropertyAssignment:
		return c.resolveEntityName(node.AsShorthandPropertyAssignment().Name(), ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, true /*ignoreErrors*/, true /*dontRecursivelyResolve*/, nil /*location*/)
	case ast.KindPropertyAssignment:
		return c.getTargetOfAliasLikeExpression(node.Initializer())
	case ast.KindElementAccessExpression, ast.KindPropertyAccessExpression:
		return c.getTargetOfAccessExpression(node)
	}
	panic("Unhandled case in getTargetOfAliasDeclaration: " + node.Kind.String())
}

/**
 * Resolves a qualified name and any involved aliases.
 */
func (c *Checker) resolveEntityName(name *ast.Node, meaning ast.SymbolFlags, ignoreErrors bool, dontResolveAlias bool, location *ast.Node) *ast.Symbol {
	if ast.NodeIsMissing(name) {
		return nil
	}
	var symbol *ast.Symbol
	switch name.Kind {
	case ast.KindIdentifier:
		var message *diagnostics.Message
		if !ignoreErrors {
			if meaning == ast.SymbolFlagsNamespace || ast.NodeIsSynthesized(name) {
				message = diagnostics.Cannot_find_namespace_0
			} else {
				message = c.getCannotFindNameDiagnosticForName(ast.GetFirstIdentifier(name))
			}
		}
		resolveLocation := location
		if resolveLocation == nil {
			resolveLocation = name
		}
		if meaning == ast.SymbolFlagsNamespace {
			symbol = c.getMergedSymbol(c.resolveName(resolveLocation, name.Text(), meaning, nil, true /*isUse*/, false /*excludeGlobals*/))
			if symbol == nil {
				alias := c.getMergedSymbol(c.resolveName(resolveLocation, name.Text(), ast.SymbolFlagsAlias, nil, true /*isUse*/, false /*excludeGlobals*/))
				if alias != nil && alias.Name == ast.InternalSymbolNameExportEquals {
					// resolve typedefs exported from commonjs, stored on the module symbol
					symbol = alias.Parent
				}
			}
			if symbol == nil && message != nil {
				c.resolveName(resolveLocation, name.Text(), meaning, message, true /*isUse*/, false /*excludeGlobals*/)
			}
		} else {
			symbol = c.getMergedSymbol(c.resolveName(resolveLocation, name.Text(), meaning, message, true /*isUse*/, false /*excludeGlobals*/))
		}
	case ast.KindQualifiedName:
		qualified := name.AsQualifiedName()
		symbol = c.resolveQualifiedName(name, qualified.Left, qualified.Right, meaning, ignoreErrors, location)
	case ast.KindPropertyAccessExpression:
		access := name.AsPropertyAccessExpression()
		symbol = c.resolveQualifiedName(name, access.Expression, access.Name(), meaning, ignoreErrors, location)
	default:
		panic("Unknown entity name kind")
	}
	if symbol != nil && symbol != c.unknownSymbol {
		if !ast.NodeIsSynthesized(name) && ast.IsEntityName(name) &&
			(symbol.Flags&ast.SymbolFlagsAlias != 0 || name.Parent != nil && name.Parent.Kind == ast.KindExportAssignment) {
			c.markSymbolOfAliasDeclarationIfTypeOnly(getAliasDeclarationFromName(name), nil)
		}
		// We know a symbol with the given meaning exists along the alias chain, so resolve until we find it.
		for symbol.Flags&meaning == 0 && !dontResolveAlias && symbol.Flags&ast.SymbolFlagsAlias != 0 {
			symbol = c.resolveAlias(symbol)
		}
	}
	return symbol
}

func (c *Checker) resolveQualifiedName(name *ast.Node, left *ast.Node, right *ast.Node, meaning ast.SymbolFlags, ignoreErrors bool, location *ast.Node) *ast.Symbol {
	namespace := c.resolveEntityName(left, ast.SymbolFlagsNamespace, ignoreErrors, false /*dontResolveAlias*/, location)
	if namespace == nil || ast.NodeIsMissing(right) {
		return nil
	}
	if namespace == c.unknownSymbol {
		return namespace
	}
	if namespace.ValueDeclaration != nil &&
		ast.IsInJSFile(namespace.ValueDeclaration) &&
		c.compilerOptions.GetModuleResolutionKind() != core.ModuleResolutionKindBundler &&
		ast.IsVariableDeclaration(namespace.ValueDeclaration) &&
		namespace.ValueDeclaration.Initializer() != nil &&
		c.isCommonJSRequire(namespace.ValueDeclaration.Initializer()) {
		moduleName := namespace.ValueDeclaration.Initializer().Arguments()[0]
		moduleSym := c.resolveExternalModuleName(moduleName, moduleName, false /*ignoreErrors*/)
		if moduleSym != nil {
			resolvedModuleSymbol := c.resolveExternalModuleSymbol(moduleSym, false /*dontResolveAlias*/)
			if resolvedModuleSymbol != nil {
				namespace = resolvedModuleSymbol
			}
		}
	}
	text := right.Text()
	symbol := c.getMergedSymbol(c.getSymbol(c.getExportsOfSymbol(namespace), text, meaning))
	if symbol == nil && namespace.Flags&ast.SymbolFlagsAlias != 0 {
		// `namespace` can be resolved further if there was a symbol merge with a re-export
		symbol = c.getMergedSymbol(c.getSymbol(c.getExportsOfSymbol(c.resolveAlias(namespace)), text, meaning))
	}
	if symbol == nil {
		if !ignoreErrors {
			namespaceName := c.getFullyQualifiedName(namespace, nil /*containingLocation*/)
			declarationName := scanner.DeclarationNameToString(right)
			suggestionForNonexistentModule := c.getSuggestedSymbolForNonexistentModule(right, namespace)
			if suggestionForNonexistentModule != nil {
				c.error(right, diagnostics.X_0_has_no_exported_member_named_1_Did_you_mean_2, namespaceName, declarationName, c.symbolToString(suggestionForNonexistentModule))
				return nil
			}
			var containingQualifiedName *ast.Node
			if ast.IsQualifiedName(name) {
				containingQualifiedName = getContainingQualifiedNameNode(name)
			}
			canSuggestTypeof := c.globalObjectType != nil && meaning&ast.SymbolFlagsType != 0 && containingQualifiedName != nil && !ast.IsTypeOfExpression(containingQualifiedName.Parent) && c.tryGetQualifiedNameAsValue(containingQualifiedName) != nil
			if canSuggestTypeof {
				c.error(containingQualifiedName, diagnostics.X_0_refers_to_a_value_but_is_being_used_as_a_type_here_Did_you_mean_typeof_0, entityNameToString(containingQualifiedName))
				return nil
			}
			if meaning&ast.SymbolFlagsNamespace != 0 {
				if ast.IsQualifiedName(name.Parent) {
					exportedTypeSymbol := c.getMergedSymbol(c.getSymbol(c.getExportsOfSymbol(namespace), text, ast.SymbolFlagsType))
					if exportedTypeSymbol != nil {
						qualified := name.Parent.AsQualifiedName()
						c.error(qualified.Right, diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1, c.symbolToString(exportedTypeSymbol), qualified.Right.Text())
						return nil
					}
				}
			}
			c.error(right, diagnostics.Namespace_0_has_no_exported_member_1, namespaceName, declarationName)
		}
	}
	return symbol
}

func (c *Checker) tryGetQualifiedNameAsValue(node *ast.Node) *ast.Symbol {
	id := ast.GetFirstIdentifier(node)
	symbol := c.resolveName(id, id.Text(), ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/)
	if symbol == nil {
		return nil
	}
	n := id
	for ast.IsQualifiedName(n.Parent) {
		t := c.getTypeOfSymbol(symbol)
		symbol = c.getPropertyOfType(t, n.Parent.AsQualifiedName().Right.Text())
		if symbol == nil {
			return nil
		}
		n = n.Parent
	}
	return symbol
}

func (c *Checker) getSuggestedSymbolForNonexistentModule(name *ast.Node, targetModule *ast.Symbol) *ast.Symbol {
	return c.getSpellingSuggestionForName(name.Text(), maps.Values(c.getExportsOfModule(targetModule)), ast.SymbolFlagsModuleMember)
}

func (c *Checker) getFullyQualifiedName(symbol *ast.Symbol, containingLocation *ast.Node) string {
	if symbol.Parent != nil {
		return c.getFullyQualifiedName(symbol.Parent, containingLocation) + "." + c.symbolToString(symbol)
	}
	return c.symbolToStringEx(symbol, containingLocation, ast.SymbolFlagsAll, SymbolFormatFlagsDoNotIncludeSymbolChain|SymbolFormatFlagsAllowAnyNodeKind)
}

func (c *Checker) getExportsOfSymbol(symbol *ast.Symbol) ast.SymbolTable {
	if symbol.Flags&ast.SymbolFlagsLateBindingContainer != 0 {
		return c.getResolvedMembersOrExportsOfSymbol(symbol, MembersOrExportsResolutionKindResolvedExports)
	}
	if symbol.Flags&ast.SymbolFlagsModule != 0 {
		return c.getExportsOfModule(symbol)
	}
	return symbol.Exports
}

func (c *Checker) getResolvedMembersOrExportsOfSymbol(symbol *ast.Symbol, resolutionKind MembersOrExportsResolutionKind) ast.SymbolTable {
	links := c.membersAndExportsLinks.Get(symbol)
	if links[resolutionKind] == nil {
		isStatic := resolutionKind == MembersOrExportsResolutionKindResolvedExports
		earlySymbols := symbol.Exports
		switch {
		case !isStatic:
			earlySymbols = symbol.Members
		case symbol.Flags&ast.SymbolFlagsModule != 0:
			earlySymbols, _ = c.getExportsOfModuleWorker(symbol)
		}
		links[resolutionKind] = earlySymbols
		// fill in any as-yet-unresolved late-bound members.
		var lateSymbols ast.SymbolTable
		for _, decl := range symbol.Declarations {
			for _, member := range getMembersOfDeclaration(decl) {
				if isStatic == ast.HasStaticModifier(member) {
					switch {
					case c.hasLateBindableName(member):
						if lateSymbols == nil {
							lateSymbols = make(ast.SymbolTable)
						}
						c.lateBindMember(symbol, earlySymbols, lateSymbols, member)
					case c.hasLateBindableIndexSignature(member):
						if lateSymbols == nil {
							lateSymbols = make(ast.SymbolTable)
						}
						c.lateBindIndexSignature(symbol, earlySymbols, lateSymbols, member.AsNode() /* as LateBoundDeclaration | LateBoundBinaryExpressionDeclaration */)
					}
				}
			}
		}
		if isStatic {
			if assignmentSymbol := symbol.Exports[ast.InternalSymbolNameAssignmentDeclaration]; assignmentSymbol != nil {
				for _, member := range assignmentSymbol.Declarations {
					if c.hasLateBindableName(member) {
						if lateSymbols == nil {
							lateSymbols = make(ast.SymbolTable)
						}
						c.lateBindMember(symbol, earlySymbols, lateSymbols, member)
					}
				}
			}
		}
		links[resolutionKind] = c.combineSymbolTables(earlySymbols, lateSymbols)
	}
	return links[resolutionKind]
}

// Performs late-binding of a dynamic member. This performs the same function for
// late-bound members that `declareSymbol` in binder.ts performs for early-bound
// members.
//
// If a symbol is a dynamic name from a computed property, we perform an additional "late"
// binding phase to attempt to resolve the name for the symbol from the type of the computed
// property's expression. If the type of the expression is a string-literal, numeric-literal,
// or unique symbol type, we can use that type as the name of the symbol.
//
// For example, given:
//
//	const x = Symbol();
//
//	interface I {
//	  [x]: number;
//	}
//
// The binder gives the property `[x]: number` a special symbol with the name "__computed".
// In the late-binding phase we can type-check the expression `x` and see that it has a
// unique symbol type which we can then use as the name of the member. This allows users
// to define custom symbols that can be used in the members of an object type.
//
// @param parent The containing symbol for the member.
// @param earlySymbols The early-bound symbols of the parent.
// @param lateSymbols The late-bound symbols of the parent.
// @param decl The member to bind.
func (c *Checker) lateBindMember(parent *ast.Symbol, earlySymbols ast.SymbolTable, lateSymbols ast.SymbolTable, decl *ast.Node) *ast.Symbol {
	debug.Assert(decl.Symbol() != nil, "The member is expected to have a symbol.")
	links := c.symbolNodeLinks.Get(decl)
	if links.resolvedSymbol == nil {
		// In the event we attempt to resolve the late-bound name of this member recursively,
		// fall back to the early-bound name of this member.
		links.resolvedSymbol = decl.Symbol()
		var declName *ast.Node
		if ast.IsBinaryExpression(decl) {
			declName = decl.AsBinaryExpression().Left
		} else {
			declName = decl.Name()
		}
		var t *Type
		if ast.IsElementAccessExpression(declName) {
			t = c.checkExpressionCached(declName.AsElementAccessExpression().ArgumentExpression)
		} else {
			t = c.checkComputedPropertyName(declName)
		}
		if isTypeUsableAsPropertyName(t) {
			memberName := getPropertyNameFromType(t)
			symbolFlags := decl.Symbol().Flags
			// Get or add a late-bound symbol for the member. This allows us to merge late-bound accessor declarations.
			lateSymbol := lateSymbols[memberName]
			if lateSymbol == nil {
				lateSymbol = c.newSymbolEx(ast.SymbolFlagsNone, memberName, ast.CheckFlagsLate)
				lateSymbols[memberName] = lateSymbol
			}
			// Report an error if there's a symbol declaration with the same name and conflicting flags.
			earlySymbol := earlySymbols[memberName]
			if lateSymbol.Flags&getExcludedSymbolFlags(symbolFlags) != 0 {
				// If we have an existing early-bound member, combine its declarations so that we can
				// report an error at each declaration.
				var declarations []*ast.Node
				if earlySymbol != nil {
					declarations = core.Concatenate(earlySymbol.Declarations, lateSymbol.Declarations)
				} else {
					declarations = lateSymbol.Declarations
				}
				name := memberName
				if t.flags&TypeFlagsUniqueESSymbol != 0 {
					name = scanner.DeclarationNameToString(declName)
				}
				for _, d := range declarations {
					c.error(core.OrElse(ast.GetNameOfDeclaration(d), d), diagnostics.Duplicate_identifier_0, name)
				}
				c.error(core.OrElse(declName, decl), diagnostics.Duplicate_identifier_0, name)
				if lateSymbol.Flags&ast.SymbolFlagsAccessor != 0 && lateSymbol.Flags&ast.SymbolFlagsAccessor != symbolFlags&ast.SymbolFlagsAccessor {
					lateSymbol.Flags |= ast.SymbolFlagsAccessor
				}
				lateSymbol = c.newSymbolEx(ast.SymbolFlagsNone, memberName, ast.CheckFlagsLate)
			}
			c.valueSymbolLinks.Get(lateSymbol).nameType = t
			c.addDeclarationToLateBoundSymbol(lateSymbol, decl, symbolFlags)
			if lateSymbol.Parent == nil {
				lateSymbol.Parent = parent
			}
			links.resolvedSymbol = lateSymbol
		}
	}
	return links.resolvedSymbol
}

func (c *Checker) lateBindIndexSignature(parent *ast.Symbol, earlySymbols ast.SymbolTable, lateSymbols ast.SymbolTable, decl *ast.Node) {
	// First, late bind the index symbol itself, if needed
	indexSymbol := lateSymbols[ast.InternalSymbolNameIndex]
	if indexSymbol == nil {
		early := earlySymbols[ast.InternalSymbolNameIndex]
		if early == nil {
			indexSymbol = c.newSymbolEx(ast.SymbolFlagsNone, ast.InternalSymbolNameIndex, ast.CheckFlagsLate)
		} else {
			indexSymbol = c.cloneSymbol(early)
			indexSymbol.CheckFlags |= ast.CheckFlagsLate
		}
		lateSymbols[ast.InternalSymbolNameIndex] = indexSymbol
	}
	// Then just add the computed name as a late bound declaration
	// (note: unlike `addDeclarationToLateBoundSymbol` we do not set up a `.lateSymbol` on `decl`'s links,
	// since that would point at an index symbol and not a single property symbol, like most consumers would expect)
	if len(indexSymbol.Declarations) == 0 || decl.Symbol().Flags&ast.SymbolFlagsReplaceableByMethod == 0 {
		indexSymbol.Declarations = append(indexSymbol.Declarations, decl)
	}
}

func isNotReplacableByMethod(decl *ast.Node) bool {
	return decl.Symbol().Flags&ast.SymbolFlagsReplaceableByMethod == 0
}

// Adds a declaration to a late-bound dynamic member. This performs the same function for
// late-bound members that `addDeclarationToSymbol` in binder.ts performs for early-bound
// members.
func (c *Checker) addDeclarationToLateBoundSymbol(symbol *ast.Symbol, member *ast.Node, symbolFlags ast.SymbolFlags) {
	debug.Assert(symbol.CheckFlags&ast.CheckFlagsLate != 0, "Expected a late-bound symbol.")
	c.lateBoundLinks.Get(member.Symbol()).lateSymbol = symbol
	if len(symbol.Declarations) == 0 || member.Symbol().Flags&ast.SymbolFlagsReplaceableByMethod == 0 {
		symbol.Flags |= symbolFlags
		symbol.Declarations = append(symbol.Declarations, member)
	} else if symbol.Flags&ast.SymbolFlagsReplaceableByMethod != 0 && member.Symbol().Flags&ast.SymbolFlagsMethod != 0 {
		// Remove all replacable-by-method members, along with their flags.
		symbol.Declarations = append(core.Filter(symbol.Declarations, isNotReplacableByMethod), member)
		oldFlags := symbol.Flags
		symbol.Flags = ast.SymbolFlagsNone
		for _, d := range symbol.Declarations {
			symbol.Flags |= d.Symbol().Flags
		}
		if oldFlags&ast.SymbolFlagsAccessor != 0 {
			symbol.Flags |= ast.SymbolFlagsAccessor
		}
	}
	if symbolFlags&ast.SymbolFlagsValue != 0 {
		binder.SetValueDeclaration(symbol, member)
	}
}

/**
 * Gets a SymbolTable containing both the early- and late-bound members of a symbol.
 *
 * For a description of late-binding, see `lateBindMember`.
 */
func (c *Checker) getMembersOfSymbol(symbol *ast.Symbol) ast.SymbolTable {
	if symbol.Flags&ast.SymbolFlagsLateBindingContainer != 0 {
		return c.getResolvedMembersOrExportsOfSymbol(symbol, MembersOrExportsResolutionKindResolvedMembers)
	}
	return symbol.Members
}

func (c *Checker) getExportsOfModule(moduleSymbol *ast.Symbol) ast.SymbolTable {
	links := c.moduleSymbolLinks.Get(moduleSymbol)
	if links.resolvedExports == nil {
		exports, typeOnlyExportStarMap := c.getExportsOfModuleWorker(moduleSymbol)
		links.resolvedExports = exports
		links.typeOnlyExportStarMap = typeOnlyExportStarMap
	}
	return links.resolvedExports
}

type ExportCollision struct {
	specifierText        string
	exportsWithDuplicate []*ast.Node
}

type ExportCollisionTable = map[string]*ExportCollision

func (c *Checker) getExportsOfModuleWorker(moduleSymbol *ast.Symbol) (exports ast.SymbolTable, typeOnlyExportStarMap map[string]*ast.Node) {
	var visitedSymbols []*ast.Symbol
	nonTypeOnlyNames := collections.NewSetWithSizeHint[string](len(moduleSymbol.Exports))
	// The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example,
	// module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error.
	var visit func(*ast.Symbol, *ast.Node, bool) ast.SymbolTable
	visit = func(symbol *ast.Symbol, exportStar *ast.Node, isTypeOnly bool) ast.SymbolTable {
		if !isTypeOnly && symbol != nil {
			// Add non-type-only names before checking if we've visited this module,
			// because we might have visited it via an 'export type *', and visiting
			// again with 'export *' will override the type-onlyness of its exports.
			for name := range symbol.Exports {
				nonTypeOnlyNames.Add(name)
			}
		}
		if symbol == nil || symbol.Exports == nil || slices.Contains(visitedSymbols, symbol) {
			return nil
		}
		visitedSymbols = append(visitedSymbols, symbol)
		symbols := maps.Clone(symbol.Exports)
		// All export * declarations are collected in an __export symbol by the binder
		exportStars := symbol.Exports[ast.InternalSymbolNameExportStar]
		if exportStars != nil {
			nestedSymbols := make(ast.SymbolTable)
			lookupTable := make(ExportCollisionTable)
			for _, node := range exportStars.Declarations {
				resolvedModule := c.resolveExternalModuleName(node, node.ModuleSpecifier(), false /*ignoreErrors*/)
				exportedSymbols := visit(resolvedModule, node, isTypeOnly || node.IsTypeOnly())
				c.extendExportSymbols(nestedSymbols, exportedSymbols, lookupTable, node)
			}
			for id, s := range lookupTable {
				// It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself
				if id == ast.InternalSymbolNameExportEquals || len(s.exportsWithDuplicate) == 0 || symbols[id] != nil {
					continue
				}
				for _, node := range s.exportsWithDuplicate {
					c.addDiagnostic(createDiagnosticForNode(node, diagnostics.Module_0_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity, s.specifierText, id))
				}
			}
			c.extendExportSymbols(symbols, nestedSymbols, nil, nil)
		}
		if exportStar != nil && exportStar.IsTypeOnly() {
			if typeOnlyExportStarMap == nil {
				typeOnlyExportStarMap = make(map[string]*ast.Node)
			}
			for name := range symbols {
				typeOnlyExportStarMap[name] = exportStar
			}
		}
		return symbols
	}
	var originalModule *ast.Symbol
	if moduleSymbol != nil {
		if c.resolveSymbolEx(moduleSymbol.Exports[ast.InternalSymbolNameExportEquals], false /*dontResolveAlias*/) != nil {
			originalModule = moduleSymbol
		}
	}
	// A module defined by an 'export=' consists of one export that needs to be resolved
	moduleSymbol = c.resolveExternalModuleSymbol(moduleSymbol, false /*dontResolveAlias*/)
	exports = visit(moduleSymbol, nil, false)
	if exports == nil {
		exports = make(ast.SymbolTable)
	}
	// A CommonJS module defined by an 'export=' might also export typedefs, stored on the original module
	if originalModule != nil && len(originalModule.Exports) > 1 {
		for _, symbol := range originalModule.Exports {
			if symbol.Name == ast.InternalSymbolNameExportEquals || symbol.Name == ast.InternalSymbolNameExportStar {
				continue
			}
			flags := c.getSymbolFlags(symbol)
			if flags&(ast.SymbolFlagsType|ast.SymbolFlagsNamespace) != 0 &&
				flags&ast.SymbolFlagsValue == 0 &&
				exports[symbol.Name] == nil {
				exports[symbol.Name] = symbol
			}
		}
	}
	for name := range nonTypeOnlyNames.Keys() {
		delete(typeOnlyExportStarMap, name)
	}
	return exports, typeOnlyExportStarMap
}

/**
 * Extends one symbol table with another while collecting information on name collisions for error message generation into the `lookupTable` argument
 * Not passing `lookupTable` and `exportNode` disables this collection, and just extends the tables
 */
func (c *Checker) extendExportSymbols(target ast.SymbolTable, source ast.SymbolTable, lookupTable ExportCollisionTable, exportNode *ast.Node) {
	for id, sourceSymbol := range source {
		if id == ast.InternalSymbolNameDefault {
			continue
		}
		targetSymbol := target[id]
		if targetSymbol == nil {
			target[id] = sourceSymbol
			if lookupTable != nil && exportNode != nil {
				lookupTable[id] = &ExportCollision{
					specifierText: scanner.GetTextOfNode(exportNode.ModuleSpecifier()),
				}
			}
		} else if lookupTable != nil && exportNode != nil && c.resolveSymbol(targetSymbol) != c.resolveSymbol(sourceSymbol) {
			s := lookupTable[id]
			s.exportsWithDuplicate = append(s.exportsWithDuplicate, exportNode)
		}
	}
}

func (c *Checker) ResolveAlias(symbol *ast.Symbol) (*ast.Symbol, bool) {
	if symbol == nil {
		return nil, false
	}
	resolved := c.resolveAlias(symbol)
	return resolved, resolved != c.unknownSymbol
}

// Resolve an alias symbol to the first target symbol in the resolution chain that includes some other
// meaning. Pure aliases are eagerly resolved and any type-only markers are back-propagated to the original
// symbol. The function panics if the argument is not a symbol with an alias meaning.
func (c *Checker) resolveAlias(symbol *ast.Symbol) *ast.Symbol {
	if symbol.Flags&ast.SymbolFlagsAlias == 0 {
		panic("Should only get alias here")
	}
	links := c.aliasSymbolLinks.Get(symbol)
	if links.aliasTarget == nil {
		if !c.pushTypeResolution(symbol, TypeSystemPropertyNameAliasTarget) {
			return c.unknownSymbol
		}
		node := c.getDeclarationOfAliasSymbol(symbol)
		if node == nil {
			panic("Unexpected nil in resolveAlias for symbol: " + c.symbolToString(symbol))
		}
		target := c.getTargetOfAliasDeclaration(node)
		if ast.IsNonLocalAlias(target, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace) {
			// When the target is a pure alias, we transitively resolve and propagate any typeOnlyDeclaration
			target = c.resolveIndirectionAlias(symbol, target)
		}
		links.aliasTarget = core.OrElse(target, c.unknownSymbol)
		if !c.popTypeResolution() {
			c.error(node, diagnostics.Circular_definition_of_import_alias_0, c.symbolToString(symbol))
			links.aliasTarget = c.unknownSymbol
		}
	}
	return links.aliasTarget
}

func (c *Checker) resolveIndirectionAlias(source *ast.Symbol, target *ast.Symbol) *ast.Symbol {
	result := c.getMergedSymbol(c.resolveAlias(target))
	if targetLinks := c.aliasSymbolLinks.Get(target); targetLinks.typeOnlyDeclaration != nil {
		if sourceLinks := c.aliasSymbolLinks.Get(source); sourceLinks.typeOnlyDeclaration == nil {
			sourceLinks.typeOnlyDeclaration = targetLinks.typeOnlyDeclaration
		}
	}
	return result
}

func (c *Checker) tryResolveAlias(symbol *ast.Symbol) *ast.Symbol {
	links := c.aliasSymbolLinks.Get(symbol)
	if links.aliasTarget != nil || c.findResolutionCycleStartIndex(symbol, TypeSystemPropertyNameAliasTarget) < 0 {
		return c.resolveAlias(symbol)
	}
	return nil
}

func (c *Checker) resolveAliasWithDeprecationCheck(symbol *ast.Symbol, location *ast.Node) *ast.Symbol {
	if symbol.Flags&ast.SymbolFlagsAlias == 0 || c.isDeprecatedSymbol(symbol) || c.getDeclarationOfAliasSymbol(symbol) == nil {
		return symbol
	}
	targetSymbol := c.resolveAlias(symbol)
	if targetSymbol == c.unknownSymbol {
		return targetSymbol
	}
	for symbol.Flags&ast.SymbolFlagsAlias != 0 {
		target := c.getImmediateAliasedSymbol(symbol)
		if target != nil {
			if target == targetSymbol {
				break
			}
			if len(target.Declarations) != 0 {
				if c.isDeprecatedSymbol(target) {
					c.addDeprecatedSuggestion(location, target.Declarations, target.Name)
					break
				} else {
					if symbol == targetSymbol {
						break
					}
					symbol = target
				}
			}
		} else {
			break
		}
	}
	return targetSymbol
}

/**
 * Gets combined flags of a `symbol` and all alias targets it resolves to. `resolveAlias`
 * is typically recursive over chains of aliases, but stops mid-chain if an alias is merged
 * with another exported symbol, e.g.
 * ```ts
 * // a.ts
 * export const a = 0;
 * // b.ts
 * export { a } from "./a";
 * export type a = number;
 * // c.ts
 * import { a } from "./b";
 * ```
 * Calling `resolveAlias` on the `a` in c.ts would stop at the merged symbol exported
 * from b.ts, even though there is still more alias to resolve. Consequently, if we were
 * trying to determine if the `a` in c.ts has a value meaning, looking at the flags on
 * the local symbol and on the symbol returned by `resolveAlias` is not enough.
 * @returns SymbolFlags.All if `symbol` is an alias that ultimately resolves to `unknown`;
 * combined flags of all alias targets otherwise.
 */
func (c *Checker) getSymbolFlags(symbol *ast.Symbol) ast.SymbolFlags {
	return c.getSymbolFlagsEx(symbol, false /*excludeTypeOnlyMeanings*/, false /*excludeLocalMeanings*/)
}

func (c *Checker) getSymbolFlagsEx(symbol *ast.Symbol, excludeTypeOnlyMeanings bool, excludeLocalMeanings bool) ast.SymbolFlags {
	var seenSymbols collections.Set[*ast.Symbol]
	var flags ast.SymbolFlags
	if !excludeLocalMeanings {
		flags = symbol.Flags
	}
	for symbol.Flags&ast.SymbolFlagsAlias != 0 {
		if excludeTypeOnlyMeanings && c.getTypeOnlyAliasDeclaration(symbol) != nil {
			break
		}
		target := c.getExportSymbolOfValueSymbolIfExported(c.resolveAlias(symbol))
		if target == c.unknownSymbol {
			return ast.SymbolFlagsAll
		}
		if target.Flags&ast.SymbolFlagsAlias != 0 {
			// Optimization - try to avoid creating or adding to `seenSymbols` if possible
			if target == symbol || seenSymbols.Has(target) {
				break
			}
			if seenSymbols.Len() == 0 {
				seenSymbols.Add(symbol)
			}
			seenSymbols.Add(target)
		}
		flags |= target.Flags
		symbol = target
	}
	return flags
}

func (c *Checker) getDeclarationOfAliasSymbol(symbol *ast.Symbol) *ast.Node {
	return core.FindLast(symbol.Declarations, ast.IsAliasSymbolDeclaration)
}

func (c *Checker) getTypeOfSymbolWithDeferredType(symbol *ast.Symbol) *Type {
	links := c.valueSymbolLinks.Get(symbol)
	if links.resolvedType == nil {
		deferred := c.deferredSymbolLinks.Get(symbol)
		if deferred.parent.flags&TypeFlagsUnion != 0 {
			links.resolvedType = c.getUnionType(deferred.constituents)
		} else {
			links.resolvedType = c.getIntersectionType(deferred.constituents)
		}
	}
	return links.resolvedType
}

func (c *Checker) getWriteTypeOfSymbolWithDeferredType(symbol *ast.Symbol) *Type {
	links := c.valueSymbolLinks.Get(symbol)
	if links.writeType == nil {
		deferred := c.deferredSymbolLinks.Get(symbol)
		if len(deferred.writeConstituents) != 0 {
			if deferred.parent.flags&TypeFlagsUnion != 0 {
				links.writeType = c.getUnionType(deferred.writeConstituents)
			} else {
				links.writeType = c.getIntersectionType(deferred.writeConstituents)
			}
		} else {
			links.writeType = c.getTypeOfSymbolWithDeferredType(symbol)
		}
	}
	return links.writeType
}

// Distinct write types come only from set accessors, but synthetic union and intersection
// properties deriving from set accessors will either pre-compute or defer the union or
// intersection of the writeTypes of their constituents.
func (c *Checker) getWriteTypeOfSymbol(symbol *ast.Symbol) *Type {
	if symbol.CheckFlags&ast.CheckFlagsSyntheticProperty != 0 {
		if symbol.CheckFlags&ast.CheckFlagsDeferredType != 0 {
			return c.getWriteTypeOfSymbolWithDeferredType(symbol)
		}
		links := c.valueSymbolLinks.Get(symbol)
		return core.OrElse(links.writeType, links.resolvedType)
	}
	if symbol.Flags&ast.SymbolFlagsProperty != 0 {
		return c.removeMissingType(c.getTypeOfSymbol(symbol), symbol.Flags&ast.SymbolFlagsOptional != 0)
	}
	if symbol.Flags&ast.SymbolFlagsAccessor != 0 {
		if symbol.CheckFlags&ast.CheckFlagsInstantiated != 0 {
			return c.getWriteTypeOfInstantiatedSymbol(symbol)
		}
		return c.getWriteTypeOfAccessors(symbol)
	}
	return c.getTypeOfSymbol(symbol)
}

func (c *Checker) GetTypeOfSymbolAtLocation(symbol *ast.Symbol, location *ast.Node) *Type {
	symbol = c.getExportSymbolOfValueSymbolIfExported(symbol)
	if location != nil {
		// If we have an identifier or a property access at the given location, if the location is
		// an dotted name expression, and if the location is not an assignment target, obtain the type
		// of the expression (which will reflect control flow analysis). If the expression indeed
		// resolved to the given symbol, return the narrowed type.
		if (ast.IsIdentifier(location) || ast.IsPrivateIdentifier(location)) &&
			!(ast.IsJsxTagName(location) || ast.IsJsxAttribute(location.Parent) || ast.IsJsxNamespacedName(location.Parent)) {
			if ast.IsRightSideOfQualifiedNameOrPropertyAccess(location) {
				location = location.Parent
			}
			if ast.IsExpressionNode(location) && (!ast.IsAssignmentTarget(location) || ast.IsWriteAccess(location)) {
				var t *Type
				if ast.IsWriteAccess(location) && location.Kind == ast.KindPropertyAccessExpression {
					t = c.checkPropertyAccessExpression(location, CheckModeNormal, true /*writeOnly*/)
				} else {
					t = c.getTypeOfExpression(location)
				}
				if c.getExportSymbolOfValueSymbolIfExported(c.symbolNodeLinks.Get(location).resolvedSymbol) == symbol {
					return c.removeOptionalTypeMarker(t)
				}
			}
		}
		if ast.IsDeclarationName(location) && ast.IsSetAccessorDeclaration(location.Parent) && c.getAnnotatedAccessorTypeNode(location.Parent) != nil {
			return c.getWriteTypeOfAccessors(location.Parent.Symbol())
		}
		// The location isn't a reference to the given symbol, meaning we're being asked
		// a hypothetical question of what type the symbol would have if there was a reference
		// to it at the given location. Since we have no control flow information for the
		// hypothetical reference (control flow information is created and attached by the
		// binder), we simply return the declared type of the symbol.
		if isRightSideOfAccessExpression(location) && ast.IsWriteAccess(location.Parent) {
			return c.getWriteTypeOfSymbol(symbol)
		}
	}
	return c.getNonMissingTypeOfSymbol(symbol)
}

func (c *Checker) getTypeOfSymbol(symbol *ast.Symbol) *Type {
	if symbol.CheckFlags&ast.CheckFlagsDeferredType != 0 {
		return c.getTypeOfSymbolWithDeferredType(symbol)
	}
	if symbol.CheckFlags&ast.CheckFlagsInstantiated != 0 {
		return c.getTypeOfInstantiatedSymbol(symbol)
	}
	if symbol.CheckFlags&ast.CheckFlagsMapped != 0 {
		return c.getTypeOfMappedSymbol(symbol)
	}
	if symbol.CheckFlags&ast.CheckFlagsReverseMapped != 0 {
		return c.getTypeOfReverseMappedSymbol(symbol)
	}
	if symbol.Flags&ast.SymbolFlagsAccessor != 0 {
		return c.getTypeOfAccessors(symbol)
	}
	if symbol.Flags&(ast.SymbolFlagsVariable|ast.SymbolFlagsProperty) != 0 {
		return c.getTypeOfVariableOrParameterOrProperty(symbol)
	}
	if symbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod|ast.SymbolFlagsClass|ast.SymbolFlagsEnum|ast.SymbolFlagsValueModule) != 0 {
		return c.getTypeOfFuncClassEnumModule(symbol)
	}
	if symbol.Flags&ast.SymbolFlagsEnumMember != 0 {
		return c.getTypeOfEnumMember(symbol)
	}
	if symbol.Flags&ast.SymbolFlagsAlias != 0 {
		return c.getTypeOfAlias(symbol)
	}
	return c.errorType
}

func (c *Checker) getNonMissingTypeOfSymbol(symbol *ast.Symbol) *Type {
	return c.removeMissingType(c.getTypeOfSymbol(symbol), symbol.Flags&ast.SymbolFlagsOptional != 0)
}

func (c *Checker) getTypeOfInstantiatedSymbol(symbol *ast.Symbol) *Type {
	links := c.valueSymbolLinks.Get(symbol)
	if links.resolvedType == nil {
		links.resolvedType = c.instantiateType(c.getTypeOfSymbol(links.target), links.mapper)
	}
	return links.resolvedType
}

func (c *Checker) getWriteTypeOfInstantiatedSymbol(symbol *ast.Symbol) *Type {
	links := c.valueSymbolLinks.Get(symbol)
	if links.writeType == nil {
		links.writeType = c.instantiateType(c.getWriteTypeOfSymbol(links.target), links.mapper)
	}
	return links.writeType
}

func (c *Checker) getTypeOfVariableOrParameterOrProperty(symbol *ast.Symbol) *Type {
	links := c.valueSymbolLinks.Get(symbol)
	if links.resolvedType == nil {
		t := c.getTypeOfVariableOrParameterOrPropertyWorker(symbol)
		if t == nil {
			panic("Unexpected nil type")
		}
		// For a contextually typed parameter it is possible that a type has already
		// been assigned (in assignTypeToParameterAndFixTypeParameters), and we want
		// to preserve this type. In fact, we need to _prefer_ that type, but it won't
		// be assigned until contextual typing is complete, so we need to defer in
		// cases where contextual typing may take place.
		if links.resolvedType == nil && !c.isParameterOfContextSensitiveSignature(symbol) {
			links.resolvedType = t
		}
		return t
	}
	return links.resolvedType
}

func (c *Checker) isParameterOfContextSensitiveSignature(symbol *ast.Symbol) bool {
	decl := symbol.ValueDeclaration
	if decl == nil {
		return false
	}
	if ast.IsBindingElement(decl) {
		decl = ast.WalkUpBindingElementsAndPatterns(decl)
	}
	if ast.IsParameterDeclaration(decl) {
		return c.isContextSensitiveFunctionOrObjectLiteralMethod(decl.Parent)
	}
	return false
}

func (c *Checker) getTypeOfVariableOrParameterOrPropertyWorker(symbol *ast.Symbol) *Type {
	// Handle prototype property
	if symbol.Flags&ast.SymbolFlagsPrototype != 0 {
		return c.getTypeOfPrototypeProperty(symbol)
	}
	// CommonsJS require and module both have type any.
	if symbol == c.requireSymbol {
		return c.anyType
	}
	debug.Assert(symbol.ValueDeclaration != nil)
	declaration := symbol.ValueDeclaration
	if ast.IsSourceFile(declaration) && ast.IsJsonSourceFile(declaration.AsSourceFile()) {
		statements := declaration.Statements()
		if len(statements) == 0 {
			return c.emptyObjectType
		}
		return c.getWidenedType(c.getWidenedLiteralType(c.checkExpression(statements[0].Expression())))
	}
	// Handle variable, parameter or property
	if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) {
		return c.reportCircularityError(symbol)
	}
	if symbol.Flags&ast.SymbolFlagsModuleExports != 0 {
		if symbol.Name == "exports" {
			return c.getTypeOfSymbol(c.resolveExternalModuleSymbol(symbol.ValueDeclaration.Symbol(), false /*dontResolveAlias*/))
		}
		return c.newAnonymousType(symbol, symbol.Members, nil, nil, nil)
	}
	var result *Type
	switch declaration.Kind {
	case ast.KindParameter, ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindVariableDeclaration,
		ast.KindBindingElement:
		result = c.getWidenedTypeForVariableLikeDeclaration(declaration, true /*reportErrors*/)
	case ast.KindPropertyAssignment:
		result = c.checkPropertyAssignment(declaration, CheckModeNormal)
	case ast.KindShorthandPropertyAssignment:
		result = c.checkShorthandPropertyAssignment(declaration, true /*inDestructuringPattern*/, CheckModeNormal)
	case ast.KindMethodDeclaration:
		result = c.checkObjectLiteralMethod(declaration, CheckModeNormal)
	case ast.KindExportAssignment:
		if declaration.Type() != nil {
			result = c.getTypeFromTypeNode(declaration.Type())
		} else {
			result = c.widenTypeForVariableLikeDeclaration(c.checkExpressionCached(declaration.Expression()), declaration, false /*reportErrors*/)
		}
	case ast.KindBinaryExpression, ast.KindCallExpression:
		result = c.getWidenedTypeForAssignmentDeclaration(symbol)
	case ast.KindJsxAttribute:
		result = c.checkJsxAttribute(declaration, CheckModeNormal)
	case ast.KindEnumMember:
		result = c.getTypeOfEnumMember(symbol)
	default:
		panic("Unhandled case in getTypeOfVariableOrParameterOrPropertyWorker: " + declaration.Kind.String())
	}
	if !c.popTypeResolution() {
		return c.reportCircularityError(symbol)
	}
	return result
}

// Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type
// specified in a type annotation or inferred from an initializer. However, in the case of a destructuring declaration it
// is a bit more involved. For example:
//
//	var [x, s = ""] = [1, "one"];
//
// Here, the array literal [1, "one"] is contextually typed by the type [any, string], which is the implied type of the
// binding pattern [x, s = ""]. Because the contextual type is a tuple type, the resulting type of [1, "one"] is the
// tuple type [number, string]. Thus, the type inferred for 'x' is number and the type inferred for 's' is string.
func (c *Checker) getWidenedTypeForVariableLikeDeclaration(declaration *ast.Node, reportErrors bool) *Type {
	return c.widenTypeForVariableLikeDeclaration(c.getTypeForVariableLikeDeclaration(declaration /*includeOptionality*/, true, CheckModeNormal), declaration, reportErrors)
}

// Return the inferred type for a variable, parameter, or property declaration
func (c *Checker) getTypeForVariableLikeDeclaration(declaration *ast.Node, includeOptionality bool, checkMode CheckMode) *Type {
	// A variable declared in a for..in statement is of type string, or of type keyof T when the
	// right hand expression is of a type parameter type.
	if ast.IsVariableDeclaration(declaration) {
		grandParent := declaration.Parent.Parent
		switch grandParent.Kind {
		case ast.KindForInStatement:
			indexType := c.getIndexType(c.getNonNullableTypeIfNeeded(c.checkExpressionEx(grandParent.Expression(), checkMode /*checkMode*/)))
			if indexType.flags&(TypeFlagsTypeParameter|TypeFlagsIndex) != 0 {
				return c.getExtractStringType(indexType)
			}
			return c.stringType
		case ast.KindForOfStatement:
			// checkRightHandSideOfForOf will return undefined if the for-of expression type was
			// missing properties/signatures required to get its iteratedType (like
			// [Symbol.iterator] or next). This may be because we accessed properties from anyType,
			// or it may have led to an error inside getElementTypeOfIterable.
			return c.checkRightHandSideOfForOf(grandParent)
		}
	} else if ast.IsBindingElement(declaration) {
		return c.getTypeForBindingElement(declaration)
	}
	isProperty := ast.IsPropertyDeclaration(declaration) && !ast.HasAccessorModifier(declaration) || ast.IsPropertySignatureDeclaration(declaration)
	isOptional := includeOptionality && isOptionalDeclaration(declaration)
	// Use type from type annotation if one is present
	declaredType := c.tryGetTypeFromTypeNode(declaration)
	if ast.IsCatchClauseVariableDeclarationOrBindingElement(declaration) {
		if declaredType != nil {
			// If the catch clause is explicitly annotated with any or unknown, accept it, otherwise error.
			if declaredType.flags&TypeFlagsAnyOrUnknown != 0 {
				return declaredType
			}
			return c.errorType
		}
		// If the catch clause is not explicitly annotated, treat it as though it were explicitly
		// annotated with unknown or any, depending on useUnknownInCatchVariables.
		if c.useUnknownInCatchVariables {
			return c.unknownType
		} else {
			return c.anyType
		}
	}
	if declaredType != nil {
		return c.addOptionalityEx(declaredType, isProperty, isOptional)
	}
	if c.noImplicitAny && ast.IsVariableDeclaration(declaration) && !ast.IsBindingPattern(declaration.Name()) &&
		c.getCombinedModifierFlagsCached(declaration)&ast.ModifierFlagsExport == 0 && declaration.Flags&ast.NodeFlagsAmbient == 0 {
		// If --noImplicitAny is on or the declaration is in a Javascript file,
		// use control flow tracked 'any' type for non-ambient, non-exported var or let variables with no
		// initializer or a 'null' or 'undefined' initializer.
		initializer := declaration.Initializer()
		if c.getCombinedNodeFlagsCached(declaration)&ast.NodeFlagsConstant == 0 && (initializer == nil || c.isNullOrUndefined(initializer)) {
			return c.autoType
		}
		// Use control flow tracked 'any[]' type for non-ambient, non-exported variables with an empty array
		// literal initializer.
		if initializer != nil && isEmptyArrayLiteral(initializer) {
			return c.autoArrayType
		}
	}
	if ast.IsParameterDeclaration(declaration) {
		if declaration.Symbol() == nil {
			// parameters of function types defined in JSDoc in TS files don't have symbols
			return nil
		}
		fn := declaration.Parent
		// For a parameter of a set accessor, use the type of the get accessor if one is present
		if ast.IsSetAccessorDeclaration(fn) && c.hasBindableName(fn) {
			getter := ast.GetDeclarationOfKind(c.getSymbolOfDeclaration(declaration.Parent), ast.KindGetAccessor)
			if getter != nil {
				getterSignature := c.getSignatureFromDeclaration(getter)
				thisParameter := c.getAccessorThisParameter(fn)
				if thisParameter != nil && declaration == thisParameter {
					// Use the type from the *getter*
					debug.Assert(thisParameter.Type() == nil)
					return c.getTypeOfSymbol(getterSignature.thisParameter)
				}
				return c.getReturnTypeOfSignature(getterSignature)
			}
		}
		if t := c.getParameterTypeOfFullSignature(fn, declaration); t != nil {
			return t
		}
		// Use contextual parameter type if one is available
		var t *Type
		if declaration.Symbol().Name == ast.InternalSymbolNameThis {
			t = c.getContextualThisParameterType(fn)
		} else {
			t = c.getContextuallyTypedParameterType(declaration)
		}
		if t != nil {
			return c.addOptionalityEx(t, false /*isProperty*/, isOptional)
		}
	}
	// Use the type of the initializer expression if one is present and the declaration is
	// not a parameter of a contextually typed function
	if declaration.Initializer() != nil {
		t := c.widenTypeInferredFromInitializer(declaration, c.checkDeclarationInitializer(declaration, checkMode, nil /*contextualType*/))
		return c.addOptionalityEx(t, isProperty, isOptional)
	}
	if c.noImplicitAny && ast.IsPropertyDeclaration(declaration) {
		// We have a property declaration with no type annotation or initializer, in noImplicitAny mode or a .js file.
		// Use control flow analysis of this.xxx assignments in the constructor or static block to determine the type of the property.
		if !ast.HasStaticModifier(declaration) {
			constructor := ast.FindConstructorDeclaration(declaration.Parent)
			var t *Type
			switch {
			case constructor != nil:
				t = c.getFlowTypeInConstructor(declaration.Symbol(), constructor)
			case declaration.ModifierFlags()&ast.ModifierFlagsAmbient != 0:
				t = c.getTypeOfPropertyInBaseClass(declaration.Symbol())
			}
			if t == nil {
				return nil
			}
			return c.addOptionalityEx(t, true /*isProperty*/, isOptional)
		} else {
			staticBlocks := core.Filter(declaration.Parent.Members(), ast.IsClassStaticBlockDeclaration)
			var t *Type
			switch {
			case len(staticBlocks) != 0:
				t = c.getFlowTypeInStaticBlocks(declaration.Symbol(), staticBlocks)
			case declaration.ModifierFlags()&ast.ModifierFlagsAmbient != 0:
				t = c.getTypeOfPropertyInBaseClass(declaration.Symbol())
			}
			if t == nil {
				return nil
			}
			return c.addOptionalityEx(t, true /*isProperty*/, isOptional)
		}
	}
	if ast.IsJsxAttribute(declaration) {
		// if JSX attribute doesn't have initializer, by default the attribute will have boolean value of true.
		// I.e <Elem attr /> is sugar for <Elem attr={true} />
		return c.trueType
	}
	// If the declaration specifies a binding pattern and is not a parameter of a contextually
	// typed function, use the type implied by the binding pattern
	if ast.IsBindingPattern(declaration.Name()) {
		return c.getTypeFromBindingPattern(declaration.Name() /*includePatternInType*/, false /*reportErrors*/, true)
	}
	// No type specified and nothing can be inferred
	return nil
}

func (c *Checker) checkDeclarationInitializer(declaration *ast.Node, checkMode CheckMode, contextualType *Type) *Type {
	initializer := declaration.Initializer()
	t := c.getQuickTypeOfExpression(initializer)
	if t == nil {
		if contextualType != nil {
			t = c.checkExpressionWithContextualType(initializer, contextualType, nil /*inferenceContext*/, checkMode)
		} else {
			t = c.checkExpressionCachedEx(initializer, checkMode)
		}
	}
	if ast.IsParameterDeclaration(ast.GetRootDeclaration(declaration)) {
		name := declaration.Name()
		switch name.Kind {
		case ast.KindObjectBindingPattern:
			if isObjectLiteralType(t) {
				return c.padObjectLiteralType(t, name)
			}
		case ast.KindArrayBindingPattern:
			if isTupleType(t) {
				return c.padTupleType(t, name)
			}
		}
	}
	return t
}

func (c *Checker) padObjectLiteralType(t *Type, pattern *ast.Node) *Type {
	var missingElements []*ast.Node
	for _, e := range pattern.Elements() {
		if e.Initializer() != nil {
			name := c.getPropertyNameFromBindingElement(e)
			if name != ast.InternalSymbolNameMissing && c.getPropertyOfType(t, name) == nil {
				missingElements = append(missingElements, e)
			}
		}
	}
	if len(missingElements) == 0 {
		return t
	}
	members := make(ast.SymbolTable)
	for _, prop := range c.getPropertiesOfObjectType(t) {
		members[prop.Name] = prop
	}
	for _, e := range missingElements {
		symbol := c.newSymbol(ast.SymbolFlagsProperty|ast.SymbolFlagsOptional, c.getPropertyNameFromBindingElement(e))
		c.valueSymbolLinks.Get(symbol).resolvedType = c.getTypeFromBindingElement(e, false /*includePatternInType*/, false /*reportErrors*/)
		members[symbol.Name] = symbol
	}
	result := c.newAnonymousType(t.symbol, members, nil, nil, c.getIndexInfosOfType(t))
	result.objectFlags = t.objectFlags
	return result
}

func (c *Checker) getPropertyNameFromBindingElement(e *ast.Node) string {
	exprType := c.getLiteralTypeFromPropertyName(e.PropertyNameOrName())
	if isTypeUsableAsPropertyName(exprType) {
		return getPropertyNameFromType(exprType)
	}
	return ast.InternalSymbolNameMissing
}

func (c *Checker) padTupleType(t *Type, pattern *ast.Node) *Type {
	patternElements := pattern.Elements()
	if t.TargetTupleType().combinedFlags&ElementFlagsVariable != 0 || c.getTypeReferenceArity(t) >= len(patternElements) {
		return t
	}
	elementTypes := slices.Clone(c.getElementTypes(t))
	elementInfos := slices.Clone(t.TargetTupleType().elementInfos)
	for i := c.getTypeReferenceArity(t); i < len(patternElements); i++ {
		e := patternElements[i]
		if i < len(patternElements)-1 || !(ast.IsBindingElement(e) && hasDotDotDotToken(e)) {
			elementType := c.anyType
			if !ast.IsOmittedExpression(e) && c.hasDefaultValue(e) {
				elementType = c.getTypeFromBindingElement(e, false /*includePatternInType*/, false /*reportErrors*/)
			}
			elementTypes = append(elementTypes, elementType)
			elementInfos = append(elementInfos, TupleElementInfo{flags: ElementFlagsOptional})
			if !ast.IsOmittedExpression(e) && !c.hasDefaultValue(e) {
				c.reportImplicitAny(e, c.anyType, WideningKindNormal)
			}
		}
	}
	return c.createTupleTypeEx(elementTypes, elementInfos, t.TargetTupleType().readonly)
}

func (c *Checker) widenTypeInferredFromInitializer(declaration *ast.Node, t *Type) *Type {
	widened := c.getWidenedLiteralTypeForInitializer(declaration, t)
	if ast.IsInJSFile(declaration) {
		if c.isEmptyLiteralType(widened) {
			c.reportImplicitAny(declaration, c.anyType, WideningKindNormal)
			return c.anyType
		}
		if c.isEmptyArrayLiteralType(widened) {
			c.reportImplicitAny(declaration, c.anyArrayType, WideningKindNormal)
			return c.anyArrayType
		}
	}
	return widened
}

func (c *Checker) getWidenedLiteralTypeForInitializer(declaration *ast.Node, t *Type) *Type {
	if c.getCombinedNodeFlagsCached(declaration)&ast.NodeFlagsConstant != 0 || isDeclarationReadonly(declaration) {
		return t
	}
	return c.getWidenedLiteralType(t)
}

func (c *Checker) getTypeOfFuncClassEnumModule(symbol *ast.Symbol) *Type {
	links := c.valueSymbolLinks.Get(symbol)
	if links.resolvedType == nil {
		links.resolvedType = c.getTypeOfFuncClassEnumModuleWorker(symbol)
	}
	return links.resolvedType
}

func (c *Checker) getTypeOfFuncClassEnumModuleWorker(symbol *ast.Symbol) *Type {
	if symbol.Flags&ast.SymbolFlagsModule != 0 && isShorthandAmbientModuleSymbol(symbol) {
		return c.anyType
	} else if symbol.Flags&ast.SymbolFlagsValueModule != 0 && symbol.ValueDeclaration != nil &&
		ast.IsSourceFile(symbol.ValueDeclaration) && symbol.ValueDeclaration.AsSourceFile().CommonJSModuleIndicator != nil {
		resolvedModule := c.resolveExternalModuleSymbol(symbol, false /*dontResolveAlias*/)
		if resolvedModule != symbol {
			return c.getTypeOfSymbol(resolvedModule)
		}
	}
	t := c.newObjectType(ObjectFlagsAnonymous, symbol)
	if symbol.Flags&ast.SymbolFlagsClass != 0 {
		baseTypeVariable := c.getBaseTypeVariableOfClass(symbol)
		if baseTypeVariable != nil {
			return c.getIntersectionType([]*Type{t, baseTypeVariable})
		}
		return t
	}
	if c.strictNullChecks && symbol.Flags&ast.SymbolFlagsOptional != 0 {
		return c.getOptionalType(t /*isProperty*/, true)
	}
	return t
}

func (c *Checker) getBaseTypeVariableOfClass(symbol *ast.Symbol) *Type {
	baseConstructorType := c.getBaseConstructorTypeOfClass(c.getDeclaredTypeOfClassOrInterface(symbol))
	switch {
	case baseConstructorType.flags&TypeFlagsTypeVariable != 0:
		return baseConstructorType
	case baseConstructorType.flags&TypeFlagsIntersection != 0:
		return core.Find(baseConstructorType.Types(), func(t *Type) bool {
			return t.flags&TypeFlagsTypeVariable != 0
		})
	}
	return nil
}

/**
 * The base constructor of a class can resolve to
 * * undefinedType if the class has no extends clause,
 * * errorType if an error occurred during resolution of the extends expression,
 * * nullType if the extends expression is the null value,
 * * anyType if the extends expression has type any, or
 * * an object type with at least one construct signature.
 */
func (c *Checker) getBaseConstructorTypeOfClass(t *Type) *Type {
	data := t.AsInterfaceType()
	if data.resolvedBaseConstructorType != nil {
		return data.resolvedBaseConstructorType
	}
	baseTypeNode := getBaseTypeNodeOfClass(t)
	if baseTypeNode == nil {
		data.resolvedBaseConstructorType = c.undefinedType
		return data.resolvedBaseConstructorType
	}
	if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedBaseConstructorType) {
		return c.errorType
	}
	baseConstructorType := c.checkExpression(baseTypeNode.Expression())
	if baseConstructorType.flags&(TypeFlagsObject|TypeFlagsIntersection) != 0 {
		// Resolving the members of a class requires us to resolve the base class of that class.
		// We force resolution here such that we catch circularities now.
		c.resolveStructuredTypeMembers(baseConstructorType)
	}
	if !c.popTypeResolution() {
		c.error(t.symbol.ValueDeclaration, diagnostics.X_0_is_referenced_directly_or_indirectly_in_its_own_base_expression, c.symbolToString(t.symbol))
		if data.resolvedBaseConstructorType == nil {
			data.resolvedBaseConstructorType = c.errorType
		}
		return data.resolvedBaseConstructorType
	}
	if baseConstructorType.flags&TypeFlagsAny == 0 && baseConstructorType != c.nullWideningType && !c.isConstructorType(baseConstructorType) {
		err := c.error(baseTypeNode.Expression(), diagnostics.Type_0_is_not_a_constructor_function_type, c.TypeToString(baseConstructorType))
		if baseConstructorType.flags&TypeFlagsTypeParameter != 0 {
			constraint := c.getConstraintFromTypeParameter(baseConstructorType)
			var ctorReturn *Type = c.unknownType
			if constraint != nil {
				ctorSigs := c.getSignaturesOfType(constraint, SignatureKindConstruct)
				if len(ctorSigs) != 0 {
					ctorReturn = c.getReturnTypeOfSignature(ctorSigs[0])
				}
			}
			if baseConstructorType.symbol.Declarations != nil {
				err.AddRelatedInfo(createDiagnosticForNode(baseConstructorType.symbol.Declarations[0], diagnostics.Did_you_mean_for_0_to_be_constrained_to_type_new_args_Colon_any_1, c.symbolToString(baseConstructorType.symbol), c.TypeToString(ctorReturn)))
			}
		}
		if data.resolvedBaseConstructorType == nil {
			data.resolvedBaseConstructorType = c.errorType
		}
		return data.resolvedBaseConstructorType
	}
	if data.resolvedBaseConstructorType == nil {
		data.resolvedBaseConstructorType = baseConstructorType
	}
	return data.resolvedBaseConstructorType
}

func (c *Checker) isFunctionType(t *Type) bool {
	return t.flags&TypeFlagsObject != 0 && len(c.getSignaturesOfType(t, SignatureKindCall)) > 0
}

func (c *Checker) isConstructorType(t *Type) bool {
	if len(c.getSignaturesOfType(t, SignatureKindConstruct)) > 0 {
		return true
	}
	if t.flags&TypeFlagsTypeVariable != 0 {
		constraint := c.getBaseConstraintOfType(t)
		return constraint != nil && c.isMixinConstructorType(constraint)
	}
	return false
}

// A type is a mixin constructor if it has a single construct signature taking no type parameters and a single
// rest parameter of type any[].
func (c *Checker) isMixinConstructorType(t *Type) bool {
	signatures := c.getSignaturesOfType(t, SignatureKindConstruct)
	if len(signatures) == 1 {
		s := signatures[0]
		if len(s.typeParameters) == 0 && len(s.parameters) == 1 && signatureHasRestParameter(s) {
			paramType := c.getTypeOfParameter(s.parameters[0])
			return IsTypeAny(paramType) || c.getElementTypeOfArrayType(paramType) == c.anyType
		}
	}
	return false
}

func signatureHasRestParameter(sig *Signature) bool {
	return sig.flags&SignatureFlagsHasRestParameter != 0
}

func (c *Checker) getTypeOfParameter(symbol *ast.Symbol) *Type {
	declaration := symbol.ValueDeclaration
	return c.addOptionalityEx(c.getTypeOfSymbol(symbol), false, declaration != nil && (declaration.Initializer() != nil || isOptionalDeclaration(declaration)))
}

func (c *Checker) getConstraintOfType(t *Type) *Type {
	switch {
	case t.flags&TypeFlagsTypeParameter != 0:
		return c.getConstraintOfTypeParameter(t)
	case t.flags&TypeFlagsIndexedAccess != 0:
		return c.getConstraintOfIndexedAccess(t)
	case t.flags&TypeFlagsConditional != 0:
		return c.getConstraintOfConditionalType(t)
	}
	return c.getBaseConstraintOfType(t)
}

func (c *Checker) getConstraintOfTypeParameter(typeParameter *Type) *Type {
	if c.hasNonCircularBaseConstraint(typeParameter) {
		return c.getConstraintFromTypeParameter(typeParameter)
	}
	return nil
}

func (c *Checker) hasNonCircularBaseConstraint(t *Type) bool {
	return c.getResolvedBaseConstraint(t, nil) != c.circularConstraintType
}

// This is a worker function. Use getConstraintOfTypeParameter which guards against circular constraints
func (c *Checker) getConstraintFromTypeParameter(t *Type) *Type {
	if t.flags&TypeFlagsTypeParameter == 0 {
		return nil
	}

	tp := t.AsTypeParameter()
	if tp.constraint == nil {
		var constraint *Type
		if tp.target != nil {
			constraint = c.instantiateType(c.getConstraintOfTypeParameter(tp.target), tp.mapper)
		} else {
			constraintDeclaration := c.getConstraintDeclaration(t)
			if constraintDeclaration != nil {
				constraint = c.getTypeFromTypeNode(constraintDeclaration)
				if constraint.flags&TypeFlagsAny != 0 && !c.isErrorType(constraint) {
					// use stringNumberSymbolType as the base constraint for mapped type key constraints (unknown isn;t assignable to that, but `any` was),
					// use unknown otherwise
					if ast.IsMappedTypeNode(constraintDeclaration.Parent.Parent) {
						constraint = c.stringNumberSymbolType
					} else {
						constraint = c.unknownType
					}
				}
			} else {
				constraint = c.getInferredTypeParameterConstraint(t, false)
			}
		}
		if constraint == nil {
			constraint = c.noConstraintType
		}
		tp.constraint = constraint
	}
	if tp.constraint != c.noConstraintType {
		return tp.constraint
	}
	return nil
}

func (c *Checker) getConstraintOrUnknownFromTypeParameter(t *Type) *Type {
	result := c.getConstraintFromTypeParameter(t)
	return core.IfElse(result != nil, result, c.unknownType)
}

func (c *Checker) getInferredTypeParameterConstraint(t *Type, omitTypeReferences bool) *Type {
	var inferences []*Type
	if t.symbol != nil && len(t.symbol.Declarations) != 0 {
		for _, declaration := range t.symbol.Declarations {
			if ast.IsInferTypeNode(declaration.Parent) {
				// When an 'infer T' declaration is immediately contained in a type reference node
				// (such as 'Foo<infer T>'), T's constraint is inferred from the constraint of the
				// corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are
				// present, we form an intersection of the inferred constraint types.
				child := declaration.Parent
				parent := child.Parent
				for parent != nil && ast.IsParenthesizedTypeNode(parent) {
					child = parent
					parent = child.Parent
				}
				switch {
				case ast.IsTypeReferenceNode(parent) && !omitTypeReferences:
					typeParameters := c.getTypeParametersForTypeReferenceOrImport(parent)
					if typeParameters != nil {
						index := slices.Index(parent.TypeArguments(), child)
						if index >= 0 && index < len(typeParameters) {
							declaredConstraint := c.getConstraintOfTypeParameter(typeParameters[index])
							if declaredConstraint != nil {
								// Type parameter constraints can reference other type parameters so
								// constraints need to be instantiated. If instantiation produces the
								// type parameter itself, we discard that inference. For example, in
								//   type Foo<T extends string, U extends T> = [T, U];
								//   type Bar<T> = T extends Foo<infer X, infer X> ? Foo<X, X> : T;
								// the instantiated constraint for U is X, so we discard that inference.
								mapper := newDeferredTypeMapper(typeParameters, core.MapIndex(typeParameters, func(_ *Type, index int) func() *Type {
									return func() *Type {
										return c.getEffectiveTypeArgumentAtIndex(parent, typeParameters, index)
									}
								}))
								constraint := c.instantiateType(declaredConstraint, mapper)
								if constraint != t {
									inferences = append(inferences, constraint)
								}
							}
						}
					}
				case ast.IsParameterDeclaration(parent) && parent.AsParameterDeclaration().DotDotDotToken != nil ||
					ast.IsRestTypeNode(parent) ||
					ast.IsNamedTupleMember(parent) && parent.AsNamedTupleMember().DotDotDotToken != nil:
					inferences = append(inferences, c.createArrayType(c.unknownType))
				case ast.IsTemplateLiteralTypeSpan(parent):
					inferences = append(inferences, c.stringType)
				case ast.IsTypeParameterDeclaration(parent) && ast.IsMappedTypeNode(parent.Parent):
					inferences = append(inferences, c.stringNumberSymbolType)
				case ast.IsMappedTypeNode(parent) && parent.Type() != nil &&
					ast.SkipParentheses(parent.Type()) == declaration.Parent &&
					ast.IsConditionalTypeNode(parent.Parent) &&
					parent.Parent.AsConditionalTypeNode().ExtendsType == parent &&
					ast.IsMappedTypeNode(parent.Parent.AsConditionalTypeNode().CheckType) &&
					parent.Parent.AsConditionalTypeNode().CheckType.Type() != nil:
					checkMappedType := parent.Parent.AsConditionalTypeNode().CheckType
					nodeType := c.getTypeFromTypeNode(checkMappedType.Type())
					checkMappedTypeParameter := checkMappedType.AsMappedTypeNode().TypeParameter
					mapper := newSimpleTypeMapper(c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(checkMappedTypeParameter)),
						core.IfElse(checkMappedTypeParameter.AsTypeParameterDeclaration().Constraint != nil,
							c.getTypeFromTypeNode(checkMappedTypeParameter.AsTypeParameterDeclaration().Constraint),
							c.stringNumberSymbolType))
					inferences = append(inferences, c.instantiateType(nodeType, mapper))
				}
			}
		}
	}
	if len(inferences) != 0 {
		return c.getIntersectionType(inferences)
	}
	return nil
}

func (c *Checker) getTypeParametersForTypeReferenceOrImport(node *ast.Node) []*Type {
	t := c.getTypeFromTypeNode(node)
	if !c.isErrorType(t) {
		symbol := c.getResolvedSymbolOrNil(node)
		if symbol != nil {
			return c.getTypeParametersForTypeAndSymbol(t, symbol)
		}
	}
	return nil
}

func (c *Checker) getTypeParametersForTypeAndSymbol(t *Type, symbol *ast.Symbol) []*Type {
	if !c.isErrorType(t) {
		if symbol.Flags&ast.SymbolFlagsTypeAlias != 0 {
			if typeParameters := c.typeAliasLinks.Get(symbol).typeParameters; len(typeParameters) != 0 {
				return typeParameters
			}
		}
		if t.objectFlags&ObjectFlagsReference != 0 {
			return t.Target().AsInterfaceType().LocalTypeParameters()
		}
	}
	return nil
}

func (c *Checker) getEffectiveTypeArgumentAtIndex(node *ast.Node, typeParameters []*Type, index int) *Type {
	typeArguments := node.TypeArguments()
	if index < len(typeArguments) {
		return c.getTypeFromTypeNode(typeArguments[index])
	}
	return c.getEffectiveTypeArguments(node, typeParameters)[index]
}

func (c *Checker) getConstraintOfIndexedAccess(t *Type) *Type {
	if c.hasNonCircularBaseConstraint(t) {
		return c.getConstraintFromIndexedAccess(t)
	}
	return nil
}

func (c *Checker) getConstraintFromIndexedAccess(t *Type) *Type {
	d := t.AsIndexedAccessType()
	if c.isMappedTypeGenericIndexedAccess(t) {
		// For indexed access types of the form { [P in K]: E }[X], where K is non-generic and X is generic,
		// we substitute an instantiation of E where P is replaced with X.
		return c.substituteIndexedMappedType(d.objectType, d.indexType)
	}
	indexConstraint := c.getSimplifiedTypeOrConstraint(d.indexType)
	if indexConstraint != nil && indexConstraint != d.indexType {
		indexedAccess := c.getIndexedAccessTypeOrUndefined(d.objectType, indexConstraint, d.accessFlags, nil, nil)
		if indexedAccess != nil {
			return indexedAccess
		}
	}
	objectConstraint := c.getSimplifiedTypeOrConstraint(d.objectType)
	if objectConstraint != nil && objectConstraint != d.objectType {
		return c.getIndexedAccessTypeOrUndefined(objectConstraint, d.indexType, d.accessFlags, nil, nil)
	}
	return nil
}

func (c *Checker) getConstraintOfConditionalType(t *Type) *Type {
	if c.hasNonCircularBaseConstraint(t) {
		return c.getConstraintFromConditionalType(t)
	}
	return nil
}

func (c *Checker) getConstraintFromConditionalType(t *Type) *Type {
	constraint := c.getConstraintOfDistributiveConditionalType(t)
	if constraint != nil {
		return constraint
	}
	return c.getDefaultConstraintOfConditionalType(t)
}

func (c *Checker) getDefaultConstraintOfConditionalType(t *Type) *Type {
	d := t.AsConditionalType()
	if d.resolvedDefaultConstraint == nil {
		// An `any` branch of a conditional type would normally be viral - specifically, without special handling here,
		// a conditional type with a single branch of type `any` would be assignable to anything, since it's constraint would simplify to
		// just `any`. This result is _usually_ unwanted - so instead here we elide an `any` branch from the constraint type,
		// in effect treating `any` like `never` rather than `unknown` in this location.
		trueConstraint := c.getInferredTrueTypeFromConditionalType(t)
		falseConstraint := c.getFalseTypeFromConditionalType(t)
		switch {
		case IsTypeAny(trueConstraint):
			d.resolvedDefaultConstraint = falseConstraint
		case IsTypeAny(falseConstraint):
			d.resolvedDefaultConstraint = trueConstraint
		default:
			d.resolvedDefaultConstraint = c.getUnionType([]*Type{trueConstraint, falseConstraint})
		}
	}
	return d.resolvedDefaultConstraint
}

func (c *Checker) getConstraintOfDistributiveConditionalType(t *Type) *Type {
	d := t.AsConditionalType()
	if d.resolvedConstraintOfDistributive == nil {
		// Check if we have a conditional type of the form 'T extends U ? X : Y', where T is a constrained
		// type parameter. If so, create an instantiation of the conditional type where T is replaced
		// with its constraint. We do this because if the constraint is a union type it will be distributed
		// over the conditional type and possibly reduced. For example, 'T extends undefined ? never : T'
		// removes 'undefined' from T.
		// We skip returning a distributive constraint for a restrictive instantiation of a conditional type
		// as the constraint for all type params (check type included) have been replace with `unknown`, which
		// is going to produce even more false positive/negative results than the distribute constraint already does.
		// Please note: the distributive constraint is a kludge for emulating what a negated type could to do filter
		// a union - once negated types exist and are applied to the conditional false branch, this "constraint"
		// likely doesn't need to exist.
		if d.root.isDistributive && c.cachedTypes[CachedTypeKey{kind: CachedTypeKindRestrictiveInstantiation, typeId: t.id}] != t {
			constraint := c.getSimplifiedType(d.checkType, false /*writing*/)
			if constraint == d.checkType {
				constraint = c.getConstraintOfType(constraint)
			}
			if constraint != nil && constraint != d.checkType {
				instantiated := c.getConditionalTypeInstantiation(t, prependTypeMapping(d.root.checkType, constraint, d.mapper), true /*forConstraint*/, nil)
				if instantiated.flags&TypeFlagsNever == 0 {
					d.resolvedConstraintOfDistributive = instantiated
					return instantiated
				}
			}
		}
		d.resolvedConstraintOfDistributive = c.noConstraintType
	}
	if d.resolvedConstraintOfDistributive != c.noConstraintType {
		return d.resolvedConstraintOfDistributive
	}
	return nil
}

func (c *Checker) getDeclaredTypeOfClassOrInterface(symbol *ast.Symbol) *Type {
	links := c.declaredTypeLinks.Get(symbol)
	if links.declaredType == nil {
		kind := core.IfElse(symbol.Flags&ast.SymbolFlagsClass != 0, ObjectFlagsClass, ObjectFlagsInterface)
		t := c.newObjectType(kind, symbol)
		links.declaredType = t
		outerTypeParameters := c.getOuterTypeParametersOfClassOrInterface(symbol)
		typeParameters := c.appendLocalTypeParametersOfClassOrInterfaceOrTypeAlias(outerTypeParameters, symbol)
		// A class or interface is generic if it has type parameters or a "this" type. We always give classes a "this" type
		// because it is not feasible to analyze all members to determine if the "this" type escapes the class (in particular,
		// property types inferred from initializers and method return types inferred from return statements are very hard
		// to exhaustively analyze). We give interfaces a "this" type if we can't definitely determine that they are free of
		// "this" references.
		if typeParameters != nil || kind == ObjectFlagsClass || !c.isThislessInterface(symbol) {
			t.objectFlags |= ObjectFlagsReference
			d := t.AsInterfaceType()
			d.thisType = c.newTypeParameter(symbol)
			d.thisType.AsTypeParameter().isThisType = true
			d.thisType.AsTypeParameter().constraint = t
			d.allTypeParameters = append(typeParameters, d.thisType)
			d.outerTypeParameterCount = len(outerTypeParameters)
			d.resolvedTypeArguments = d.TypeParameters()
			d.instantiations = make(map[CacheHashKey]*Type)
			d.instantiations[getTypeListKey(d.resolvedTypeArguments)] = t
			d.target = t
		}
	}
	return links.declaredType
}

/**
 * Returns true if the interface given by the symbol is free of "this" references.
 *
 * Specifically, the result is true if the interface itself contains no references
 * to "this" in its body, if all base types are interfaces,
 * and if none of the base interfaces have a "this" type.
 */
func (c *Checker) isThislessInterface(symbol *ast.Symbol) bool {
	for _, declaration := range symbol.Declarations {
		if ast.IsInterfaceDeclaration(declaration) {
			if declaration.Flags&ast.NodeFlagsContainsThis != 0 {
				return false
			}
			baseTypeNodes := ast.GetExtendsHeritageClauseElements(declaration)
			for _, node := range baseTypeNodes {
				if ast.IsEntityNameExpression(node.Expression()) {
					baseSymbol := c.resolveEntityName(node.Expression(), ast.SymbolFlagsType, true /*ignoreErrors*/, false, nil)
					if baseSymbol == nil || baseSymbol.Flags&ast.SymbolFlagsInterface == 0 || c.getDeclaredTypeOfClassOrInterface(baseSymbol).AsInterfaceType().thisType != nil {
						return false
					}
				}
			}
		}
	}
	return true
}

func hashWrite32[T ~int32 | ~uint32](h *xxh3.Hasher, value T) {
	v := uint32(value)
	_, _ = h.Write([]byte{
		byte(v),
		byte(v >> 8),
		byte(v >> 16),
		byte(v >> 24),
	})
}

func hashWrite64[T ~int | ~uint | ~int64 | ~uint64](h *xxh3.Hasher, value T) {
	v := uint64(value)
	_, _ = h.Write([]byte{
		byte(v),
		byte(v >> 8),
		byte(v >> 16),
		byte(v >> 24),
		byte(v >> 32),
		byte(v >> 40),
		byte(v >> 48),
		byte(v >> 56),
	})
}

type CacheHashKey xxh3.Uint128

func (k CacheHashKey) IsZero() bool {
	return xxh3.Uint128(k) == xxh3.Uint128{}
}

type keyBuilder struct {
	h xxh3.Hasher
}

func (b *keyBuilder) hash() CacheHashKey {
	return CacheHashKey(b.h.Sum128())
}

func (b *keyBuilder) writeByte(c byte) {
	_, _ = b.h.Write([]byte{c})
}

func (b *keyBuilder) writeString(s string) {
	_, _ = b.h.WriteString(s)
}

func (b *keyBuilder) writeInt(value int) {
	hashWrite64(&b.h, value)
}

func (b *keyBuilder) writeSymbol(s *ast.Symbol) {
	hashWrite64(&b.h, ast.GetSymbolId(s))
}

func (b *keyBuilder) writeType(t *Type) {
	hashWrite32(&b.h, t.id)
}

func (b *keyBuilder) writeTypes(types []*Type) {
	b.writeInt(len(types))
	for _, t := range types {
		b.writeType(t)
	}
}

func (b *keyBuilder) writeAlias(alias *TypeAlias) {
	if alias != nil {
		b.writeByte(1)
		b.writeSymbol(alias.symbol)
		b.writeTypes(alias.typeArguments)
	} else {
		b.writeByte(0)
	}
}

func (b *keyBuilder) writeGenericTypeReferences(source *Type, target *Type, ignoreConstraints bool) bool {
	var constrained bool
	typeParameters := make([]*Type, 0, 8)
	var writeTypeReference func(*Type, int)
	writeTypeReference = func(ref *Type, depth int) {
		b.writeType(ref.Target())
		for _, t := range ref.AsTypeReference().resolvedTypeArguments {
			if t.flags&TypeFlagsTypeParameter != 0 {
				if ignoreConstraints || t.checker.getConstraintOfTypeParameter(t) == nil {
					index := slices.Index(typeParameters, t)
					if index < 0 {
						index = len(typeParameters)
						typeParameters = append(typeParameters, t)
					}
					b.writeByte('=')
					b.writeInt(index)
					continue
				}
				constrained = true
			} else if depth < 4 && isTypeReferenceWithGenericArguments(t) {
				b.writeByte('<')
				writeTypeReference(t, depth+1)
				b.writeByte('>')
				continue
			}
			b.writeByte('-')
			b.writeType(t)
		}
	}
	writeTypeReference(source, 0)
	b.writeByte(',')
	writeTypeReference(target, 0)
	return constrained
}

func (b *keyBuilder) writeNodeId(id ast.NodeId) {
	hashWrite64(&b.h, id)
}

func (b *keyBuilder) writeNode(node *ast.Node) {
	if node != nil {
		b.writeNodeId(ast.GetNodeId(node))
	}
}

func getTypeListKey(types []*Type) CacheHashKey {
	var b keyBuilder
	b.writeTypes(types)
	return b.hash()
}

func getAliasKey(alias *TypeAlias) CacheHashKey {
	var b keyBuilder
	b.writeAlias(alias)
	return b.hash()
}

func getUnionKey(types []*Type, origin *Type, alias *TypeAlias) CacheHashKey {
	var b keyBuilder
	switch {
	case origin == nil:
		b.writeTypes(types)
	case origin.flags&TypeFlagsUnion != 0:
		b.writeByte('|')
		b.writeTypes(origin.Types())
	case origin.flags&TypeFlagsIntersection != 0:
		b.writeByte('&')
		b.writeTypes(origin.Types())
	case origin.flags&TypeFlagsIndex != 0:
		// origin type id alone is insufficient, as `keyof x` may resolve to multiple WIP values while `x` is still resolving
		b.writeByte('#')
		b.writeType(origin)
		b.writeByte('|')
		b.writeTypes(types)
	default:
		panic("Unhandled case in getUnionKey")
	}
	b.writeAlias(alias)
	return b.hash()
}

func getIntersectionKey(types []*Type, flags IntersectionFlags, alias *TypeAlias) CacheHashKey {
	var b keyBuilder
	b.writeTypes(types)
	if flags&IntersectionFlagsNoConstraintReduction == 0 {
		b.writeAlias(alias)
	} else {
		b.writeByte('*')
	}
	return b.hash()
}

func getTupleKey(elementInfos []TupleElementInfo, readonly bool) CacheHashKey {
	var b keyBuilder
	for _, e := range elementInfos {
		switch {
		case e.flags&ElementFlagsRequired != 0:
			b.writeByte('#')
		case e.flags&ElementFlagsOptional != 0:
			b.writeByte('?')
		case e.flags&ElementFlagsRest != 0:
			b.writeByte('.')
		default:
			b.writeByte('*')
		}
		if e.labeledDeclaration != nil {
			b.writeNode(e.labeledDeclaration)
		}
	}
	if readonly {
		b.writeByte('!')
	}
	return b.hash()
}

func getTypeAliasInstantiationKey(typeArguments []*Type, alias *TypeAlias) CacheHashKey {
	return getTypeInstantiationKey(typeArguments, alias, false)
}

func getTypeInstantiationKey(typeArguments []*Type, alias *TypeAlias, singleSignature bool) CacheHashKey {
	var b keyBuilder
	b.writeTypes(typeArguments)
	b.writeAlias(alias)
	if singleSignature {
		b.writeByte('!')
	}
	return b.hash()
}

func getIndexedAccessKey(objectType *Type, indexType *Type, accessFlags AccessFlags, alias *TypeAlias) CacheHashKey {
	var b keyBuilder
	b.writeType(objectType)
	b.writeType(indexType)
	hashWrite32(&b.h, accessFlags)
	b.writeAlias(alias)
	return b.hash()
}

func getTemplateTypeKey(texts []string, types []*Type) CacheHashKey {
	var b keyBuilder
	b.writeTypes(types)
	b.writeByte('|')
	for _, s := range texts {
		b.writeInt(len(s))
	}
	b.writeByte('|')
	for _, s := range texts {
		b.writeString(s)
	}
	return b.hash()
}

func getConditionalTypeKey(typeArguments []*Type, alias *TypeAlias, forConstraint bool) CacheHashKey {
	var b keyBuilder
	b.writeTypes(typeArguments)
	b.writeAlias(alias)
	if forConstraint {
		b.writeByte('!')
	}
	return b.hash()
}

func getRelationKey(source *Type, target *Type, intersectionState IntersectionState, isIdentity bool, ignoreConstraints bool) (CacheHashKey, bool) {
	if isIdentity && source.id > target.id {
		source, target = target, source
	}
	var b keyBuilder
	var constrained bool
	if isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target) {
		b.writeByte('g')
		constrained = b.writeGenericTypeReferences(source, target, ignoreConstraints)
	} else {
		b.writeByte('s')
		b.writeType(source)
		b.writeType(target)
	}
	hashWrite32(&b.h, intersectionState)
	return b.hash(), constrained
}

func getNodeListKey(nodes []*ast.Node) CacheHashKey {
	var b keyBuilder
	b.writeInt(len(nodes))
	for _, n := range nodes {
		b.writeNode(n)
	}
	return b.hash()
}

func isTypeReferenceWithGenericArguments(t *Type) bool {
	return isNonDeferredTypeReference(t) && core.Some(t.checker.getTypeArguments(t), func(t *Type) bool {
		return t.flags&TypeFlagsTypeParameter != 0 || isTypeReferenceWithGenericArguments(t)
	})
}

func isNonDeferredTypeReference(t *Type) bool {
	return t.objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node == nil
}

// Return true if type parameter originates in an unconstrained declaration in a type parameter list
func isUnconstrainedTypeParameter(tp *Type) bool {
	target := tp.Target()
	if target == nil {
		target = tp
	}
	if target.symbol == nil {
		return false
	}
	for _, d := range target.symbol.Declarations {
		if ast.IsTypeParameterDeclaration(d) && (d.AsTypeParameterDeclaration().Constraint != nil || ast.IsMappedTypeNode(d.Parent) || ast.IsInferTypeNode(d.Parent)) {
			return false
		}
	}
	return true
}

func (c *Checker) isNullOrUndefined(node *ast.Node) bool {
	expr := ast.SkipParentheses(node)
	switch expr.Kind {
	case ast.KindNullKeyword:
		return true
	case ast.KindIdentifier:
		return c.getResolvedSymbol(expr) == c.undefinedSymbol
	}
	return false
}

func (c *Checker) checkRightHandSideOfForOf(statement *ast.Node) *Type {
	use := core.IfElse(statement.AsForInOrOfStatement().AwaitModifier != nil, IterationUseForAwaitOf, IterationUseForOf)
	return c.checkIteratedTypeOrElementType(use, c.checkNonNullExpression(statement.Expression()), c.undefinedType, statement.Expression())
}

// Return the inferred type for a binding element
func (c *Checker) getTypeForBindingElement(declaration *ast.Node) *Type {
	checkMode := core.IfElse(hasDotDotDotToken(declaration), CheckModeRestBindingElement, CheckModeNormal)
	parentType := c.getTypeForBindingElementParent(declaration.Parent.Parent, checkMode)
	if parentType != nil {
		return c.getBindingElementTypeFromParentType(declaration, parentType, false /*noTupleBoundsCheck*/)
	}
	return nil
}

// Return the type of a binding element parent. We check SymbolLinks first to see if a type has been
// assigned by contextual typing.
func (c *Checker) getTypeForBindingElementParent(node *ast.Node, checkMode CheckMode) *Type {
	if checkMode == CheckModeNormal {
		// We can use a cached resolved type if no optionality was included in that type.
		if symbol := c.getSymbolOfDeclaration(node); symbol != nil {
			if resolvedType := c.valueSymbolLinks.Get(symbol).resolvedType; resolvedType != nil && !(c.strictNullChecks && isOptionalDeclaration(node)) {
				return resolvedType
			}
		}
	}
	return c.getTypeForVariableLikeDeclaration(node, false /*includeOptionality*/, checkMode)
}

func (c *Checker) getBindingElementTypeFromParentType(declaration *ast.Node, parentType *Type, noTupleBoundsCheck bool) *Type {
	// If an any type was inferred for parent, infer that for the binding element
	if IsTypeAny(parentType) {
		return parentType
	}
	pattern := declaration.Parent
	// Relax null check on ambient destructuring parameters, since the parameters have no implementation and are just documentation
	if c.strictNullChecks && declaration.Flags&ast.NodeFlagsAmbient != 0 && ast.IsPartOfParameterDeclaration(declaration) {
		parentType = c.GetNonNullableType(parentType)
	} else if c.strictNullChecks && pattern.Parent.Initializer() != nil && !c.hasTypeFacts(c.getTypeOfInitializer(pattern.Parent.Initializer()), TypeFactsEQUndefined) {
		parentType = c.getTypeWithFacts(parentType, TypeFactsNEUndefined)
	}
	accessFlags := AccessFlagsExpressionPosition | core.IfElse(noTupleBoundsCheck || c.hasDefaultValue(declaration), AccessFlagsAllowMissing, 0)
	var t *Type
	switch pattern.Kind {
	case ast.KindObjectBindingPattern:
		if hasDotDotDotToken(declaration) {
			parentType = c.getReducedType(parentType)
			if parentType.flags&TypeFlagsUnknown != 0 || !c.isValidSpreadType(parentType) {
				c.error(declaration, diagnostics.Rest_types_may_only_be_created_from_object_types)
				return c.errorType
			}
			elements := pattern.Elements()
			literalMembers := make([]*ast.Node, 0, len(elements))
			for _, element := range elements {
				if !hasDotDotDotToken(element) {
					name := element.PropertyNameOrName()
					literalMembers = append(literalMembers, name)
				}
			}
			t = c.getRestType(parentType, literalMembers, declaration.Symbol())
		} else {
			// Use explicitly specified property name ({ p: xxx } form), or otherwise the implied name ({ p } form)
			name := declaration.PropertyNameOrName()
			indexType := c.getLiteralTypeFromPropertyName(name)
			declaredType := c.getIndexedAccessTypeEx(parentType, indexType, accessFlags, name, nil)
			t = c.getFlowTypeOfDestructuring(declaration, declaredType)
		}
	case ast.KindArrayBindingPattern:
		// This elementType will be used if the specific property corresponding to this index is not
		// present (aka the tuple element property). This call also checks that the parentType is in
		// fact an iterable or array (depending on target language).
		elementType := c.checkIteratedTypeOrElementType(IterationUseDestructuring|core.IfElse(hasDotDotDotToken(declaration), 0, IterationUsePossiblyOutOfBounds), parentType, c.undefinedType, pattern)
		index := slices.Index(pattern.Elements(), declaration)
		if hasDotDotDotToken(declaration) {
			// If the parent is a tuple type, the rest element has a tuple type of the
			// remaining tuple element types. Otherwise, the rest element has an array type with same
			// element type as the parent type.
			baseConstraint := c.mapType(parentType, func(t *Type) *Type {
				if t.flags&TypeFlagsInstantiableNonPrimitive != 0 {
					return c.getBaseConstraintOrType(t)
				}
				return t
			})
			if everyType(baseConstraint, isTupleType) {
				t = c.mapType(baseConstraint, func(t *Type) *Type {
					return c.sliceTupleType(t, index, 0)
				})
			} else {
				t = c.createArrayType(elementType)
			}
		} else if c.isArrayLikeType(parentType) {
			indexType := c.getNumberLiteralType(jsnum.Number(index))
			declaredType := core.OrElse(c.getIndexedAccessTypeOrUndefined(parentType, indexType, accessFlags, declaration.Name(), nil), c.errorType)
			t = c.getFlowTypeOfDestructuring(declaration, declaredType)
		} else {
			t = elementType
		}
	default:
		panic("Unhandled case in getBindingElementTypeFromParentType")
	}
	if declaration.Initializer() == nil {
		return t
	}
	if ast.WalkUpBindingElementsAndPatterns(declaration).Type() != nil {
		// In strict null checking mode, if a default value of a non-undefined type is specified, remove
		// undefined from the final type.
		if c.strictNullChecks && !c.hasTypeFacts(c.checkDeclarationInitializer(declaration, CheckModeNormal, nil), TypeFactsIsUndefined) {
			return c.getNonUndefinedType(t)
		}
		return t
	}
	return c.widenTypeInferredFromInitializer(declaration, c.getUnionTypeEx([]*Type{c.getNonUndefinedType(t), c.checkDeclarationInitializer(declaration, CheckModeNormal, nil)}, UnionReductionSubtype, nil, nil))
}

func (c *Checker) getRestType(source *Type, properties []*ast.Node, symbol *ast.Symbol) *Type {
	source = c.filterType(source, func(t *Type) bool { return t.flags&TypeFlagsNullable == 0 })
	if source.flags&TypeFlagsNever != 0 {
		return c.emptyObjectType
	}
	if source.flags&TypeFlagsUnion != 0 {
		return c.mapType(source, func(t *Type) *Type {
			return c.getRestType(t, properties, symbol)
		})
	}
	omitKeyType := c.getUnionType(core.Map(properties, c.getLiteralTypeFromPropertyName))
	var spreadableProperties []*ast.Symbol
	var unspreadableToRestKeys []*Type
	for _, prop := range c.getPropertiesOfType(source) {
		literalTypeFromProperty := c.getLiteralTypeFromProperty(prop, TypeFlagsStringOrNumberLiteralOrUnique, false)
		if !c.isTypeAssignableTo(literalTypeFromProperty, omitKeyType) && getDeclarationModifierFlagsFromSymbol(prop)&(ast.ModifierFlagsPrivate|ast.ModifierFlagsProtected) == 0 && c.isSpreadableProperty(prop) {
			spreadableProperties = append(spreadableProperties, prop)
		} else {
			unspreadableToRestKeys = append(unspreadableToRestKeys, literalTypeFromProperty)
		}
	}
	if c.isGenericObjectType(source) || c.isGenericIndexType(omitKeyType) {
		if len(unspreadableToRestKeys) != 0 {
			// If the type we're spreading from has properties that cannot
			// be spread into the rest type (e.g. getters, methods), ensure
			// they are explicitly omitted, as they would in the non-generic case.
			omitKeyType = c.getUnionType(append([]*Type{omitKeyType}, unspreadableToRestKeys...))
		}
		if omitKeyType.flags&TypeFlagsNever != 0 {
			return source
		}
		omitTypeAlias := c.getGlobalOmitSymbol()
		if omitTypeAlias == nil {
			return c.errorType
		}
		return c.getTypeAliasInstantiation(omitTypeAlias, []*Type{source, omitKeyType}, nil)
	}
	members := make(ast.SymbolTable)
	for _, prop := range spreadableProperties {
		members[prop.Name] = c.getSpreadSymbol(prop, false /*readonly*/)
	}
	result := c.newAnonymousType(symbol, members, nil, nil, c.getIndexInfosOfType(source))
	result.objectFlags |= ObjectFlagsObjectRestType
	return result
}

// Determine the control flow type associated with a destructuring declaration or assignment. The following
// forms of destructuring are possible:
//
//	let { x } = obj;  // BindingElement
//	let [ x ] = obj;  // BindingElement
//	{ x } = obj;      // ShorthandPropertyAssignment
//	{ x: v } = obj;   // PropertyAssignment
//	[ x ] = obj;      // Expression
//
// We construct a synthetic element access expression corresponding to 'obj.x' such that the control
// flow analyzer doesn't have to handle all the different syntactic forms.
func (c *Checker) getFlowTypeOfDestructuring(node *ast.Node, declaredType *Type) *Type {
	reference := c.getSyntheticElementAccess(node)
	if reference != nil {
		return c.getFlowTypeOfReference(reference, declaredType)
	}
	return declaredType
}

func (c *Checker) getSyntheticElementAccess(node *ast.Node) *ast.Node {
	parentAccess := c.getParentElementAccess(node)
	if parentAccess != nil && getFlowNodeOfNode(parentAccess) != nil {
		if propName, ok := c.getDestructuringPropertyName(node); ok {
			literal := c.factory.NewStringLiteral(propName, ast.TokenFlagsNone)
			literal.Loc = node.Loc
			lhsExpr := parentAccess
			if !ast.IsLeftHandSideExpression(parentAccess) {
				lhsExpr = c.factory.NewParenthesizedExpression(parentAccess)
				lhsExpr.Loc = node.Loc
			}
			result := c.factory.NewElementAccessExpression(lhsExpr, nil, literal, ast.NodeFlagsNone)
			result.Loc = node.Loc
			literal.Parent = result
			result.Parent = node
			if lhsExpr != parentAccess {
				lhsExpr.Parent = result
			}
			result.FlowNodeData().FlowNode = getFlowNodeOfNode(parentAccess)
			return result
		}
	}
	return nil
}

func (c *Checker) getParentElementAccess(node *ast.Node) *ast.Node {
	ancestor := node.Parent.Parent
	switch ancestor.Kind {
	case ast.KindBindingElement, ast.KindPropertyAssignment:
		return c.getSyntheticElementAccess(ancestor)
	case ast.KindArrayLiteralExpression:
		return c.getSyntheticElementAccess(node.Parent)
	case ast.KindVariableDeclaration:
		return ancestor.Initializer()
	case ast.KindBinaryExpression:
		return ancestor.AsBinaryExpression().Right
	}
	return nil
}

// Return the type implied by a binding pattern. This is the type implied purely by the binding pattern itself
// and without regard to its context (i.e. without regard any type annotation or initializer associated with the
// declaration in which the binding pattern is contained). For example, the implied type of [x, y] is [any, any]
// and the implied type of { x, y: z = 1 } is { x: any; y: number; }. The type implied by a binding pattern is
// used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring
// parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of
// the parameter.
func (c *Checker) getTypeFromBindingPattern(pattern *ast.Node, includePatternInType bool, reportErrors bool) *Type {
	if includePatternInType {
		c.contextualBindingPatterns = append(c.contextualBindingPatterns, pattern)
	}
	var result *Type
	if ast.IsObjectBindingPattern(pattern) {
		result = c.getTypeFromObjectBindingPattern(pattern, includePatternInType, reportErrors)
	} else {
		result = c.getTypeFromArrayBindingPattern(pattern, includePatternInType, reportErrors)
	}
	if includePatternInType {
		c.contextualBindingPatterns = c.contextualBindingPatterns[:len(c.contextualBindingPatterns)-1]
	}
	return result
}

// Return the type implied by an object binding pattern
func (c *Checker) getTypeFromObjectBindingPattern(pattern *ast.Node, includePatternInType bool, reportErrors bool) *Type {
	members := make(ast.SymbolTable)
	var stringIndexInfo *IndexInfo
	objectFlags := ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral
	for _, e := range pattern.Elements() {
		name := e.PropertyNameOrName()
		if hasDotDotDotToken(e) {
			stringIndexInfo = c.newIndexInfo(c.stringType, c.anyType, false /*isReadonly*/, nil, nil)
			continue
		}
		exprType := c.getLiteralTypeFromPropertyName(name)
		if !isTypeUsableAsPropertyName(exprType) {
			// do not include computed properties in the implied type
			objectFlags |= ObjectFlagsObjectLiteralPatternWithComputedProperties
			continue
		}
		text := getPropertyNameFromType(exprType)
		flags := ast.SymbolFlagsProperty | core.IfElse(e.Initializer() != nil, ast.SymbolFlagsOptional, 0)
		symbol := c.newSymbol(flags, text)
		c.valueSymbolLinks.Get(symbol).resolvedType = c.getTypeFromBindingElement(e, includePatternInType, reportErrors)
		members[symbol.Name] = symbol
	}
	var indexInfos []*IndexInfo
	if stringIndexInfo != nil {
		indexInfos = []*IndexInfo{stringIndexInfo}
	}
	result := c.newAnonymousType(nil, members, nil, nil, indexInfos)
	result.objectFlags |= objectFlags
	if includePatternInType {
		c.patternForType[result] = pattern
		result.objectFlags |= ObjectFlagsContainsObjectOrArrayLiteral
	}
	return result
}

// Return the type implied by an array binding pattern
func (c *Checker) getTypeFromArrayBindingPattern(pattern *ast.Node, includePatternInType bool, reportErrors bool) *Type {
	elements := pattern.Elements()
	lastElement := core.LastOrNil(elements)
	var restElement *ast.Node
	if lastElement != nil && ast.IsBindingElement(lastElement) && hasDotDotDotToken(lastElement) {
		restElement = lastElement
	}
	if len(elements) == 0 || len(elements) == 1 && restElement != nil {
		// TODO: remove ScriptTargetES2015
		if c.languageVersion >= core.ScriptTargetES2015 {
			return c.createIterableType(c.anyType)
		}
		return c.anyArrayType
	}
	minLength := core.FindLastIndex(elements, func(e *ast.Node) bool {
		return !(e == restElement || e.Name() == nil || c.hasDefaultValue(e))
	}) + 1
	elementTypes := make([]*Type, len(elements))
	elementInfos := make([]TupleElementInfo, len(elements))
	for i, e := range elements {
		var t *Type
		if e.Name() == nil {
			t = c.anyType
		} else {
			t = c.getTypeFromBindingElement(e, includePatternInType, reportErrors)
		}
		var flags ElementFlags
		if e == restElement {
			flags = ElementFlagsRest
		} else if i >= minLength {
			flags = ElementFlagsOptional
		} else {
			flags = ElementFlagsRequired
		}
		elementTypes[i] = t
		elementInfos[i] = TupleElementInfo{flags: flags}
	}
	result := c.createTupleTypeEx(elementTypes, elementInfos, false)
	if includePatternInType {
		result = c.cloneTypeReference(result)
		c.patternForType[result] = pattern
		result.objectFlags |= ObjectFlagsContainsObjectOrArrayLiteral
	}
	return result
}

// Return the type implied by a binding pattern element. This is the type of the initializer of the element if
// one is present. Otherwise, if the element is itself a binding pattern, it is the type implied by the binding
// pattern. Otherwise, it is the type any.
func (c *Checker) getTypeFromBindingElement(element *ast.Node, includePatternInType bool, reportErrors bool) *Type {
	if element.Initializer() != nil {
		// The type implied by a binding pattern is independent of context, so we check the initializer with no
		// contextual type or, if the element itself is a binding pattern, with the type implied by that binding
		// pattern.
		contextualType := c.unknownType
		if ast.IsBindingPattern(element.Name()) {
			contextualType = c.getTypeFromBindingPattern(element.Name(), true /*includePatternInType*/, false /*reportErrors*/)
		}
		return c.addOptionality(c.getWidenedLiteralTypeForInitializer(element, c.checkDeclarationInitializer(element, CheckModeNormal, contextualType)))
	}
	if ast.IsBindingPattern(element.Name()) {
		return c.getTypeFromBindingPattern(element.Name(), includePatternInType, reportErrors)
	}
	if reportErrors && !c.declarationBelongsToPrivateAmbientMember(element) {
		c.reportImplicitAny(element, c.anyType, WideningKindNormal)
	}
	// When we're including the pattern in the type (an indication we're obtaining a contextual type), we
	// use a non-inferrable any type. Inference will never directly infer this type, but it is possible
	// to infer a type that contains it, e.g. for a binding pattern like [foo] or { foo }. In such cases,
	// widening of the binding pattern type substitutes a regular any for the non-inferrable any.
	if includePatternInType {
		return c.nonInferrableAnyType
	}
	return c.anyType
}

func (c *Checker) declarationBelongsToPrivateAmbientMember(declaration *ast.Node) bool {
	memberDeclaration := ast.GetRootDeclaration(declaration)
	if ast.IsParameterDeclaration(memberDeclaration) {
		memberDeclaration = memberDeclaration.Parent
	}
	return isPrivateWithinAmbient(memberDeclaration)
}

func (c *Checker) getTypeOfPrototypeProperty(prototype *ast.Symbol) *Type {
	// TypeScript 1.0 spec (April 2014): 8.4
	// Every class automatically contains a static property member named 'prototype',
	// the type of which is an instantiation of the class type with type Any supplied as a type argument for each type parameter.
	// It is an error to explicitly declare a static property member with the name 'prototype'.
	classType := c.getDeclaredTypeOfSymbol(c.getParentOfSymbol(prototype))
	typeParameters := classType.AsInterfaceType().TypeParameters()
	if len(typeParameters) != 0 {
		return c.createTypeReference(classType, core.Map(typeParameters, func(*Type) *Type { return c.anyType }))
	}
	return classType
}

type thisAssignmentDeclarationKind int32

const (
	thisAssignmentDeclarationNone        thisAssignmentDeclarationKind = iota // not (all) this.property assignments
	thisAssignmentDeclarationTyped                                            // typed; use the type annotation
	thisAssignmentDeclarationConstructor                                      // at least one in the constructor; use control flow
	thisAssignmentDeclarationMethod                                           // methods only; look in base first, and if not found, union all declaration types plus undefined
)

func (c *Checker) getWidenedTypeForAssignmentDeclaration(symbol *ast.Symbol) *Type {
	var t *Type
	kind, location := c.isConstructorDeclaredThisProperty(symbol)
	switch kind {
	case thisAssignmentDeclarationTyped:
		if location == nil {
			panic("location should not be nil when this assignment has a type.")
		}
		t = c.getTypeFromTypeNode(location)
	case thisAssignmentDeclarationConstructor:
		if location == nil {
			panic("constructor should not be nil when this assignment is in a constructor.")
		}
		t = c.getFlowTypeInConstructor(symbol, location)
	case thisAssignmentDeclarationMethod:
		t = c.getTypeOfPropertyInBaseClass(symbol)
	}
	if t == nil {
		var types []*Type
		for i, declaration := range symbol.Declarations {
			if ast.IsBinaryExpression(declaration) && declaration.Type() != nil {
				t = c.getTypeFromTypeNode(declaration.Type())
				break
			}
			if assignedType := c.getAssignmentDeclarationInitializerType(declaration); assignedType != nil {
				// We ignore initial assignments of undefined to CommonJS exports when there are multiple assignment declarations
				if ast.GetAssignmentDeclarationKind(declaration) != ast.JSDeclarationKindExportsProperty || i != 0 || len(symbol.Declarations) == 1 || assignedType.flags&TypeFlagsUndefined == 0 {
					types = core.AppendIfUnique(types, assignedType)
				}
			}
		}
		if kind == thisAssignmentDeclarationMethod && len(types) > 0 {
			if c.strictNullChecks {
				types = core.AppendIfUnique(types, c.undefinedOrMissingType)
			}
		}
		if t == nil {
			t = c.anyType
			if len(types) != 0 {
				t = c.getUnionType(types)
			}
		}
	}
	t = c.getWidenedType(t)
	// report an all-nullable or empty union as an implicit any in JS files
	if symbol.ValueDeclaration != nil && ast.IsInJSFile(symbol.ValueDeclaration) &&
		c.filterType(t, func(c *Type) bool { return c.Flags() & ^TypeFlagsNullable != 0 }) == c.neverType {
		c.reportImplicitAny(symbol.ValueDeclaration, c.anyType, WideningKindNormal)
		return c.anyType
	}
	return t
}

func (c *Checker) getAssignmentDeclarationInitializerType(node *ast.Node) *Type {
	if ast.IsBinaryExpression(node) {
		var t *Type
		switch ast.GetAssignmentDeclarationKind(node) {
		case ast.JSDeclarationKindModuleExports, ast.JSDeclarationKindExportsProperty:
			t = c.getRegularTypeOfLiteralType(c.checkExpressionCached(ast.GetRightMostAssignedExpression(node)))
		case ast.JSDeclarationKindThisProperty:
			if c.containsSameNamedThisProperty(node.AsBinaryExpression().Left, node.AsBinaryExpression().Right) {
				return nil
			}
			fallthrough
		default:
			t = c.checkExpressionForMutableLocation(node.AsBinaryExpression().Right, CheckModeNormal)
		}
		if c.isEmptyArrayLiteralType(t) && !c.hasParentWithTypeAnnotation(node.Symbol()) {
			c.reportImplicitAny(node, c.anyArrayType, WideningKindNormal)
			return c.anyArrayType
		}
		return t
	}
	if ast.IsCallExpression(node) {
		return c.getTypeFromPropertyDescriptor(node.Arguments()[2])
	}
	return nil
}

// Return true if the parent symbol of the given assignment declaration symbol has declaration with a type
// annotation. For example, returns true for the symbol associated with `f.a` below:
//
//	const f: { (): void, a: string[] } = () => {};
//	f.a = [];
func (c *Checker) hasParentWithTypeAnnotation(symbol *ast.Symbol) bool {
	if symbol.Parent != nil && symbol.Parent.ValueDeclaration != nil && ast.IsFunctionExpressionOrArrowFunction(symbol.Parent.ValueDeclaration) {
		if possiblyAnnotatedSymbol := c.getSymbolOfNode(symbol.Parent.ValueDeclaration.Parent); possiblyAnnotatedSymbol != nil && possiblyAnnotatedSymbol.ValueDeclaration != nil {
			return possiblyAnnotatedSymbol.ValueDeclaration.Type() != nil
		}
	}
	return false
}

func (c *Checker) containsSameNamedThisProperty(thisProperty *ast.Node, expression *ast.Node) bool {
	var visit func(node *ast.Node) bool
	visit = func(node *ast.Node) bool {
		if c.isMatchingReference(thisProperty, node) {
			return true
		}
		if ast.IsFunctionLike(node) {
			return false
		}
		return node.ForEachChild(visit)
	}
	return visit(expression)
}

func (c *Checker) getTypeFromPropertyDescriptor(node *ast.Node) *Type {
	objectLiteralType := c.checkExpressionCached(node)
	if valueType := c.getTypeOfPropertyOfType(objectLiteralType, "value"); valueType != nil {
		return valueType
	}
	if getFunc := c.getTypeOfPropertyOfType(objectLiteralType, "get"); getFunc != nil {
		if getSig := c.getSingleCallSignature(getFunc); getSig != nil {
			return c.getReturnTypeOfSignature(getSig)
		}
	}
	if setFunc := c.getTypeOfPropertyOfType(objectLiteralType, "set"); setFunc != nil {
		if setSig := c.getSingleCallSignature(setFunc); setSig != nil {
			return c.getTypeOfFirstParameterOfSignature(setSig)
		}
	}
	return c.anyType
}

// A property is considered a constructor declared property when all declaration sites are this.xxx assignments,
// when no declaration sites have JSDoc type annotations, and when at least one declaration site is in the body of
// a class constructor.
func (c *Checker) isConstructorDeclaredThisProperty(symbol *ast.Symbol) (thisAssignmentDeclarationKind, *ast.Node) {
	if symbol.ValueDeclaration == nil || !ast.IsBinaryExpression(symbol.ValueDeclaration) {
		return thisAssignmentDeclarationNone, nil
	}
	if kind, ok := c.thisExpandoKinds[symbol]; ok {
		location, ok2 := c.thisExpandoLocations[symbol]
		if !ok2 {
			panic("location should be cached whenever this expando symbol is cached")
		}
		return kind, location
	}
	allThis := true
	var typeAnnotation *ast.Node
	for _, declaration := range symbol.Declarations {
		if !ast.IsBinaryExpression(declaration) {
			allThis = false
			break
		}
		bin := declaration.AsBinaryExpression()
		if ast.GetAssignmentDeclarationKind(declaration) == ast.JSDeclarationKindThisProperty &&
			(bin.Left.Kind != ast.KindElementAccessExpression || ast.IsStringOrNumericLiteralLike(bin.Left.AsElementAccessExpression().ArgumentExpression)) {
			if bin.Type != nil {
				typeAnnotation = bin.Type
			}
		} else {
			allThis = false
			break
		}
	}
	var location *ast.Node
	kind := thisAssignmentDeclarationNone
	if allThis {
		if typeAnnotation != nil {
			location = typeAnnotation
			kind = thisAssignmentDeclarationTyped
		} else {
			location = c.getDeclaringConstructor(symbol)
			kind = core.IfElse(location == nil, thisAssignmentDeclarationMethod, thisAssignmentDeclarationConstructor)
		}
	}
	c.thisExpandoKinds[symbol] = kind
	c.thisExpandoLocations[symbol] = location
	return kind, location
}

func (c *Checker) isGlobalSymbolConstructor(node *ast.Node) bool {
	symbol := c.getSymbolOfNode(node)
	globalSymbol := c.getGlobalESSymbolConstructorTypeSymbolOrNil()
	return globalSymbol != nil && symbol == globalSymbol
}

func (c *Checker) widenTypeForVariableLikeDeclaration(t *Type, declaration *ast.Node, reportErrors bool) *Type {
	if t != nil {
		// This special case is required for backwards compatibility with libraries that merge a `symbol` property into `SymbolConstructor`.
		// See https://github.com/microsoft/typescript-go/issues/1212
		if t.flags&TypeFlagsESSymbol != 0 && c.isGlobalSymbolConstructor(declaration.Parent) {
			t = c.getESSymbolLikeTypeForNode(declaration)
		}

		if reportErrors {
			c.reportErrorsFromWidening(declaration, t, WideningKindNormal)
		}

		// always widen a 'unique symbol' type if the type was created for a different declaration.
		if t.flags&TypeFlagsUniqueESSymbol != 0 && (ast.IsBindingElement(declaration) || declaration.Type() == nil) && t.symbol != c.getSymbolOfDeclaration(declaration) {
			t = c.esSymbolType
		}
		return c.getWidenedType(t)
	}
	// Rest parameters default to type any[], other parameters default to type any
	if ast.IsParameterDeclaration(declaration) && declaration.AsParameterDeclaration().DotDotDotToken != nil {
		t = c.anyArrayType
	} else {
		t = c.anyType
	}
	// Report implicit any errors unless this is a private property within an ambient declaration
	if reportErrors {
		if !declarationBelongsToPrivateAmbientMember(declaration) {
			c.reportImplicitAny(declaration, t, WideningKindNormal)
		}
	}
	return t
}

func (c *Checker) reportImplicitAny(declaration *ast.Node, t *Type, wideningKind WideningKind) {
	if ast.IsInJSFile(declaration) && !ast.IsCheckJSEnabledForFile(ast.GetSourceFileOfNode(declaration), c.compilerOptions) {
		// Only report implicit any errors/suggestions in TS and ts-check JS files
		return
	}
	typeAsString := c.TypeToString(c.getWidenedType(t))
	var diagnostic *diagnostics.Message
	switch declaration.Kind {
	case ast.KindBinaryExpression, ast.KindPropertyDeclaration, ast.KindPropertySignature:
		diagnostic = core.IfElse(c.noImplicitAny,
			diagnostics.Member_0_implicitly_has_an_1_type,
			diagnostics.Member_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage)
	case ast.KindParameter:
		param := declaration.AsParameterDeclaration()
		if ast.IsIdentifier(param.Name()) {
			name := param.Name().AsIdentifier()
			originalKeywordKind := scanner.IdentifierToKeywordKind(name)
			if (ast.IsCallSignatureDeclaration(declaration.Parent) || ast.IsMethodSignatureDeclaration(declaration.Parent) || ast.IsFunctionTypeNode(declaration.Parent)) &&
				slices.Contains(declaration.Parent.Parameters(), declaration) &&
				(ast.IsTypeNodeKind(originalKeywordKind) || c.resolveName(declaration, name.Text, ast.SymbolFlagsType, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/) != nil) {
				newName := fmt.Sprintf("arg%v", slices.Index(declaration.Parent.Parameters(), declaration))
				typeName := scanner.DeclarationNameToString(param.Name()) + core.IfElse(param.DotDotDotToken != nil, "[]", "")
				c.errorOrSuggestion(c.noImplicitAny, declaration, diagnostics.Parameter_has_a_name_but_no_type_Did_you_mean_0_Colon_1, newName, typeName)
				return
			}
		}
		switch {
		case param.DotDotDotToken != nil:
			if c.noImplicitAny {
				diagnostic = diagnostics.Rest_parameter_0_implicitly_has_an_any_type
			} else {
				diagnostic = diagnostics.Rest_parameter_0_implicitly_has_an_any_type_but_a_better_type_may_be_inferred_from_usage
			}
		case c.noImplicitAny:
			diagnostic = diagnostics.Parameter_0_implicitly_has_an_1_type
		default:
			diagnostic = diagnostics.Parameter_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage
		}
	case ast.KindBindingElement:
		diagnostic = diagnostics.Binding_element_0_implicitly_has_an_1_type
		if !c.noImplicitAny {
			// Don't issue a suggestion for binding elements since the codefix doesn't yet support them.
			return
		}
	case ast.KindFunctionDeclaration, ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindGetAccessor,
		ast.KindSetAccessor, ast.KindFunctionExpression, ast.KindArrowFunction:
		if c.noImplicitAny && declaration.Name() == nil {
			if wideningKind == WideningKindGeneratorYield {
				c.error(declaration, diagnostics.Generator_implicitly_has_yield_type_0_Consider_supplying_a_return_type_annotation, typeAsString)
			} else {
				c.error(declaration, diagnostics.Function_expression_which_lacks_return_type_annotation_implicitly_has_an_0_return_type, typeAsString)
			}
			return
		}
		switch {
		case !c.noImplicitAny:
			diagnostic = diagnostics.X_0_implicitly_has_an_1_return_type_but_a_better_type_may_be_inferred_from_usage
		case declaration.Flags&ast.NodeFlagsReparsed != 0:
			c.error(declaration, diagnostics.This_overload_implicitly_returns_the_type_0_because_it_lacks_a_return_type_annotation, typeAsString)
			return
		case wideningKind == WideningKindGeneratorYield:
			diagnostic = diagnostics.X_0_which_lacks_return_type_annotation_implicitly_has_an_1_yield_type
		default:
			diagnostic = diagnostics.X_0_which_lacks_return_type_annotation_implicitly_has_an_1_return_type
		}
	case ast.KindMappedType:
		if c.noImplicitAny {
			c.error(declaration, diagnostics.Mapped_object_type_implicitly_has_an_any_template_type)
		}
		return
	default:
		if c.noImplicitAny {
			diagnostic = diagnostics.Variable_0_implicitly_has_an_1_type
		} else {
			diagnostic = diagnostics.Variable_0_implicitly_has_an_1_type_but_a_better_type_may_be_inferred_from_usage
		}
	}
	c.errorOrSuggestion(c.noImplicitAny, declaration, diagnostic, scanner.DeclarationNameToString(ast.GetNameOfDeclaration(declaration)), typeAsString)
}

func (c *Checker) getWidenedType(t *Type) *Type {
	return c.getWidenedTypeWithContext(t, nil /*context*/)
}

func (c *Checker) getWidenedTypeWithContext(t *Type, context *WideningContext) *Type {
	if t.objectFlags&ObjectFlagsRequiresWidening != 0 {
		if context == nil {
			if cached := c.cachedTypes[CachedTypeKey{kind: CachedTypeKindWidened, typeId: t.id}]; cached != nil {
				return cached
			}
		}
		var result *Type
		switch {
		case t.flags&(TypeFlagsAny|TypeFlagsNullable) != 0:
			result = c.anyType
		case isObjectLiteralType(t):
			result = c.getWidenedTypeOfObjectLiteral(t, context)
		case t.flags&TypeFlagsUnion != 0:
			unionContext := context
			if unionContext == nil {
				unionContext = &WideningContext{siblings: t.Types()}
			}
			widenedTypes := core.SameMap(t.Types(), func(t *Type) *Type {
				if t.flags&TypeFlagsNullable != 0 {
					return t
				}
				return c.getWidenedTypeWithContext(t, unionContext)
			})
			// Widening an empty object literal transitions from a highly restrictive type to
			// a highly inclusive one. For that reason we perform subtype reduction here if the
			// union includes empty object types (e.g. reducing {} | string to just {}).
			result = c.getUnionTypeEx(widenedTypes, core.IfElse(core.Some(widenedTypes, c.isEmptyObjectType), UnionReductionSubtype, UnionReductionLiteral), nil, nil)
		case t.flags&TypeFlagsIntersection != 0:
			result = c.getIntersectionType(core.SameMap(t.Types(), c.getWidenedType))
		case c.isArrayOrTupleType(t):
			result = c.createTypeReference(t.Target(), core.SameMap(c.getTypeArguments(t), c.getWidenedType))
		}
		if result != nil && context == nil {
			c.cachedTypes[CachedTypeKey{kind: CachedTypeKindWidened, typeId: t.id}] = result
		}
		return core.OrElse(result, t)
	}
	return t
}

func (c *Checker) getWidenedTypeOfObjectLiteral(t *Type, context *WideningContext) *Type {
	if context != nil {
		if cached := context.widenedTypes[t]; cached != nil {
			return cached
		}
	}
	members := make(ast.SymbolTable)
	for _, prop := range c.getPropertiesOfObjectType(t) {
		members[prop.Name] = c.getWidenedProperty(prop, context)
	}
	if context != nil {
		for _, prop := range c.getPropertiesOfContext(context) {
			if _, ok := members[prop.Name]; !ok {
				members[prop.Name] = c.getUndefinedProperty(prop)
			}
		}
	}
	result := c.newAnonymousType(t.symbol, members, nil, nil, core.SameMap(c.getIndexInfosOfType(t), func(info *IndexInfo) *IndexInfo {
		return c.newIndexInfo(info.keyType, c.getWidenedType(info.valueType), info.isReadonly, info.declaration, info.components)
	}))
	// Retain js literal flag through widening
	result.objectFlags |= t.objectFlags & (ObjectFlagsJSLiteral | ObjectFlagsNonInferrableType)
	// Only cache in child contexts since the root context never widens a particular object literal type more than once
	if context != nil && context.parent != nil {
		if context.widenedTypes == nil {
			context.widenedTypes = make(map[*Type]*Type)
		}
		context.widenedTypes[t] = result
	}
	return result
}

func (c *Checker) getWidenedProperty(prop *ast.Symbol, context *WideningContext) *ast.Symbol {
	if prop.Flags&ast.SymbolFlagsProperty == 0 {
		// Since get accessors already widen their return value there is no need to
		// widen accessor based properties here.
		return prop
	}
	original := c.getTypeOfSymbol(prop)
	var propContext *WideningContext
	if context != nil {
		propContext = context.getChildContext(prop.Name)
	}
	widened := c.getWidenedTypeWithContext(original, propContext)
	if widened == original {
		return prop
	}
	return c.createSymbolWithType(prop, widened)
}

func (w *WideningContext) getChildContext(propertyName string) *WideningContext {
	if cached := w.childContexts[propertyName]; cached != nil {
		return cached
	}
	result := &WideningContext{parent: w, propertyName: propertyName}
	if w.childContexts == nil {
		w.childContexts = make(map[string]*WideningContext)
	}
	w.childContexts[propertyName] = result
	return result
}

func (c *Checker) getPropertiesOfContext(context *WideningContext) []*ast.Symbol {
	if context.resolvedProperties == nil {
		var names collections.OrderedMap[string, *ast.Symbol]
		for _, t := range c.getSiblingsOfContext(context) {
			if isObjectLiteralType(t) && t.objectFlags&ObjectFlagsContainsSpread == 0 {
				for _, prop := range c.getPropertiesOfType(t) {
					names.Set(prop.Name, prop)
				}
			}
		}
		context.resolvedProperties = slices.Collect(names.Values())
	}
	return context.resolvedProperties
}

func (c *Checker) getSiblingsOfContext(context *WideningContext) []*Type {
	if context.siblings == nil {
		siblings := []*Type{}
		for _, t := range c.getSiblingsOfContext(context.parent) {
			if isObjectLiteralType(t) {
				prop := c.getPropertyOfObjectType(t, context.propertyName)
				if prop != nil {
					siblings = append(siblings, c.getTypeOfSymbol(prop).Distributed()...)
				}
			}
		}
		context.siblings = siblings
	}
	return context.siblings
}

func (c *Checker) getUndefinedProperty(prop *ast.Symbol) *ast.Symbol {
	if cached := c.undefinedProperties[prop.Name]; cached != nil {
		return cached
	}
	result := c.createSymbolWithType(prop, c.undefinedOrMissingType)
	result.Flags |= ast.SymbolFlagsOptional
	c.undefinedProperties[prop.Name] = result
	return result
}

func (c *Checker) getTypeOfEnumMember(symbol *ast.Symbol) *Type {
	links := c.valueSymbolLinks.Get(symbol)
	if links.resolvedType == nil {
		links.resolvedType = c.getDeclaredTypeOfEnumMember(symbol)
	}
	return links.resolvedType
}

func (c *Checker) getTypeOfAccessors(symbol *ast.Symbol) *Type {
	links := c.valueSymbolLinks.Get(symbol)
	if links.resolvedType == nil {
		if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) {
			return c.errorType
		}
		getter := ast.GetDeclarationOfKind(symbol, ast.KindGetAccessor)
		setter := ast.GetDeclarationOfKind(symbol, ast.KindSetAccessor)
		accessor := core.Find(symbol.Declarations, ast.IsAutoAccessorPropertyDeclaration)
		// We try to resolve a getter type annotation, a setter type annotation, or a getter function
		// body return type inference, in that order.
		t := c.getAnnotatedAccessorType(getter)
		if t == nil {
			t = c.getAnnotatedAccessorType(setter)
		}
		if t == nil {
			t = c.getAnnotatedAccessorType(accessor)
		}
		if t == nil && getter != nil {
			if body := getter.Body(); body != nil {
				t = c.getReturnTypeFromBody(getter, CheckModeNormal)
			}
		}
		if t == nil && accessor != nil {
			t = c.getWidenedTypeForVariableLikeDeclaration(accessor, true /*reportErrors*/)
		}
		if t == nil {
			if setter != nil && !isPrivateWithinAmbient(setter) {
				c.errorOrSuggestion(c.noImplicitAny, setter, diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation, c.symbolToString(symbol))
			} else if getter != nil && !isPrivateWithinAmbient(getter) {
				c.errorOrSuggestion(c.noImplicitAny, getter, diagnostics.Property_0_implicitly_has_type_any_because_its_get_accessor_lacks_a_return_type_annotation, c.symbolToString(symbol))
			} else if accessor != nil && !isPrivateWithinAmbient(accessor) {
				c.errorOrSuggestion(c.noImplicitAny, accessor, diagnostics.Member_0_implicitly_has_an_1_type, c.symbolToString(symbol), "any")
			}
			t = c.anyType
		}
		if !c.popTypeResolution() {
			if c.getAnnotatedAccessorTypeNode(getter) != nil {
				c.error(getter, diagnostics.X_0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, c.symbolToString(symbol))
			} else if c.getAnnotatedAccessorTypeNode(setter) != nil {
				c.error(setter, diagnostics.X_0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, c.symbolToString(symbol))
			} else if c.getAnnotatedAccessorTypeNode(accessor) != nil {
				c.error(setter, diagnostics.X_0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, c.symbolToString(symbol))
			} else if getter != nil && c.noImplicitAny {
				c.error(getter, diagnostics.X_0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, c.symbolToString(symbol))
			}
			t = c.anyType
		}
		if links.resolvedType == nil {
			links.resolvedType = t
		}
	}
	return links.resolvedType
}

func (c *Checker) getWriteTypeOfAccessors(symbol *ast.Symbol) *Type {
	links := c.valueSymbolLinks.Get(symbol)
	if links.writeType == nil {
		if !c.pushTypeResolution(symbol, TypeSystemPropertyNameWriteType) {
			return c.errorType
		}
		setter := ast.GetDeclarationOfKind(symbol, ast.KindSetAccessor)
		if setter == nil {
			propDeclaration := ast.GetDeclarationOfKind(symbol, ast.KindPropertyDeclaration)
			if propDeclaration != nil && ast.IsAutoAccessorPropertyDeclaration(propDeclaration) {
				setter = propDeclaration
			}
		}
		writeType := c.getAnnotatedAccessorType(setter)
		if !c.popTypeResolution() {
			if c.getAnnotatedAccessorTypeNode(setter) != nil {
				c.error(setter, diagnostics.X_0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, c.symbolToString(symbol))
			}
			writeType = c.anyType
		}
		// Absent an explicit setter type annotation we use the read type of the accessor.
		if links.writeType == nil {
			if writeType != nil {
				links.writeType = writeType
			} else {
				links.writeType = c.getTypeOfAccessors(symbol)
			}
		}
	}
	return links.writeType
}

func (c *Checker) getTypeOfAlias(symbol *ast.Symbol) *Type {
	links := c.valueSymbolLinks.Get(symbol)
	if links.resolvedType == nil {
		if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) {
			return c.errorType
		}
		targetSymbol := c.resolveAlias(symbol)
		exportSymbol := c.getTargetOfAliasDeclaration(c.getDeclarationOfAliasSymbol(symbol))
		// It only makes sense to get the type of a value symbol. If the result of resolving
		// the alias is not a value, then it has no type. To get the type associated with a
		// type symbol, call getDeclaredTypeOfSymbol.
		// This check is important because without it, a call to getTypeOfSymbol could end
		// up recursively calling getTypeOfAlias, causing a stack overflow.
		if links.resolvedType == nil {
			if c.getSymbolFlags(targetSymbol)&ast.SymbolFlagsValue != 0 {
				links.resolvedType = c.getTypeOfSymbol(targetSymbol)
			} else {
				links.resolvedType = c.errorType
			}
		}
		if !c.popTypeResolution() {
			c.reportCircularityError(core.OrElse(exportSymbol, symbol))
			if links.resolvedType == nil {
				links.resolvedType = c.errorType
			}
			return links.resolvedType
		}
	}
	return links.resolvedType
}

func (c *Checker) addOptionality(t *Type) *Type {
	return c.addOptionalityEx(t, false /*isProperty*/, true /*isOptional*/)
}

func (c *Checker) addOptionalityEx(t *Type, isProperty bool, isOptional bool) *Type {
	if c.strictNullChecks && isOptional {
		return c.getOptionalType(t, isProperty)
	}
	return t
}

func (c *Checker) getOptionalType(t *Type, isProperty bool) *Type {
	debug.Assert(c.strictNullChecks)
	missingOrUndefined := core.IfElse(isProperty, c.undefinedOrMissingType, c.undefinedType)
	if t == missingOrUndefined || t.flags&TypeFlagsUnion != 0 && t.Types()[0] == missingOrUndefined {
		return t
	}
	return c.getUnionType([]*Type{t, missingOrUndefined})
}

// Add undefined or null or both to a type if they are missing.
func (c *Checker) getNullableType(t *Type, flags TypeFlags) *Type {
	missing := (flags & ^t.flags) & (TypeFlagsUndefined | TypeFlagsNull)
	switch {
	case missing == 0:
		return t
	case missing == TypeFlagsUndefined:
		return c.getUnionType([]*Type{t, c.undefinedType})
	case missing == TypeFlagsNull:
		return c.getUnionType([]*Type{t, c.nullType})
	}
	return c.getUnionType([]*Type{t, c.undefinedType, c.nullType})
}

func (c *Checker) GetNonNullableType(t *Type) *Type {
	if c.strictNullChecks {
		return c.getAdjustedTypeWithFacts(t, TypeFactsNEUndefinedOrNull)
	}
	return t
}

func (c *Checker) IsNullableType(t *Type) bool {
	return c.hasTypeFacts(t, TypeFactsIsUndefinedOrNull)
}

func (c *Checker) getNonNullableTypeIfNeeded(t *Type) *Type {
	if c.IsNullableType(t) {
		return c.GetNonNullableType(t)
	}
	return t
}

func (c *Checker) getDeclarationNodeFlagsFromSymbol(s *ast.Symbol) ast.NodeFlags {
	if s.ValueDeclaration != nil {
		return c.getCombinedNodeFlagsCached(s.ValueDeclaration)
	}
	return ast.NodeFlagsNone
}

func (c *Checker) getCombinedNodeFlagsCached(node *ast.Node) ast.NodeFlags {
	// we hold onto the last node and result to speed up repeated lookups against the same node.
	if c.lastGetCombinedNodeFlagsNode == node {
		return c.lastGetCombinedNodeFlagsResult
	}
	c.lastGetCombinedNodeFlagsNode = node
	c.lastGetCombinedNodeFlagsResult = ast.GetCombinedNodeFlags(node)
	return c.lastGetCombinedNodeFlagsResult
}

func (c *Checker) isVarConstLike(node *ast.Node) bool {
	blockScopeKind := c.getCombinedNodeFlagsCached(node) & ast.NodeFlagsBlockScoped
	return blockScopeKind == ast.NodeFlagsConst || blockScopeKind == ast.NodeFlagsUsing || blockScopeKind == ast.NodeFlagsAwaitUsing
}

func (c *Checker) getEffectivePropertyNameForPropertyNameNode(node *ast.PropertyName) (string, bool) {
	name := ast.GetPropertyNameForPropertyNameNode(node)
	switch {
	case name != ast.InternalSymbolNameMissing:
		return name, true
	case ast.IsComputedPropertyName(node):
		return c.tryGetNameFromType(c.getTypeOfExpression(node.Expression()))
	}
	return "", false
}

func (c *Checker) tryGetNameFromType(t *Type) (name string, ok bool) {
	switch {
	case t.flags&TypeFlagsUniqueESSymbol != 0:
		return t.AsUniqueESSymbolType().name, true
	case t.flags&TypeFlagsStringLiteral != 0:
		s := getStringLiteralValue(t)
		return s, true
	case t.flags&TypeFlagsNumberLiteral != 0:
		s := getNumberLiteralValue(t).String()
		return s, true
	default:
		return "", false
	}
}

func (c *Checker) getCombinedModifierFlagsCached(node *ast.Node) ast.ModifierFlags {
	// we hold onto the last node and result to speed up repeated lookups against the same node.
	if c.lastGetCombinedModifierFlagsNode == node {
		return c.lastGetCombinedModifierFlagsResult
	}
	c.lastGetCombinedModifierFlagsNode = node
	c.lastGetCombinedModifierFlagsResult = ast.GetCombinedModifierFlags(node)
	return c.lastGetCombinedModifierFlagsResult
}

/**
 * Push an entry on the type resolution stack. If an entry with the given target and the given property name
 * is already on the stack, and no entries in between already have a type, then a circularity has occurred.
 * In this case, the result values of the existing entry and all entries pushed after it are changed to false,
 * and the value false is returned. Otherwise, the new entry is just pushed onto the stack, and true is returned.
 * In order to see if the same query has already been done before, the target object and the propertyName both
 * must match the one passed in.
 *
 * @param target The symbol, type, or signature whose type is being queried
 * @param propertyName The property name that should be used to query the target for its type
 */
func (c *Checker) pushTypeResolution(target TypeSystemEntity, propertyName TypeSystemPropertyName) bool {
	resolutionCycleStartIndex := c.findResolutionCycleStartIndex(target, propertyName)
	if resolutionCycleStartIndex >= 0 {
		// A cycle was found
		for i := resolutionCycleStartIndex; i < len(c.typeResolutions); i++ {
			c.typeResolutions[i].result = false
		}
		return false
	}
	c.typeResolutions = append(c.typeResolutions, TypeResolution{target: target, propertyName: propertyName, result: true})
	return true
}

/**
 * Pop an entry from the type resolution stack and return its associated result value. The result value will
 * be true if no circularities were detected, or false if a circularity was found.
 */
func (c *Checker) popTypeResolution() bool {
	lastIndex := len(c.typeResolutions) - 1
	result := c.typeResolutions[lastIndex].result
	c.typeResolutions[lastIndex] = TypeResolution{} // Clear the last entry to avoid memory leaks
	c.typeResolutions = c.typeResolutions[:lastIndex]
	return result
}

func (c *Checker) findResolutionCycleStartIndex(target TypeSystemEntity, propertyName TypeSystemPropertyName) int {
	for i := len(c.typeResolutions) - 1; i >= c.resolutionStart; i-- {
		resolution := &c.typeResolutions[i]
		if c.typeResolutionHasProperty(resolution) {
			return -1
		}
		if resolution.target == target && resolution.propertyName == propertyName {
			return i
		}
	}
	return -1
}

func (c *Checker) typeResolutionHasProperty(r *TypeResolution) bool {
	switch r.propertyName {
	case TypeSystemPropertyNameType:
		return c.valueSymbolLinks.Get(r.target.(*ast.Symbol)).resolvedType != nil
	case TypeSystemPropertyNameDeclaredType:
		return c.typeAliasLinks.Get(r.target.(*ast.Symbol)).declaredType != nil
	case TypeSystemPropertyNameResolvedTypeArguments:
		return r.target.(*Type).AsTypeReference().resolvedTypeArguments != nil
	case TypeSystemPropertyNameResolvedBaseTypes:
		return r.target.(*Type).AsInterfaceType().baseTypesResolved
	case TypeSystemPropertyNameResolvedBaseConstructorType:
		return r.target.(*Type).AsInterfaceType().resolvedBaseConstructorType != nil
	case TypeSystemPropertyNameResolvedReturnType:
		return r.target.(*Signature).resolvedReturnType != nil
	case TypeSystemPropertyNameResolvedBaseConstraint:
		return r.target.(*Type).AsConstrainedType().resolvedBaseConstraint != nil
	case TypeSystemPropertyNameInitializerIsUndefined:
		return c.nodeLinks.Get(r.target.(*ast.Node)).flags&NodeCheckFlagsInitializerIsUndefinedComputed != 0
	case TypeSystemPropertyNameWriteType:
		return c.valueSymbolLinks.Get(r.target.(*ast.Symbol)).writeType != nil
	case TypeSystemPropertyNameAliasTarget:
		return c.aliasSymbolLinks.Get(r.target.(*ast.Symbol)).aliasTarget != nil
	}
	panic("Unhandled case in typeResolutionHasProperty")
}

func (c *Checker) reportCircularityError(symbol *ast.Symbol) *Type {
	declaration := symbol.ValueDeclaration
	// Check if variable has type annotation that circularly references the variable itself
	if declaration != nil {
		if declaration.Type() != nil {
			c.error(symbol.ValueDeclaration, diagnostics.X_0_is_referenced_directly_or_indirectly_in_its_own_type_annotation, c.symbolToString(symbol))
			return c.errorType
		}
		// Check if variable has initializer that circularly references the variable itself
		if c.noImplicitAny && (!ast.IsParameterDeclaration(declaration) || declaration.Initializer() != nil) {
			c.error(symbol.ValueDeclaration, diagnostics.X_0_implicitly_has_type_any_because_it_does_not_have_a_type_annotation_and_is_referenced_directly_or_indirectly_in_its_own_initializer, c.symbolToString(symbol))
		}
	} else if symbol.Flags&ast.SymbolFlagsAlias != 0 {
		node := c.getDeclarationOfAliasSymbol(symbol)
		if node != nil {
			c.error(node, diagnostics.Circular_definition_of_import_alias_0, c.symbolToString(symbol))
		}
	}
	// Circularities could also result from parameters in function expressions that end up
	// having themselves as contextual types following type argument inference. In those cases
	// we have already reported an implicit any error so we don't report anything here.
	return c.anyType
}

func (c *Checker) getPropertiesOfType(t *Type) []*ast.Symbol {
	t = c.getReducedApparentType(t)
	if t.flags&TypeFlagsUnionOrIntersection != 0 {
		return c.getPropertiesOfUnionOrIntersectionType(t)
	}
	return c.getPropertiesOfObjectType(t)
}

func (c *Checker) getPropertiesOfObjectType(t *Type) []*ast.Symbol {
	if t.flags&TypeFlagsObject != 0 {
		return c.resolveStructuredTypeMembers(t).properties
	}
	return nil
}

func (c *Checker) getPropertiesOfUnionOrIntersectionType(t *Type) []*ast.Symbol {
	d := t.AsUnionOrIntersectionType()
	if d.resolvedProperties == nil {
		var checked collections.Set[string]
		props := []*ast.Symbol{}
		for _, current := range d.types {
			for _, prop := range c.getPropertiesOfType(current) {
				if !checked.Has(prop.Name) {
					checked.Add(prop.Name)
					combinedProp := c.getPropertyOfUnionOrIntersectionType(t, prop.Name, t.flags&TypeFlagsIntersection != 0 /*skipObjectFunctionPropertyAugment*/)
					if combinedProp != nil {
						props = append(props, combinedProp)
					}
				}
			}
			// The properties of a union type are those that are present in all constituent types, so
			// we only need to check the properties of the first type without index signature
			if t.flags&TypeFlagsUnion != 0 && len(c.getIndexInfosOfType(current)) == 0 {
				break
			}
		}
		d.resolvedProperties = props
	}
	return d.resolvedProperties
}

func (c *Checker) getPropertyOfType(t *Type, name string) *ast.Symbol {
	return c.getPropertyOfTypeEx(t, name, false /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/)
}

/**
 * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when
 * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from
 * Object and Function as appropriate.
 *
 * @param type a type to look up property from
 * @param name a name of property to look up in a given type
 */
func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPropertyAugment bool, includeTypeOnlyMembers bool) *ast.Symbol {
	t = c.getReducedApparentType(t)
	switch {
	case t.flags&TypeFlagsObject != 0:
		resolved := c.resolveStructuredTypeMembers(t)
		symbol := resolved.members[name]
		if symbol != nil {
			if !includeTypeOnlyMembers && t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsValueModule != 0 && c.moduleSymbolLinks.Get(t.symbol).typeOnlyExportStarMap[name] != nil {
				// If this is the type of a module, `resolved.members.get(name)` might have effectively skipped over
				// an `export type * from './foo'`, leaving `symbolIsValue` unable to see that the symbol is being
				// viewed through a type-only export.
				return nil
			}
			if c.symbolIsValueEx(symbol, includeTypeOnlyMembers) {
				return symbol
			}
		}
		if skipObjectFunctionPropertyAugment {
			return nil
		}
		var functionType *Type
		switch {
		case t == c.anyFunctionType:
			functionType = c.globalFunctionType
		case len(resolved.CallSignatures()) != 0:
			functionType = c.globalCallableFunctionType
		case len(resolved.ConstructSignatures()) != 0:
			functionType = c.globalNewableFunctionType
		}
		if functionType != nil {
			symbol = c.getPropertyOfObjectType(functionType, name)
			if symbol != nil {
				return symbol
			}
		}
		return c.getPropertyOfObjectType(c.globalObjectType, name)
	case t.flags&TypeFlagsIntersection != 0:
		prop := c.getPropertyOfUnionOrIntersectionType(t, name, true /*skipObjectFunctionPropertyAugment*/)
		if prop != nil {
			return prop
		}
		if !skipObjectFunctionPropertyAugment {
			return c.getPropertyOfUnionOrIntersectionType(t, name, skipObjectFunctionPropertyAugment)
		}
		return nil
	case t.flags&TypeFlagsUnion != 0:
		return c.getPropertyOfUnionOrIntersectionType(t, name, skipObjectFunctionPropertyAugment)
	}
	return nil
}

// Return the type of the given property in the given type, or nil if no such property exists
func (c *Checker) getTypeOfPropertyOfType(t *Type, name string) *Type {
	prop := c.getPropertyOfType(t, name)
	if prop != nil {
		return c.getTypeOfSymbol(prop)
	}
	return nil
}

func (c *Checker) getSignaturesOfType(t *Type, kind SignatureKind) []*Signature {
	return c.getSignaturesOfStructuredType(c.getReducedApparentType(t), kind)
}

func (c *Checker) getSignaturesOfStructuredType(t *Type, kind SignatureKind) []*Signature {
	if t.flags&TypeFlagsStructuredType == 0 {
		return nil
	}
	resolved := c.resolveStructuredTypeMembers(t)
	if kind == SignatureKindCall {
		return resolved.signatures[:resolved.callSignatureCount]
	}
	return resolved.signatures[resolved.callSignatureCount:]
}

func (c *Checker) getIndexInfosOfType(t *Type) []*IndexInfo {
	return c.getIndexInfosOfStructuredType(c.getReducedApparentType(t))
}

func (c *Checker) getIndexInfosOfStructuredType(t *Type) []*IndexInfo {
	if t.flags&TypeFlagsStructuredType != 0 {
		return c.resolveStructuredTypeMembers(t).indexInfos
	}
	return nil
}

// Return the indexing info of the given kind in the given type. Creates synthetic union index types when necessary and
// maps primitive types and type parameters are to their apparent types.
func (c *Checker) getIndexInfoOfType(t *Type, keyType *Type) *IndexInfo {
	return findIndexInfo(c.getIndexInfosOfType(t), keyType)
}

// Return the index type of the given kind in the given type. Creates synthetic union index types when necessary and
// maps primitive types and type parameters are to their apparent types.
func (c *Checker) getIndexTypeOfType(t *Type, keyType *Type) *Type {
	info := c.getIndexInfoOfType(t, keyType)
	if info != nil {
		return info.valueType
	}
	return nil
}

func (c *Checker) getIndexTypeOfTypeEx(t *Type, keyType *Type, defaultType *Type) *Type {
	if result := c.getIndexTypeOfType(t, keyType); result != nil {
		return result
	}
	return defaultType
}

func (c *Checker) getApplicableIndexInfo(t *Type, keyType *Type) *IndexInfo {
	return c.findApplicableIndexInfo(c.getIndexInfosOfType(t), keyType)
}

func (c *Checker) getApplicableIndexInfoForName(t *Type, name string) *IndexInfo {
	if isLateBoundName(name) {
		return c.getApplicableIndexInfo(t, c.esSymbolType)
	}
	return c.getApplicableIndexInfo(t, c.getStringLiteralType(name))
}

func (c *Checker) findApplicableIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexInfo {
	// Index signatures for type 'string' are considered only when no other index signatures apply.
	var stringIndexInfo *IndexInfo
	applicableInfos := make([]*IndexInfo, 0, 8)
	for _, info := range indexInfos {
		if info.keyType == c.stringType {
			stringIndexInfo = info
		} else if c.isApplicableIndexType(keyType, info.keyType) {
			applicableInfos = append(applicableInfos, info)
		}
	}
	// When more than one index signature is applicable we create a synthetic IndexInfo. Instead of computing
	// the intersected key type, we just use unknownType for the key type as nothing actually depends on the
	// keyType property of the returned IndexInfo.
	switch len(applicableInfos) {
	case 0:
		if stringIndexInfo != nil && c.isApplicableIndexType(keyType, c.stringType) {
			return stringIndexInfo
		}
		return nil
	case 1:
		return applicableInfos[0]
	default:
		isReadonly := true
		types := make([]*Type, len(applicableInfos))
		for i, info := range applicableInfos {
			types[i] = info.valueType
			if !info.isReadonly {
				isReadonly = false
			}
		}
		return c.newIndexInfo(c.unknownType, c.getIntersectionType(types), isReadonly, nil, nil)
	}
}

func (c *Checker) isApplicableIndexType(source *Type, target *Type) bool {
	// A 'string' index signature applies to types assignable to 'string' or 'number', and a 'number' index
	// signature applies to types assignable to 'number', `${number}` and numeric string literal types.
	return c.isTypeAssignableTo(source, target) ||
		target == c.stringType && c.isTypeAssignableTo(source, c.numberType) ||
		target == c.numberType && (source == c.numericStringType || source.flags&TypeFlagsStringLiteral != 0 && isNumericLiteralName(getStringLiteralValue(source)))
}

func (c *Checker) resolveStructuredTypeMembers(t *Type) *StructuredType {
	if t.objectFlags&ObjectFlagsMembersResolved == 0 {
		switch {
		case t.flags&TypeFlagsObject != 0:
			switch {
			case t.objectFlags&ObjectFlagsReference != 0:
				c.resolveTypeReferenceMembers(t)
			case t.objectFlags&ObjectFlagsClassOrInterface != 0:
				c.resolveClassOrInterfaceMembers(t)
			case t.objectFlags&ObjectFlagsReverseMapped != 0:
				c.resolveReverseMappedTypeMembers(t)
			case t.objectFlags&ObjectFlagsAnonymous != 0:
				c.resolveAnonymousTypeMembers(t)
			case t.objectFlags&ObjectFlagsMapped != 0:
				c.resolveMappedTypeMembers(t)
			default:
				panic("Unhandled case in resolveStructuredTypeMembers")
			}
		case t.flags&TypeFlagsUnion != 0:
			c.resolveUnionTypeMembers(t)
		case t.flags&TypeFlagsIntersection != 0:
			c.resolveIntersectionTypeMembers(t)
		default:
			panic("Unhandled case in resolveStructuredTypeMembers")
		}
	}
	return t.AsStructuredType()
}

func (c *Checker) resolveClassOrInterfaceMembers(t *Type) {
	c.resolveObjectTypeMembers(t, t, nil, nil)
}

func (c *Checker) resolveTypeReferenceMembers(t *Type) {
	source := t.Target()
	typeParameters := source.AsInterfaceType().allTypeParameters
	typeArguments := c.getTypeArguments(t)
	paddedTypeArguments := typeArguments
	if len(typeArguments) == len(typeParameters)-1 {
		paddedTypeArguments = core.Concatenate(typeArguments, []*Type{t})
	}
	c.resolveObjectTypeMembers(t, source, typeParameters, paddedTypeArguments)
}

func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters []*Type, typeArguments []*Type) {
	var mapper *TypeMapper
	var members ast.SymbolTable
	var callSignatures []*Signature
	var constructSignatures []*Signature
	var indexInfos []*IndexInfo
	var instantiated bool
	resolved := c.resolveDeclaredMembers(source)
	if slices.Equal(typeParameters, typeArguments) {
		members = resolved.declaredMembers
		callSignatures = resolved.declaredCallSignatures
		constructSignatures = resolved.declaredConstructSignatures
		indexInfos = resolved.declaredIndexInfos
	} else {
		instantiated = true
		mapper = newTypeMapper(typeParameters, typeArguments)
		members = c.instantiateSymbolTable(resolved.declaredMembers, mapper)
		callSignatures = c.instantiateSignatures(resolved.declaredCallSignatures, mapper)
		constructSignatures = c.instantiateSignatures(resolved.declaredConstructSignatures, mapper)
		indexInfos = c.instantiateIndexInfos(resolved.declaredIndexInfos, mapper)
	}
	baseTypes := c.getBaseTypes(source)
	if len(baseTypes) != 0 {
		if !instantiated {
			members = maps.Clone(members)
		}
		c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos)
		thisArgument := core.LastOrNil(typeArguments)
		t.objectFlags |= ObjectFlagsUnresolvedMembers
		for _, baseType := range baseTypes {
			instantiatedBaseType := baseType
			if thisArgument != nil {
				instantiatedBaseType = c.getTypeWithThisArgument(c.instantiateType(baseType, mapper), thisArgument, false /*needsApparentType*/)
			}
			members = c.addInheritedMembers(members, c.getPropertiesOfType(instantiatedBaseType))
			callSignatures = core.Concatenate(callSignatures, c.getSignaturesOfType(instantiatedBaseType, SignatureKindCall))
			constructSignatures = core.Concatenate(constructSignatures, c.getSignaturesOfType(instantiatedBaseType, SignatureKindConstruct))
			var inheritedIndexInfos []*IndexInfo
			if instantiatedBaseType != c.anyType {
				inheritedIndexInfos = c.getIndexInfosOfType(instantiatedBaseType)
			} else {
				inheritedIndexInfos = []*IndexInfo{c.anyBaseTypeIndexInfo}
			}
			indexInfos = core.Concatenate(indexInfos, core.Filter(inheritedIndexInfos, func(info *IndexInfo) bool {
				return findIndexInfo(indexInfos, info.keyType) == nil
			}))
		}
		t.objectFlags &^= ObjectFlagsUnresolvedMembers
	}
	c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos)
}

func findIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexInfo {
	for _, info := range indexInfos {
		if info.keyType == keyType {
			return info
		}
	}
	return nil
}

func (c *Checker) getBaseTypes(t *Type) []*Type {
	if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsTuple) == 0 {
		return nil
	}
	data := t.AsInterfaceType()
	if !data.baseTypesResolved {
		if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedBaseTypes) {
			return data.resolvedBaseTypes
		}
		switch {
		case t.objectFlags&ObjectFlagsTuple != 0:
			data.resolvedBaseTypes = []*Type{c.getTupleBaseType(t)}
		case t.symbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0:
			if t.symbol.Flags&ast.SymbolFlagsClass != 0 {
				c.resolveBaseTypesOfClass(t)
			}
			if t.symbol.Flags&ast.SymbolFlagsInterface != 0 {
				c.resolveBaseTypesOfInterface(t)
			}
		default:
			panic("Unhandled case in getBaseTypes")
		}
		if !c.popTypeResolution() && t.symbol.Declarations != nil {
			for _, declaration := range t.symbol.Declarations {
				if ast.IsClassDeclaration(declaration) || ast.IsInterfaceDeclaration(declaration) {
					c.reportCircularBaseType(declaration, t)
				}
			}
		}
		// In general, base type resolution always precedes member resolution. However, it is possible
		// for resolution of type parameter defaults to cause circularity errors, possibly leaving
		// members partially resolved. Here we ensure any such partial resolution is reset.
		// See https://github.com/microsoft/TypeScript/issues/16861 for an example.
		t.objectFlags &^= ObjectFlagsMembersResolved
		data.baseTypesResolved = true
	}
	return data.resolvedBaseTypes
}

func (c *Checker) getTupleBaseType(t *Type) *Type {
	typeParameters := t.AsTupleType().TypeParameters()
	elementInfos := t.AsTupleType().elementInfos
	elementTypes := make([]*Type, len(typeParameters))
	for i, tp := range typeParameters {
		if elementInfos[i].flags&ElementFlagsVariadic != 0 {
			elementTypes[i] = c.getIndexedAccessType(tp, c.numberType)
		} else {
			elementTypes[i] = tp
		}
	}
	return c.createArrayTypeEx(c.getUnionType(elementTypes), t.AsTupleType().readonly)
}

func (c *Checker) resolveBaseTypesOfClass(t *Type) {
	baseConstructorType := c.getApparentType(c.getBaseConstructorTypeOfClass(t))
	if baseConstructorType.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsAny) == 0 {
		return
	}
	baseTypeNode := getBaseTypeNodeOfClass(t)
	var baseType *Type
	var originalBaseType *Type
	if baseConstructorType.symbol != nil {
		originalBaseType = c.getDeclaredTypeOfSymbol(baseConstructorType.symbol)
	}
	if baseConstructorType.symbol != nil && baseConstructorType.symbol.Flags&ast.SymbolFlagsClass != 0 && c.areAllOuterTypeParametersApplied(originalBaseType) {
		// When base constructor type is a class with no captured type arguments we know that the constructors all have the same type parameters as the
		// class and all return the instance type of the class. There is no need for further checks and we can apply the
		// type arguments in the same manner as a type reference to get the same error reporting experience.
		baseType = c.getTypeFromClassOrInterfaceReference(baseTypeNode, baseConstructorType.symbol)
	} else if baseConstructorType.flags&TypeFlagsAny != 0 {
		baseType = baseConstructorType
	} else {
		// The class derives from a "class-like" constructor function, check that we have at least one construct signature
		// with a matching number of type parameters and use the return type of the first instantiated signature. Elsewhere
		// we check that all instantiated signatures return the same type.
		constructors := c.getInstantiatedConstructorsForTypeArguments(baseConstructorType, baseTypeNode.TypeArguments(), baseTypeNode)
		if len(constructors) == 0 {
			c.error(baseTypeNode.Expression(), diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments)
			return
		}
		baseType = c.getReturnTypeOfSignature(constructors[0])
	}
	if c.isErrorType(baseType) {
		return
	}
	reducedBaseType := c.getReducedType(baseType)
	if !c.isValidBaseType(reducedBaseType) {
		errorNode := baseTypeNode.Expression()
		diagnostic := c.elaborateNeverIntersection(nil, errorNode, baseType)
		diagnostic = NewDiagnosticChainForNode(diagnostic, errorNode, diagnostics.Base_constructor_return_type_0_is_not_an_object_type_or_intersection_of_object_types_with_statically_known_members, c.TypeToString(reducedBaseType))
		c.addDiagnostic(diagnostic)
		return
	}
	if t == reducedBaseType || c.hasBaseType(reducedBaseType, t) {
		c.error(t.symbol.ValueDeclaration, diagnostics.Type_0_recursively_references_itself_as_a_base_type, c.TypeToString(t))
		return
	}
	t.AsInterfaceType().resolvedBaseTypes = []*Type{reducedBaseType}
}

func getBaseTypeNodeOfClass(t *Type) *ast.Node {
	decl := ast.GetClassLikeDeclarationOfSymbol(t.symbol)
	if decl != nil {
		return ast.GetExtendsHeritageClauseElement(decl)
	}
	return nil
}

func (c *Checker) getInstantiatedConstructorsForTypeArguments(t *Type, typeArgumentNodes []*ast.Node, location *ast.Node) []*Signature {
	signatures := c.getConstructorsForTypeArguments(t, typeArgumentNodes, location)
	typeArguments := core.Map(typeArgumentNodes, c.getTypeFromTypeNode)
	return core.SameMap(signatures, func(sig *Signature) *Signature {
		if len(sig.typeParameters) != 0 {
			return c.getSignatureInstantiation(sig, typeArguments, ast.IsInJSFile(location), nil)
		}
		return sig
	})
}

func (c *Checker) getConstructorsForTypeArguments(t *Type, typeArgumentNodes []*ast.Node, location *ast.Node) []*Signature {
	typeArgCount := len(typeArgumentNodes)
	return core.Filter(c.getSignaturesOfType(t, SignatureKindConstruct), func(sig *Signature) bool {
		return typeArgCount >= c.getMinTypeArgumentCount(sig.typeParameters) && typeArgCount <= len(sig.typeParameters)
	})
}

func (c *Checker) getSignatureInstantiation(sig *Signature, typeArguments []*Type, isJavaScript bool, inferredTypeParameters []*Type) *Signature {
	instantiatedSignature := c.getSignatureInstantiationWithoutFillingInTypeArguments(sig, c.fillMissingTypeArguments(typeArguments, sig.typeParameters, c.getMinTypeArgumentCount(sig.typeParameters), isJavaScript))
	if len(inferredTypeParameters) != 0 {
		returnSignature := c.getSingleCallOrConstructSignature(c.getReturnTypeOfSignature(instantiatedSignature))
		if returnSignature != nil {
			newReturnSignature := c.cloneSignature(returnSignature)
			newReturnSignature.typeParameters = inferredTypeParameters
			newReturnType := c.getOrCreateTypeFromSignature(newReturnSignature)
			newReturnType.AsObjectType().mapper = instantiatedSignature.mapper
			newInstantiatedSignature := c.cloneSignature(instantiatedSignature)
			newInstantiatedSignature.resolvedReturnType = newReturnType
			return newInstantiatedSignature
		}
	}
	return instantiatedSignature
}

func (c *Checker) cloneSignature(sig *Signature) *Signature {
	result := c.newSignature(sig.flags&SignatureFlagsPropagatingFlags, sig.declaration, sig.typeParameters, sig.thisParameter, sig.parameters, nil, nil, int(sig.minArgumentCount))
	result.target = sig.target
	result.mapper = sig.mapper
	result.composite = sig.composite
	return result
}

func (c *Checker) getSignatureInstantiationWithoutFillingInTypeArguments(sig *Signature, typeArguments []*Type) *Signature {
	key := CachedSignatureKey{sig: sig, key: getTypeListKey(typeArguments)}
	instantiation := c.cachedSignatures[key]
	if instantiation == nil {
		instantiation = c.createSignatureInstantiation(sig, typeArguments)
		c.cachedSignatures[key] = instantiation
	}
	return instantiation
}

func (c *Checker) createSignatureInstantiation(sig *Signature, typeArguments []*Type) *Signature {
	return c.instantiateSignatureEx(sig, c.createSignatureTypeMapper(sig, typeArguments), true /*eraseTypeParameters*/)
}

func (c *Checker) createSignatureTypeMapper(sig *Signature, typeArguments []*Type) *TypeMapper {
	return newTypeMapper(c.getTypeParametersForMapper(sig), typeArguments)
}

func (c *Checker) getTypeParametersForMapper(sig *Signature) []*Type {
	return core.SameMap(sig.typeParameters, func(tp *Type) *Type { return c.instantiateType(tp, tp.Mapper()) })
}

// If type has a single call signature and no other members, return that signature. Otherwise, return nil.
func (c *Checker) getSingleCallSignature(t *Type) *Signature {
	return c.getSingleSignature(t, SignatureKindCall, false /*allowMembers*/)
}

func (c *Checker) getSingleCallOrConstructSignature(t *Type) *Signature {
	callSig := c.getSingleSignature(t, SignatureKindCall, false /*allowMembers*/)
	if callSig != nil {
		return callSig
	}
	return c.getSingleSignature(t, SignatureKindConstruct, false /*allowMembers*/)
}

func (c *Checker) getSingleSignature(t *Type, kind SignatureKind, allowMembers bool) *Signature {
	if t.flags&TypeFlagsObject != 0 {
		resolved := c.resolveStructuredTypeMembers(t)
		if allowMembers || len(resolved.properties) == 0 && len(resolved.indexInfos) == 0 {
			if kind == SignatureKindCall && len(resolved.CallSignatures()) == 1 && len(resolved.ConstructSignatures()) == 0 {
				return resolved.CallSignatures()[0]
			}
			if kind == SignatureKindConstruct && len(resolved.ConstructSignatures()) == 1 && len(resolved.CallSignatures()) == 0 {
				return resolved.ConstructSignatures()[0]
			}
		}
	}
	return nil
}

func (c *Checker) getOrCreateTypeFromSignature(sig *Signature) *Type {
	// There are two ways to declare a construct signature, one is by declaring a class constructor
	// using the constructor keyword, and the other is declaring a bare construct signature in an
	// object type literal or interface (using the new keyword). Each way of declaring a constructor
	// will result in a different declaration kind.
	if sig.isolatedSignatureType == nil {
		var kind ast.Kind
		if sig.declaration != nil {
			kind = sig.declaration.Kind
		}
		// If declaration is undefined, it is likely to be the signature of the default constructor.
		isConstructor := kind == ast.KindUnknown || kind == ast.KindConstructor || kind == ast.KindConstructSignature || kind == ast.KindConstructorType

		var symbol *ast.Symbol
		if sig.declaration != nil {
			symbol = sig.declaration.Symbol()
		}
		t := c.newObjectType(ObjectFlagsAnonymous|ObjectFlagsSingleSignatureType, symbol)
		if isConstructor {
			c.setStructuredTypeMembers(t, nil, nil, []*Signature{sig}, nil)
		} else {
			c.setStructuredTypeMembers(t, nil, []*Signature{sig}, nil, nil)
		}
		sig.isolatedSignatureType = t
	}
	return sig.isolatedSignatureType
}

func (c *Checker) getErasedSignature(signature *Signature) *Signature {
	if len(signature.typeParameters) == 0 {
		return signature
	}
	key := CachedSignatureKey{sig: signature, key: SignatureKeyErased}
	erased := c.cachedSignatures[key]
	if erased == nil {
		erased = c.instantiateSignatureEx(signature, newArrayToSingleTypeMapper(signature.typeParameters, c.anyType), true /*eraseTypeParameters*/)
		c.cachedSignatures[key] = erased
	}
	return erased
}

func (c *Checker) getCanonicalSignature(signature *Signature) *Signature {
	if len(signature.typeParameters) == 0 {
		return signature
	}
	key := CachedSignatureKey{sig: signature, key: SignatureKeyCanonical}
	canonical := c.cachedSignatures[key]
	if canonical == nil {
		canonical = c.createCanonicalSignature(signature)
		c.cachedSignatures[key] = canonical
	}
	return canonical
}

func (c *Checker) createCanonicalSignature(signature *Signature) *Signature {
	// Create an instantiation of the signature where each unconstrained type parameter is replaced with
	// its original. When a generic class or interface is instantiated, each generic method in the class or
	// interface is instantiated with a fresh set of cloned type parameters (which we need to handle scenarios
	// where different generations of the same type parameter are in scope). This leads to a lot of new type
	// identities, and potentially a lot of work comparing those identities, so here we create an instantiation
	// that uses the original type identities for all unconstrained type parameters.
	typeArguments := core.Map(signature.typeParameters, func(tp *Type) *Type {
		if tp.Target() != nil && c.getConstraintOfTypeParameter(tp.Target()) == nil {
			return tp.Target()
		}
		return tp
	})
	return c.getSignatureInstantiation(signature, typeArguments, ast.IsInJSFile(signature.declaration), nil /*inferredTypeParameters*/)
}

func (c *Checker) getBaseSignature(signature *Signature) *Signature {
	typeParameters := signature.typeParameters
	if len(typeParameters) == 0 {
		return signature
	}
	key := CachedSignatureKey{sig: signature, key: SignatureKeyBase}
	if cached := c.cachedSignatures[key]; cached != nil {
		return cached
	}
	baseConstraintMapper := newTypeMapper(typeParameters, core.Map(typeParameters, func(tp *Type) *Type {
		return core.OrElse(c.getConstraintOfTypeParameter(tp), c.unknownType)
	}))
	baseConstraints := core.Map(typeParameters, func(tp *Type) *Type {
		return c.instantiateType(tp, baseConstraintMapper)
	})
	// Run the immediate constraint mapper N-1 times so non-circular interdependent type parameters
	// resolve to their external dependencies without adding an extra expansion step for self-recursive constraints.
	for range len(typeParameters) - 1 {
		baseConstraints = c.instantiateTypes(baseConstraints, baseConstraintMapper)
	}
	// and then apply a type eraser to remove any remaining circularly dependent type parameters
	baseConstraints = c.instantiateTypes(baseConstraints, newArrayToSingleTypeMapper(typeParameters, c.anyType))
	result := c.instantiateSignatureEx(signature, newTypeMapper(typeParameters, baseConstraints), true /*eraseTypeParameters*/)
	c.cachedSignatures[key] = result
	return result
}

// Instantiate a generic signature in the context of a non-generic signature (section 3.8.5 in TypeScript spec)
func (c *Checker) instantiateSignatureInContextOf(signature *Signature, contextualSignature *Signature, inferenceContext *InferenceContext, compareTypes TypeComparer) *Signature {
	context := c.newInferenceContext(c.getTypeParametersForMapper(signature), signature, InferenceFlagsNone, compareTypes)
	// We clone the inferenceContext to avoid fixing. For example, when the source signature is <T>(x: T) => T[] and
	// the contextual signature is (...args: A) => B, we want to infer the element type of A's constraint (say 'any')
	// for T but leave it possible to later infer '[any]' back to A.
	restType := c.getEffectiveRestType(contextualSignature)
	var mapper *TypeMapper
	if inferenceContext != nil {
		if restType != nil && restType.flags&TypeFlagsTypeParameter != 0 {
			mapper = inferenceContext.nonFixingMapper
		} else {
			mapper = inferenceContext.mapper
		}
	}
	var sourceSignature *Signature
	if mapper != nil {
		sourceSignature = c.instantiateSignature(contextualSignature, mapper)
	} else {
		sourceSignature = contextualSignature
	}
	c.applyToParameterTypes(sourceSignature, signature, func(source *Type, target *Type) {
		// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
		c.inferTypes(context.inferences, source, target, InferencePriorityNone, false)
	})
	if inferenceContext == nil {
		c.applyToReturnTypes(contextualSignature, signature, func(source *Type, target *Type) {
			c.inferTypes(context.inferences, source, target, InferencePriorityReturnType, false)
		})
	}
	return c.getSignatureInstantiation(signature, c.getInferredTypes(context), ast.IsInJSFile(contextualSignature.declaration), nil /*inferredTypeParameters*/)
}

func (c *Checker) resolveBaseTypesOfInterface(t *Type) {
	data := t.AsInterfaceType()
	for _, declaration := range t.symbol.Declarations {
		if ast.IsInterfaceDeclaration(declaration) {
			for _, node := range ast.GetExtendsHeritageClauseElements(declaration) {
				baseType := c.getReducedType(c.getTypeFromTypeNode(node))
				if !c.isErrorType(baseType) {
					if c.isValidBaseType(baseType) {
						if t != baseType && !c.hasBaseType(baseType, t) {
							data.resolvedBaseTypes = append(data.resolvedBaseTypes, baseType)
						} else {
							c.reportCircularBaseType(declaration, t)
						}
					} else {
						c.error(node, diagnostics.An_interface_can_only_extend_an_object_type_or_intersection_of_object_types_with_statically_known_members)
					}
				}
			}
		}
	}
}

func (c *Checker) areAllOuterTypeParametersApplied(t *Type) bool {
	// An unapplied type parameter has its symbol still the same as the matching argument symbol.
	// Since parameters are applied outer-to-inner, only the last outer parameter needs to be checked.
	outerTypeParameters := t.AsInterfaceType().OuterTypeParameters()
	if len(outerTypeParameters) != 0 {
		last := len(outerTypeParameters) - 1
		typeArguments := c.getTypeArguments(t)
		return outerTypeParameters[last].symbol != typeArguments[last].symbol
	}
	return true
}

func (c *Checker) reportCircularBaseType(node *ast.Node, t *Type) {
	c.error(node, diagnostics.Type_0_recursively_references_itself_as_a_base_type, c.typeToStringEx(t, nil, TypeFormatFlagsWriteArrayAsGenericType, nil))
}

// A valid base type is `any`, an object type or intersection of object types.
func (c *Checker) isValidBaseType(t *Type) bool {
	if t.flags&TypeFlagsTypeParameter != 0 {
		constraint := c.getBaseConstraintOfType(t)
		if constraint != nil {
			return c.isValidBaseType(constraint)
		}
	}
	// TODO: Given that we allow type parameters here now, is this `!isGenericMappedType(type)` check really needed?
	// There's no reason a `T` should be allowed while a `Readonly<T>` should not.
	return t.flags&(TypeFlagsObject|TypeFlagsNonPrimitive|TypeFlagsAny) != 0 && !c.isGenericMappedType(t) ||
		t.flags&TypeFlagsIntersection != 0 && core.Every(t.Types(), c.isValidBaseType)
}

// TODO: GH#18217 If `checkBase` is undefined, we should not call this because this will always return false.
func (c *Checker) hasBaseType(t *Type, checkBase *Type) bool {
	var check func(*Type) bool
	check = func(t *Type) bool {
		if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 {
			target := getTargetType(t)
			return target == checkBase || core.Some(c.getBaseTypes(target), check)
		}
		if t.flags&TypeFlagsIntersection != 0 {
			return core.Some(t.Types(), check)
		}
		return false
	}
	return check(t)
}

func getTargetType(t *Type) *Type {
	if t.objectFlags&ObjectFlagsReference != 0 {
		return t.Target()
	}
	return t
}

func (c *Checker) getTypeWithThisArgument(t *Type, thisArgument *Type, needApparentType bool) *Type {
	if t.objectFlags&ObjectFlagsReference != 0 {
		target := t.Target()
		typeArguments := c.getTypeArguments(t)
		if len(target.AsInterfaceType().TypeParameters()) == len(typeArguments) {
			if thisArgument == nil {
				thisArgument = target.AsInterfaceType().thisType
			}
			return c.createTypeReference(target, core.Concatenate(typeArguments, []*Type{thisArgument}))
		}
		return t
	} else if t.flags&TypeFlagsIntersection != 0 {
		types := t.Types()
		newTypes := core.SameMap(types, func(t *Type) *Type { return c.getTypeWithThisArgument(t, thisArgument, needApparentType) })
		if core.Same(newTypes, types) {
			return t
		}
		return c.getIntersectionType(newTypes)
	}
	if needApparentType {
		return c.getApparentType(t)
	}
	return t
}

func (c *Checker) addInheritedMembers(symbols ast.SymbolTable, baseSymbols []*ast.Symbol) ast.SymbolTable {
	for _, base := range baseSymbols {
		if !isStaticPrivateIdentifierProperty(base) {
			if s, ok := symbols[base.Name]; !ok || s.Flags&ast.SymbolFlagsValue == 0 {
				if symbols == nil {
					symbols = make(ast.SymbolTable)
				}
				symbols[base.Name] = base
			}
		}
	}
	return symbols
}

func (c *Checker) resolveDeclaredMembers(t *Type) *InterfaceType {
	d := t.AsInterfaceType()
	if !d.declaredMembersResolved {
		members := c.getMembersOfSymbol(t.symbol)
		d.declaredMembersResolved = true
		d.declaredMembers = members
		d.declaredCallSignatures = c.getSignaturesOfSymbol(d.declaredMembers[ast.InternalSymbolNameCall])
		d.declaredConstructSignatures = c.getSignaturesOfSymbol(d.declaredMembers[ast.InternalSymbolNameNew])
		d.declaredIndexInfos = c.getIndexInfosOfSymbol(t.symbol)
	}
	return d
}

func (c *Checker) getIndexInfosOfSymbol(symbol *ast.Symbol) []*IndexInfo {
	indexSymbol := c.getIndexSymbol(symbol)
	if indexSymbol != nil {
		return c.getIndexInfosOfIndexSymbol(indexSymbol, slices.Collect(maps.Values(c.getMembersOfSymbol(symbol))))
	}
	return nil
}

// note intentional similarities to index signature building in `checkObjectLiteral` for parity
func (c *Checker) getIndexInfosOfIndexSymbol(indexSymbol *ast.Symbol, siblingSymbols []*ast.Symbol) []*IndexInfo {
	var indexInfos []*IndexInfo
	hasComputedStringProperty := false
	hasComputedNumberProperty := false
	hasComputedSymbolProperty := false
	readonlyComputedStringProperty := true
	readonlyComputedNumberProperty := true
	readonlyComputedSymbolProperty := true
	var propertySymbols []*ast.Symbol
	for _, declaration := range indexSymbol.Declarations {
		if ast.IsIndexSignatureDeclaration(declaration) {
			parameters := declaration.Parameters()
			returnTypeNode := declaration.Type()
			if len(parameters) == 1 {
				typeNode := parameters[0].Type()
				if typeNode != nil {
					valueType := c.anyType
					if returnTypeNode != nil {
						valueType = c.getTypeFromTypeNode(returnTypeNode)
					}
					forEachType(c.getTypeFromTypeNode(typeNode), func(keyType *Type) {
						if c.isValidIndexKeyType(keyType) && findIndexInfo(indexInfos, keyType) == nil {
							indexInfo := c.newIndexInfo(keyType, valueType, ast.HasModifier(declaration, ast.ModifierFlagsReadonly), declaration, nil)
							indexInfos = append(indexInfos, indexInfo)
						}
					})
				}
			}
		} else if c.hasLateBindableIndexSignature(declaration) {
			var declName *ast.Node
			if ast.IsBinaryExpression(declaration) {
				declName = declaration.AsBinaryExpression().Left
			} else {
				declName = declaration.Name()
			}
			var keyType *Type
			if ast.IsElementAccessExpression(declName) {
				keyType = c.checkExpressionCached(declName.AsElementAccessExpression().ArgumentExpression)
			} else {
				keyType = c.checkComputedPropertyName(declName)
			}
			if findIndexInfo(indexInfos, keyType) != nil {
				continue
				// Explicit index for key type takes priority
			}
			if c.isTypeAssignableTo(keyType, c.stringNumberSymbolType) {
				if c.isTypeAssignableTo(keyType, c.numberType) {
					hasComputedNumberProperty = true
					if !hasReadonlyModifier(declaration) {
						readonlyComputedNumberProperty = false
					}
				} else if c.isTypeAssignableTo(keyType, c.esSymbolType) {
					hasComputedSymbolProperty = true
					if !hasReadonlyModifier(declaration) {
						readonlyComputedSymbolProperty = false
					}
				} else {
					hasComputedStringProperty = true
					if !hasReadonlyModifier(declaration) {
						readonlyComputedStringProperty = false
					}
				}
				propertySymbols = append(propertySymbols, declaration.Symbol())
			}
		}
	}
	if hasComputedStringProperty || hasComputedNumberProperty || hasComputedSymbolProperty {
		for _, sym := range siblingSymbols {
			if sym != indexSymbol {
				propertySymbols = append(propertySymbols, sym)
			}
		}
		// aggregate similar index infos implied to be the same key to the same combined index info
		if hasComputedStringProperty && findIndexInfo(indexInfos, c.stringType) == nil {
			indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(readonlyComputedStringProperty, propertySymbols, c.stringType))
		}
		if hasComputedNumberProperty && findIndexInfo(indexInfos, c.numberType) == nil {
			indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(readonlyComputedNumberProperty, propertySymbols, c.numberType))
		}
		if hasComputedSymbolProperty && findIndexInfo(indexInfos, c.esSymbolType) == nil {
			indexInfos = append(indexInfos, c.getObjectLiteralIndexInfo(readonlyComputedSymbolProperty, propertySymbols, c.esSymbolType))
		}
	}
	return indexInfos
}

// NOTE: currently does not make pattern literal indexers, eg `${number}px`
func (c *Checker) getObjectLiteralIndexInfo(isReadonly bool, properties []*ast.Symbol, keyType *Type) *IndexInfo {
	var propTypes []*Type
	var components []*ast.Node
	for _, prop := range properties {
		if keyType == c.stringType && !c.isSymbolWithSymbolName(prop) ||
			keyType == c.numberType && c.isSymbolWithNumericName(prop) ||
			keyType == c.esSymbolType && c.isSymbolWithSymbolName(prop) {
			propTypes = append(propTypes, c.getTypeOfSymbol(prop))
			if c.isSymbolWithComputedName(prop) {
				components = append(components, prop.Declarations[0])
			}
		}
	}
	unionType := c.undefinedType
	if len(propTypes) != 0 {
		unionType = c.getUnionTypeEx(propTypes, UnionReductionSubtype, nil, nil)
	}
	return c.newIndexInfo(keyType, unionType, isReadonly, nil /*declaration*/, components)
}

func (c *Checker) isSymbolWithSymbolName(symbol *ast.Symbol) bool {
	if IsKnownSymbol(symbol) {
		return true
	}
	if len(symbol.Declarations) != 0 {
		name := symbol.Declarations[0].Name()
		return name != nil && ast.IsComputedPropertyName(name) && c.isTypeAssignableToKind(c.checkComputedPropertyName(name), TypeFlagsESSymbol)
	}
	return false
}

func (c *Checker) isSymbolWithNumericName(symbol *ast.Symbol) bool {
	if isNumericLiteralName(symbol.Name) {
		return true
	}
	if len(symbol.Declarations) != 0 {
		name := symbol.Declarations[0].Name()
		return name != nil && c.isNumericName(name)
	}
	return false
}

func (c *Checker) isSymbolWithComputedName(symbol *ast.Symbol) bool {
	if len(symbol.Declarations) != 0 {
		name := symbol.Declarations[0].Name()
		return name != nil && ast.IsComputedPropertyName(name)
	}
	return false
}

func (c *Checker) isNumericName(name *ast.Node) bool {
	switch name.Kind {
	case ast.KindComputedPropertyName:
		return c.isNumericComputedName(name)
	case ast.KindIdentifier, ast.KindNumericLiteral, ast.KindStringLiteral:
		return isNumericLiteralName(name.Text())
	}
	return false
}

func (c *Checker) isNumericComputedName(name *ast.Node) bool {
	// It seems odd to consider an expression of type Any to result in a numeric name,
	// but this behavior is consistent with checkIndexedAccess
	return c.isTypeAssignableToKind(c.checkComputedPropertyName(name), TypeFlagsNumberLike)
}

func (c *Checker) isValidIndexKeyType(t *Type) bool {
	return t.flags&(TypeFlagsString|TypeFlagsNumber|TypeFlagsESSymbol) != 0 ||
		c.isPatternLiteralType(t) ||
		t.flags&TypeFlagsIntersection != 0 && !c.isGenericType(t) && core.Some(t.Types(), c.isValidIndexKeyType)
}

func (c *Checker) findIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexInfo {
	for _, info := range indexInfos {
		if info.keyType == keyType {
			return info
		}
	}
	return nil
}

func (c *Checker) getIndexSymbol(symbol *ast.Symbol) *ast.Symbol {
	return c.getMembersOfSymbol(symbol)[ast.InternalSymbolNameIndex]
}

func (c *Checker) getSignaturesOfSymbol(symbol *ast.Symbol) []*Signature {
	if symbol == nil {
		return nil
	}
	var result []*Signature
	for i, decl := range symbol.Declarations {
		if !ast.IsFunctionLike(decl) {
			continue
		}
		// Don't include signature if node is the implementation of an overloaded function. A node is considered
		// an implementation node if it has a body and the previous node is of the same kind and immediately
		// precedes the implementation node (i.e. has the same parent and ends where the implementation starts).
		if i > 0 && decl.Body() != nil {
			previous := symbol.Declarations[i-1]
			if decl.Parent == previous.Parent && decl.Kind == previous.Kind &&
				(decl.Pos() == previous.End() || previous.Flags&ast.NodeFlagsReparsed != 0) {
				continue
			}
		}
		// If this is a function or method declaration, get the signature from the @type tag for the sake of optional parameters.
		// Exclude contextually-typed kinds because we already apply the @type tag to the context, plus applying it here to the initializer would suppress checks that the two are compatible.
		sig := c.getSignatureOfFullSignatureType(decl)
		if sig == nil {
			sig = c.getSignatureFromDeclaration(decl)
		}
		result = append(result, sig)
	}
	return result
}

func (c *Checker) getSignatureFromDeclaration(declaration *ast.Node) *Signature {
	links := c.signatureLinks.Get(declaration)
	if links.resolvedSignature != nil {
		return links.resolvedSignature
	}
	var parameters []*ast.Symbol
	var flags SignatureFlags
	var thisParameter *ast.Symbol
	minArgumentCount := 0
	hasThisParameter := false
	iife := ast.GetImmediatelyInvokedFunctionExpression(declaration)
	isUntypedSignatureInJSFile := iife == nil &&
		ast.IsInJSFile(declaration) &&
		(ast.IsFunctionExpression(declaration) || ast.IsArrowFunction(declaration) || ast.IsMethodOrAccessor(declaration) || ast.IsFunctionDeclaration(declaration) || ast.IsConstructorDeclaration(declaration)) &&
		core.Every(declaration.Parameters(), func(param *ast.Node) bool { return param.Type() == nil }) &&
		c.getContextualType(declaration, ContextFlagsSignature) == nil
	if isUntypedSignatureInJSFile {
		flags |= SignatureFlagsIsUntypedSignatureInJSFile
	}
	for i, param := range declaration.Parameters() {
		paramSymbol := param.Symbol()
		typeNode := param.Type()
		// Include parameter symbol instead of property symbol in the signature
		if paramSymbol != nil && paramSymbol.Flags&ast.SymbolFlagsProperty != 0 && !ast.IsBindingPattern(param.Name()) {
			resolvedSymbol := c.resolveName(param, paramSymbol.Name, ast.SymbolFlagsValue, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/)
			paramSymbol = resolvedSymbol
		}
		if i == 0 && paramSymbol.Name == ast.InternalSymbolNameThis {
			hasThisParameter = true
			thisParameter = param.Symbol()
		} else {
			parameters = append(parameters, paramSymbol)
		}
		if typeNode != nil && typeNode.Kind == ast.KindLiteralType {
			flags |= SignatureFlagsHasLiteralTypes
		}
		// Record a new minimum argument count if this is not an optional parameter
		isOptionalParameter := isOptionalDeclaration(param) ||
			param.Initializer() != nil ||
			isRestParameter(param) ||
			iife != nil && len(parameters) > len(iife.Arguments()) && typeNode == nil
		if !isOptionalParameter {
			minArgumentCount = len(parameters)
		}
	}
	// If only one accessor includes a this-type annotation, the other behaves as if it had the same type annotation
	if (ast.IsGetAccessorDeclaration(declaration) || ast.IsSetAccessorDeclaration(declaration)) && c.hasBindableName(declaration) && (!hasThisParameter || thisParameter == nil) {
		otherKind := core.IfElse(ast.IsGetAccessorDeclaration(declaration), ast.KindSetAccessor, ast.KindGetAccessor)
		other := ast.GetDeclarationOfKind(c.getSymbolOfDeclaration(declaration), otherKind)
		if other != nil {
			thisParameter = c.getAnnotatedAccessorThisParameter(other)
		}
	}
	var classType *Type
	if ast.IsConstructorDeclaration(declaration) {
		classType = c.getDeclaredTypeOfClassOrInterface(c.getMergedSymbol(declaration.Parent.Symbol()))
	}
	var typeParameters []*Type
	if classType != nil {
		typeParameters = classType.AsInterfaceType().LocalTypeParameters()
	} else {
		typeParameters = c.getTypeParametersFromDeclaration(declaration)
	}
	if hasRestParameter(declaration) {
		flags |= SignatureFlagsHasRestParameter
	}
	if ast.IsConstructorTypeNode(declaration) || ast.IsConstructorDeclaration(declaration) || ast.IsConstructSignatureDeclaration(declaration) {
		flags |= SignatureFlagsConstruct
	}
	if ast.IsConstructorTypeNode(declaration) && ast.HasSyntacticModifier(declaration, ast.ModifierFlagsAbstract) || ast.IsConstructorDeclaration(declaration) && ast.HasSyntacticModifier(declaration.Parent, ast.ModifierFlagsAbstract) {
		flags |= SignatureFlagsAbstract
	}
	links.resolvedSignature = c.newSignature(flags, declaration, typeParameters, thisParameter, parameters, nil /*resolvedReturnType*/, nil /*resolvedTypePredicate*/, minArgumentCount)
	return links.resolvedSignature
}

func (c *Checker) getTypeParametersFromDeclaration(declaration *ast.Node) []*Type {
	if sig := c.getSignatureOfFullSignatureType(declaration); sig != nil {
		return sig.TypeParameters()
	}
	var result []*Type
	for _, node := range declaration.TypeParameters() {
		result = core.AppendIfUnique(result, c.getDeclaredTypeOfTypeParameter(node.Symbol()))
	}
	return result
}

func (c *Checker) getAnnotatedAccessorThisParameter(accessor *ast.Node) *ast.Symbol {
	parameter := c.getAccessorThisParameter(accessor)
	if parameter != nil {
		return parameter.Symbol()
	}
	return nil
}

func (c *Checker) getAccessorThisParameter(accessor *ast.Node) *ast.Node {
	if len(accessor.Parameters()) == core.IfElse(ast.IsGetAccessorDeclaration(accessor), 1, 2) {
		return ast.GetThisParameter(accessor)
	}
	return nil
}

/**
 * Indicates whether a declaration has an early-bound name or a dynamic name that can be late-bound.
 */
func (c *Checker) hasBindableName(node *ast.Node) bool {
	return !ast.HasDynamicName(node) || c.hasLateBindableName(node)
}

/**
 * Indicates whether a declaration has a late-bindable dynamic name.
 */
func (c *Checker) hasLateBindableName(node *ast.Node) bool {
	name := ast.GetNameOfDeclaration(node)
	return name != nil && c.isLateBindableName(name)
}

/**
 * Indicates whether a declaration name is definitely late-bindable.
 * A declaration name is only late-bindable if:
 * - It is a `ComputedPropertyName`.
 * - Its expression is an `Identifier` or either a `PropertyAccessExpression` an
 * `ElementAccessExpression` consisting only of these same three types of nodes.
 * - The type of its expression is a string or numeric literal type, or is a `unique symbol` type.
 */
func (c *Checker) isLateBindableName(node *ast.Node) bool {
	if !isLateBindableAST(node) {
		return false
	}
	if ast.IsComputedPropertyName(node) {
		return isTypeUsableAsPropertyName(c.checkComputedPropertyName(node))
	}
	return isTypeUsableAsPropertyName(c.checkExpressionCached(node.AsElementAccessExpression().ArgumentExpression))
}

func (c *Checker) hasLateBindableIndexSignature(node *ast.Node) bool {
	name := ast.GetNameOfDeclaration(node)
	return name != nil && c.isLateBindableIndexSignature(name)
}

func (c *Checker) isLateBindableIndexSignature(node *ast.Node) bool {
	if !isLateBindableAST(node) {
		return false
	}
	if ast.IsComputedPropertyName(node) {
		return c.isTypeUsableAsIndexSignatureDeclaration(c.checkComputedPropertyName(node))
	}
	return c.isTypeUsableAsIndexSignatureDeclaration(c.checkExpressionCached(node.AsElementAccessExpression().ArgumentExpression))
}

func (c *Checker) isTypeUsableAsIndexSignatureDeclaration(t *Type) bool {
	return c.isTypeAssignableTo(t, c.stringNumberSymbolType)
}

func isLateBindableAST(node *ast.Node) bool {
	var expr *ast.Node
	switch {
	case ast.IsComputedPropertyName(node):
		expr = node.Expression()
	case ast.IsElementAccessExpression(node):
		expr = node.AsElementAccessExpression().ArgumentExpression
	}
	return expr != nil && ast.IsEntityNameExpression(expr)
}

func (c *Checker) getReturnTypeOfSignature(sig *Signature) *Type {
	if sig.resolvedReturnType != nil {
		return sig.resolvedReturnType
	}
	if !c.pushTypeResolution(sig, TypeSystemPropertyNameResolvedReturnType) {
		return c.errorType
	}
	var t *Type
	switch {
	case sig.target != nil:
		t = c.instantiateType(c.getReturnTypeOfSignature(sig.target), sig.mapper)
	case sig.composite != nil:
		t = c.instantiateType(c.getUnionOrIntersectionType(core.Map(sig.composite.signatures, c.getReturnTypeOfSignature), sig.composite.isUnion, UnionReductionSubtype), sig.mapper)
	default:
		t = c.getReturnTypeFromAnnotation(sig.declaration)
		if t == nil {
			if !ast.NodeIsMissing(sig.declaration.Body()) {
				t = c.getReturnTypeFromBody(sig.declaration, CheckModeNormal)
			} else {
				t = c.anyType
			}
		}
	}
	if sig.flags&SignatureFlagsIsInnerCallChain != 0 {
		t = c.addOptionalTypeMarker(t)
	} else if sig.flags&SignatureFlagsIsOuterCallChain != 0 {
		t = c.getOptionalType(t, false /*isProperty*/)
	}
	if !c.popTypeResolution() {
		if sig.declaration != nil {
			typeNode := sig.declaration.Type()
			if typeNode != nil {
				c.error(typeNode, diagnostics.Return_type_annotation_circularly_references_itself)
			} else if c.noImplicitAny {
				name := ast.GetNameOfDeclaration(sig.declaration)
				if name != nil {
					c.error(name, diagnostics.X_0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, scanner.DeclarationNameToString(name))
				} else {
					c.error(sig.declaration, diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions)
				}
			}
		}
		t = c.anyType
	}
	if sig.resolvedReturnType == nil {
		sig.resolvedReturnType = t
	}
	return sig.resolvedReturnType
}

func (c *Checker) getNonCircularReturnTypeOfSignature(sig *Signature) *Type {
	if c.isResolvingReturnTypeOfSignature(sig) {
		return c.anyType
	}
	return c.getReturnTypeOfSignature(sig)
}

func (c *Checker) getReturnTypeFromAnnotation(declaration *ast.Node) *Type {
	if ast.IsConstructorDeclaration(declaration) {
		return c.getDeclaredTypeOfClassOrInterface(c.getMergedSymbol(declaration.Parent.Symbol()))
	}
	returnType := declaration.Type()
	if returnType != nil {
		return c.getTypeFromTypeNode(returnType)
	}
	if ast.IsGetAccessorDeclaration(declaration) && c.hasBindableName(declaration) {
		return c.getAnnotatedAccessorType(ast.GetDeclarationOfKind(c.getSymbolOfDeclaration(declaration), ast.KindSetAccessor))
	}
	return c.getReturnTypeOfFullSignature(declaration)
}

func (c *Checker) getSignatureOfFullSignatureType(node *ast.Node) *Signature {
	if ast.IsInJSFile(node) && (ast.IsFunctionDeclaration(node) || ast.IsMethodDeclaration(node) || ast.IsFunctionExpressionOrArrowFunction(node)) && node.FunctionLikeData().FullSignature != nil {
		return c.getSingleCallSignature(c.getTypeFromTypeNode(node.FunctionLikeData().FullSignature))
	}
	return nil
}

func (c *Checker) getParameterTypeOfFullSignature(node *ast.Node, parameter *ast.ParameterDeclarationNode) *Type {
	if signature := c.getSignatureOfFullSignatureType(node); signature != nil {
		pos := slices.Index(node.Parameters(), parameter)
		if parameter.AsParameterDeclaration().DotDotDotToken != nil {
			return c.getRestTypeAtPosition(signature, pos, false /*readonly*/)
		} else {
			return c.getTypeAtPosition(signature, pos)
		}
	}
	return nil
}

func (c *Checker) getReturnTypeOfFullSignature(node *ast.Node) *Type {
	if signature := c.getSignatureOfFullSignatureType(node); signature != nil {
		return c.getReturnTypeOfSignature(signature)
	}
	return nil
}

func (c *Checker) getAnnotatedAccessorType(accessor *ast.Node) *Type {
	node := c.getAnnotatedAccessorTypeNode(accessor)
	if node != nil {
		return c.getTypeFromTypeNode(node)
	}
	return nil
}

func (c *Checker) getAnnotatedAccessorTypeNode(accessor *ast.Node) *ast.Node {
	if accessor != nil {
		switch accessor.Kind {
		case ast.KindGetAccessor, ast.KindPropertyDeclaration:
			return accessor.Type()
		case ast.KindSetAccessor:
			return getEffectiveSetAccessorTypeAnnotationNode(accessor)
		}
	}
	return nil
}

func getEffectiveSetAccessorTypeAnnotationNode(node *ast.Node) *ast.Node {
	param := GetSetAccessorValueParameter(node)
	if param != nil {
		return param.Type()
	}
	return nil
}

func (c *Checker) getReturnTypeFromBody(fn *ast.Node, checkMode CheckMode) *Type {
	body := fn.Body()
	if body == nil {
		return c.errorType
	}
	functionFlags := ast.GetFunctionFlags(fn)
	isAsync := (functionFlags & ast.FunctionFlagsAsync) != 0
	isGenerator := (functionFlags & ast.FunctionFlagsGenerator) != 0
	var returnType *Type
	var yieldType *Type
	var nextType *Type
	var fallbackReturnType *Type = c.voidType
	switch {
	case !ast.IsBlock(body):
		returnType = c.checkExpressionCachedEx(body, checkMode & ^CheckModeSkipGenericFunctions)
		if c.isConstContext(body) {
			returnType = c.getRegularTypeOfLiteralType(returnType)
		}
		if isAsync {
			// From within an async function you can return either a non-promise value or a promise. Any
			// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
			// return type of the body should be unwrapped to its awaited type, which we will wrap in
			// the native Promise<T> type later in this function.
			returnType = c.unwrapAwaitedType(c.checkAwaitedType(returnType, false /*withAlias*/, fn /*errorNode*/, diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member))
		}
	case isGenerator:
		returnTypes, isNeverReturning := c.checkAndAggregateReturnExpressionTypes(fn, checkMode)
		if isNeverReturning {
			fallbackReturnType = c.neverType
		} else if len(returnTypes) != 0 {
			returnType = c.getUnionTypeEx(returnTypes, UnionReductionSubtype, nil, nil)
		}
		yieldTypes, nextTypes := c.checkAndAggregateYieldOperandTypes(fn, checkMode)
		if len(yieldTypes) != 0 {
			yieldType = c.getUnionTypeEx(yieldTypes, UnionReductionSubtype, nil, nil)
		}
		if len(nextTypes) != 0 {
			nextType = c.getIntersectionType(nextTypes)
		}
	default:
		types, isNeverReturning := c.checkAndAggregateReturnExpressionTypes(fn, checkMode)
		if isNeverReturning {
			// For an async function, the return type will not be never, but rather a Promise for never.
			if functionFlags&ast.FunctionFlagsAsync != 0 {
				return c.createPromiseReturnType(fn, c.neverType)
			}
			// Normal function
			return c.neverType
		}
		if len(types) == 0 {
			// For an async function, the return type will not be void/undefined, but rather a Promise for void/undefined.
			contextualReturnType := c.getContextualReturnType(fn, ContextFlagsNone)
			var returnType *Type
			if contextualReturnType != nil && someType(core.OrElse(c.unwrapReturnType(contextualReturnType, functionFlags), c.voidType), func(t *Type) bool { return t.flags&TypeFlagsUndefined != 0 }) {
				returnType = c.undefinedType
			} else {
				returnType = c.voidType
			}
			if functionFlags&ast.FunctionFlagsAsync != 0 {
				return c.createPromiseReturnType(fn, returnType)
			}
			// Normal function
			return returnType
		}
		// Return a union of the return expression types.
		returnType = c.getUnionTypeEx(types, UnionReductionSubtype, nil, nil)
	}
	if returnType != nil || yieldType != nil || nextType != nil {
		if yieldType != nil {
			c.reportErrorsFromWidening(fn, yieldType, WideningKindGeneratorYield)
		}
		if returnType != nil {
			c.reportErrorsFromWidening(fn, returnType, WideningKindFunctionReturn)
		}
		if nextType != nil {
			c.reportErrorsFromWidening(fn, nextType, WideningKindGeneratorNext)
		}
		if returnType != nil && isUnitType(returnType) || yieldType != nil && isUnitType(yieldType) || nextType != nil && isUnitType(nextType) {
			contextualSignature := c.getContextualSignatureForFunctionLikeDeclaration(fn)
			var contextualType *Type
			switch {
			case contextualSignature == nil:
				// No contextual type
			case contextualSignature == c.getSignatureFromDeclaration(fn):
				if !isGenerator {
					contextualType = returnType
				}
			default:
				contextualType = c.instantiateContextualType(c.getReturnTypeOfSignature(contextualSignature), fn, ContextFlagsNone)
			}
			if isGenerator {
				yieldType = c.getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(yieldType, contextualType, IterationTypeKindYield, isAsync)
				returnType = c.getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(returnType, contextualType, IterationTypeKindReturn, isAsync)
				nextType = c.getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(nextType, contextualType, IterationTypeKindNext, isAsync)
			} else {
				returnType = c.getWidenedLiteralLikeTypeForContextualReturnTypeIfNeeded(returnType, contextualType, isAsync)
			}
		}
		if yieldType != nil {
			yieldType = c.getWidenedType(yieldType)
		}
		if returnType != nil {
			returnType = c.getWidenedType(returnType)
		}
		if nextType != nil {
			nextType = c.getWidenedType(nextType)
		}
	}
	if returnType == nil {
		returnType = fallbackReturnType
	}
	if isGenerator {
		if yieldType == nil {
			yieldType = c.neverType
		}
		if nextType == nil {
			nextType = c.getContextualIterationType(IterationTypeKindNext, fn)
			if nextType == nil {
				nextType = c.unknownType
			}
		}
		return c.createGeneratorType(yieldType, returnType, nextType, isAsync)
	}
	// From within an async function you can return either a non-promise value or a promise. Any
	// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
	// return type of the body is awaited type of the body, wrapped in a native Promise<T> type.
	if isAsync {
		return c.createPromiseType(returnType)
	}
	return returnType
}

// Returns the aggregated list of return types, plus a bool indicating a never-returning function.
func (c *Checker) checkAndAggregateReturnExpressionTypes(fn *ast.Node, checkMode CheckMode) ([]*Type, bool) {
	functionFlags := ast.GetFunctionFlags(fn)
	var aggregatedTypes []*Type
	hasReturnWithNoExpression := c.functionHasImplicitReturn(fn)
	hasReturnOfTypeNever := false
	ast.ForEachReturnStatement(fn.Body(), func(returnStatement *ast.Node) bool {
		expr := returnStatement.Expression()
		if expr == nil {
			hasReturnWithNoExpression = true
			return false
		}
		expr = ast.SkipParentheses(expr)
		// Bare calls to this same function don't contribute to inference
		// and `return await` is also safe to unwrap here
		if functionFlags&ast.FunctionFlagsAsync != 0 && ast.IsAwaitExpression(expr) {
			expr = ast.SkipParentheses(expr.Expression())
		}
		if ast.IsCallExpression(expr) && ast.IsIdentifier(expr.Expression()) && c.checkExpressionCached(expr.Expression()).symbol == c.getMergedSymbol(fn.Symbol()) &&
			(!ast.IsFunctionExpressionOrArrowFunction(fn.Symbol().ValueDeclaration) || c.isConstantReference(expr.Expression())) {
			hasReturnOfTypeNever = true
			return false
		}
		t := c.checkExpressionCachedEx(expr, checkMode & ^CheckModeSkipGenericFunctions)
		if functionFlags&ast.FunctionFlagsAsync != 0 {
			// From within an async function you can return either a non-promise value or a promise. Any
			// Promise/A+ compatible implementation will always assimilate any foreign promise, so the
			// return type of the body should be unwrapped to its awaited type, which should be wrapped in
			// the native Promise<T> type by the caller.
			t = c.unwrapAwaitedType(c.checkAwaitedType(t, false /*withAlias*/, fn, diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member))
		}
		if t.flags&TypeFlagsNever != 0 {
			hasReturnOfTypeNever = true
		}
		if c.isConstContext(expr) {
			t = c.getRegularTypeOfLiteralType(t)
		}
		aggregatedTypes = core.AppendIfUnique(aggregatedTypes, t)
		return false
	})
	if len(aggregatedTypes) == 0 && !hasReturnWithNoExpression && (hasReturnOfTypeNever || mayReturnNever(fn)) {
		return nil, true
	}
	if c.strictNullChecks && len(aggregatedTypes) != 0 && hasReturnWithNoExpression {
		aggregatedTypes = core.AppendIfUnique(aggregatedTypes, c.undefinedType)
	}
	return aggregatedTypes, false
}

func (c *Checker) functionHasImplicitReturn(fn *ast.Node) bool {
	endFlowNode := fn.BodyData().EndFlowNode
	return endFlowNode != nil && c.isReachableFlowNode(endFlowNode)
}

func mayReturnNever(fn *ast.Node) bool {
	switch fn.Kind {
	case ast.KindFunctionExpression, ast.KindArrowFunction:
		return true
	case ast.KindMethodDeclaration:
		return ast.IsObjectLiteralExpression(fn.Parent)
	}
	return false
}

func (c *Checker) checkAndAggregateYieldOperandTypes(fn *ast.Node, checkMode CheckMode) (yieldTypes []*Type, nextTypes []*Type) {
	isAsync := (ast.GetFunctionFlags(fn) & ast.FunctionFlagsAsync) != 0
	forEachYieldExpression(fn.Body(), func(yieldExpr *ast.Node) bool {
		yieldExprType := c.undefinedWideningType
		if yieldExpr.Expression() != nil {
			yieldExprType = c.checkExpressionEx(yieldExpr.Expression(), checkMode & ^CheckModeSkipGenericFunctions)
		}
		if yieldExpr.Expression() != nil && c.isConstContext(yieldExpr.Expression()) {
			yieldExprType = c.getRegularTypeOfLiteralType(yieldExprType)
		}
		yieldTypes = core.AppendIfUnique(yieldTypes, c.getYieldedTypeOfYieldExpression(yieldExpr, yieldExprType, c.anyType, isAsync))
		var nextType *Type
		if yieldExpr.AsYieldExpression().AsteriskToken != nil {
			iterationTypes := c.getIterationTypesOfIterable(yieldExprType, core.IfElse(isAsync, IterationUseAsyncYieldStar, IterationUseYieldStar), yieldExpr.Expression())
			nextType = iterationTypes.nextType
		} else {
			nextType = c.getContextualType(yieldExpr, ContextFlagsNone)
		}
		if nextType != nil {
			nextTypes = core.AppendIfUnique(nextTypes, nextType)
		}
		return false
	})
	return yieldTypes, nextTypes
}

func (c *Checker) createPromiseType(promisedType *Type) *Type {
	// creates a `Promise<T>` type where `T` is the promisedType argument
	globalPromiseType := c.getGlobalPromiseTypeChecked()
	if globalPromiseType != c.emptyGenericType {
		// if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type
		// Unwrap an `Awaited<T>` to `T` to improve inference.
		promisedType = core.OrElse(c.getAwaitedTypeNoAlias(c.unwrapAwaitedType(promisedType)), c.unknownType)
		return c.createTypeReference(globalPromiseType, []*Type{promisedType})
	}
	return c.unknownType
}

func (c *Checker) createPromiseLikeType(promisedType *Type) *Type {
	// creates a `PromiseLike<T>` type where `T` is the promisedType argument
	globalPromiseLikeType := c.getGlobalPromiseLikeType()
	if globalPromiseLikeType != c.emptyGenericType {
		// if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type
		// Unwrap an `Awaited<T>` to `T` to improve inference.
		promisedType = core.OrElse(c.getAwaitedTypeNoAlias(c.unwrapAwaitedType(promisedType)), c.unknownType)
		return c.createTypeReference(globalPromiseLikeType, []*Type{promisedType})
	}
	return c.unknownType
}

func (c *Checker) createPromiseReturnType(fn *ast.Node, promisedType *Type) *Type {
	promiseType := c.createPromiseType(promisedType)
	if promiseType == c.unknownType {
		c.error(fn, core.IfElse(ast.IsImportCall(fn),
			diagnostics.A_dynamic_import_call_returns_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option,
			diagnostics.An_async_function_or_method_must_return_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option))
		return c.errorType
	}
	if c.getGlobalPromiseConstructorSymbol() == nil {
		c.error(fn, core.IfElse(ast.IsImportCall(fn),
			diagnostics.A_dynamic_import_call_in_ES5_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option,
			diagnostics.An_async_function_or_method_in_ES5_requires_the_Promise_constructor_Make_sure_you_have_a_declaration_for_the_Promise_constructor_or_include_ES2015_in_your_lib_option))
	}
	return promiseType
}

func (c *Checker) unwrapReturnType(returnType *Type, functionFlags ast.FunctionFlags) *Type {
	isGenerator := functionFlags&ast.FunctionFlagsGenerator != 0
	isAsync := functionFlags&ast.FunctionFlagsAsync != 0
	if isGenerator {
		returnIterationType := c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, returnType, isAsync)
		if returnIterationType == nil {
			return c.errorType
		}
		if isAsync {
			return c.getAwaitedTypeNoAlias(c.unwrapAwaitedType(returnIterationType))
		}
		return returnIterationType
	}
	if isAsync {
		return core.OrElse(c.getAwaitedTypeNoAlias(returnType), c.errorType)
	}
	return returnType
}

func (c *Checker) getWidenedLiteralLikeTypeForContextualReturnTypeIfNeeded(t *Type, contextualSignatureReturnType *Type, isAsync bool) *Type {
	if t != nil && isUnitType(t) {
		var contextualType *Type
		switch {
		case contextualSignatureReturnType == nil:
			// No contextual type
		case isAsync:
			contextualType = c.GetPromisedTypeOfPromise(contextualSignatureReturnType)
		default:
			contextualType = contextualSignatureReturnType
		}
		t = c.getWidenedLiteralLikeTypeForContextualType(t, contextualType)
	}
	return t
}

func (c *Checker) getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(t *Type, contextualSignatureReturnType *Type, kind IterationTypeKind, isAsyncGenerator bool) *Type {
	if t != nil && isUnitType(t) {
		var contextualType *Type
		if contextualSignatureReturnType != nil {
			contextualType = c.getIterationTypeOfGeneratorFunctionReturnType(kind, contextualSignatureReturnType, isAsyncGenerator)
		}
		t = c.getWidenedLiteralLikeTypeForContextualType(t, contextualType)
	}
	return t
}

func (c *Checker) createGeneratorType(yieldType *Type, returnType *Type, nextType *Type, isAsyncGenerator bool) *Type {
	resolver := core.IfElse(isAsyncGenerator, c.asyncIterationTypesResolver, c.syncIterationTypesResolver)
	globalGeneratorType := resolver.getGlobalGeneratorType()
	yieldType = core.OrElse(resolver.resolveIterationType(yieldType, nil /*errorNode*/), c.unknownType)
	returnType = core.OrElse(resolver.resolveIterationType(returnType, nil /*errorNode*/), c.unknownType)
	if globalGeneratorType == c.emptyGenericType {
		// Fall back to the global IterableIterator type.
		globalIterableIteratorType := resolver.getGlobalIterableIteratorType()
		if globalIterableIteratorType != c.emptyGenericType {
			return c.createTypeFromGenericGlobalType(globalIterableIteratorType, []*Type{yieldType, returnType, nextType})
		}
		// The global Generator type doesn't exist, so report an error
		resolver.getGlobalIterableIteratorTypeChecked()
		return c.emptyObjectType
	}
	return c.createTypeFromGenericGlobalType(globalGeneratorType, []*Type{yieldType, returnType, nextType})
}

func (c *Checker) reportErrorsFromWidening(declaration *ast.Node, t *Type, wideningKind WideningKind) {
	if c.noImplicitAny && t.objectFlags&ObjectFlagsContainsWideningType != 0 {
		if wideningKind == WideningKindNormal || ast.IsFunctionLikeDeclaration(declaration) && c.shouldReportErrorsFromWideningWithContextualSignature(declaration, wideningKind) {
			// Report implicit any error within type if possible, otherwise report error on declaration
			if !c.reportWideningErrorsInType(t) {
				c.reportImplicitAny(declaration, t, wideningKind)
			}
		}
	}
}

func (c *Checker) shouldReportErrorsFromWideningWithContextualSignature(declaration *ast.Node, wideningKind WideningKind) bool {
	signature := c.getContextualSignatureForFunctionLikeDeclaration(declaration)
	if signature == nil {
		return true
	}
	returnType := c.getReturnTypeOfSignature(signature)
	flags := ast.GetFunctionFlags(declaration)
	switch wideningKind {
	case WideningKindFunctionReturn:
		if flags&ast.FunctionFlagsGenerator != 0 {
			returnType = core.OrElse(c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, returnType, flags&ast.FunctionFlagsAsync != 0), returnType)
		} else if flags&ast.FunctionFlagsAsync != 0 {
			returnType = core.OrElse(c.getAwaitedTypeNoAlias(returnType), returnType)
		}
		return c.isGenericType(returnType)
	case WideningKindGeneratorYield:
		yieldType := c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindYield, returnType, flags&ast.FunctionFlagsAsync != 0)
		return yieldType != nil && c.isGenericType(yieldType)
	case WideningKindGeneratorNext:
		nextType := c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindNext, returnType, flags&ast.FunctionFlagsAsync != 0)
		return nextType != nil && c.isGenericType(nextType)
	}
	return false
}

// Reports implicit any errors that occur as a result of widening 'null' and 'undefined'
// to 'any'. A call to reportWideningErrorsInType is normally accompanied by a call to
// getWidenedType. But in some cases getWidenedType is called without reporting errors
// (type argument inference is an example).
//
// The return value indicates whether an error was in fact reported. The particular circumstances
// are on a best effort basis. Currently, if the null or undefined that causes widening is inside
// an object literal property (arbitrarily deeply), this function reports an error. If no error is
// reported, reportImplicitAnyError is a suitable fallback to report a general error.
func (c *Checker) reportWideningErrorsInType(t *Type) bool {
	errorReported := false
	if t.objectFlags&ObjectFlagsContainsWideningType != 0 {
		if t.flags&TypeFlagsUnion != 0 {
			if core.Some(t.Types(), c.isEmptyObjectType) {
				errorReported = true
			} else {
				for _, s := range t.Types() {
					errorReported = errorReported || c.reportWideningErrorsInType(s)
				}
			}
		} else if c.isArrayOrTupleType(t) {
			for _, s := range c.getTypeArguments(t) {
				errorReported = errorReported || c.reportWideningErrorsInType(s)
			}
		} else if isObjectLiteralType(t) {
			for _, p := range c.getPropertiesOfObjectType(t) {
				s := c.getTypeOfSymbol(p)
				if s.objectFlags&ObjectFlagsContainsWideningType != 0 {
					errorReported = c.reportWideningErrorsInType(s)
					if !errorReported {
						// we need to account for property types coming from object literal type normalization in unions
						valueDeclaration := core.Find(p.Declarations, func(d *ast.Node) bool {
							valueDeclaration := d.Symbol().ValueDeclaration
							return valueDeclaration != nil && valueDeclaration.Parent == t.symbol.ValueDeclaration
						})
						if valueDeclaration != nil {
							c.error(valueDeclaration, diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, c.symbolToString(p), c.TypeToString(c.getWidenedType(s)))
							errorReported = true
						}
					}
				}
			}
		}
	}
	return errorReported
}

func (c *Checker) getTypePredicateFromBody(fn *ast.Node) *TypePredicate {
	switch fn.Kind {
	case ast.KindConstructor, ast.KindGetAccessor, ast.KindSetAccessor:
		return nil
	}
	functionFlags := ast.GetFunctionFlags(fn)
	if functionFlags != ast.FunctionFlagsNormal {
		return nil
	}
	// Only attempt to infer a type predicate if there's exactly one return.
	var singleReturn *ast.Node
	body := fn.Body()
	if body != nil && !ast.IsBlock(body) {
		// arrow function
		singleReturn = body
	} else {
		bailedEarly := ast.ForEachReturnStatement(body, func(returnStatement *ast.Node) bool {
			if singleReturn != nil || returnStatement.Expression() == nil {
				return true
			}
			singleReturn = returnStatement.Expression()
			return false
		})
		if bailedEarly || singleReturn == nil || c.functionHasImplicitReturn(fn) {
			return nil
		}
	}
	return c.checkIfExpressionRefinesAnyParameter(fn, singleReturn)
}

func (c *Checker) checkIfExpressionRefinesAnyParameter(fn *ast.Node, expr *ast.Node) *TypePredicate {
	expr = ast.SkipParentheses(expr)
	returnType := c.checkExpressionCached(expr)
	if returnType.flags&TypeFlagsBoolean == 0 {
		return nil
	}
	for i, param := range fn.Parameters() {
		initType := c.getTypeOfSymbol(param.Symbol())
		if initType == nil || initType.flags&TypeFlagsBoolean != 0 || !ast.IsIdentifier(param.Name()) || c.isSymbolAssigned(param.Symbol()) || isRestParameter(param) {
			// Refining "x: boolean" to "x is true" or "x is false" isn't useful.
			continue
		}
		trueType := c.checkIfExpressionRefinesParameter(fn, expr, param, initType)
		if trueType != nil {
			return c.newTypePredicate(TypePredicateKindIdentifier, param.Name().Text(), int32(i), trueType)
		}
	}
	return nil
}

func (c *Checker) checkIfExpressionRefinesParameter(fn *ast.Node, expr *ast.Node, param *ast.Node, initType *Type) *Type {
	antecedent := getFlowNodeOfNode(expr)
	if antecedent == nil && ast.IsReturnStatement(expr.Parent) {
		antecedent = getFlowNodeOfNode(expr.Parent)
	}
	if antecedent == nil {
		antecedent = &ast.FlowNode{Flags: ast.FlowFlagsStart}
	}
	trueCondition := &ast.FlowNode{Flags: ast.FlowFlagsTrueCondition, Node: expr, Antecedent: antecedent}
	trueType := c.getFlowTypeOfReferenceEx(param.Name(), initType, initType, fn, trueCondition)
	if trueType == initType {
		return nil
	}
	// "x is T" means that x is T if and only if it returns true. If it returns false then x is not T.
	// This means that if the function is called with an argument of type trueType, there can't be anything left in the `else` branch. It must reduce to `never`.
	falseCondition := &ast.FlowNode{Flags: ast.FlowFlagsFalseCondition, Node: expr, Antecedent: antecedent}
	falseSubtype := c.getReducedType(c.getFlowTypeOfReferenceEx(param.Name(), initType, trueType, fn, falseCondition))
	if falseSubtype.flags&TypeFlagsNever != 0 {
		return trueType
	}
	return nil
}

func (c *Checker) addOptionalTypeMarker(t *Type) *Type {
	if c.strictNullChecks {
		return c.getUnionType([]*Type{t, c.optionalType})
	}
	return t
}

func (c *Checker) instantiateSignature(sig *Signature, m *TypeMapper) *Signature {
	return c.instantiateSignatureEx(sig, m, m == c.permissiveMapper /*eraseTypeParameters*/)
}

func (c *Checker) instantiateSignatureEx(sig *Signature, m *TypeMapper, eraseTypeParameters bool) *Signature {
	var freshTypeParameters []*Type
	if len(sig.typeParameters) != 0 && !eraseTypeParameters {
		// First create a fresh set of type parameters, then include a mapping from the old to the
		// new type parameters in the mapper function. Finally store this mapper in the new type
		// parameters such that we can use it when instantiating constraints.
		freshTypeParameters = core.Map(sig.typeParameters, c.cloneTypeParameter)
		m = c.combineTypeMappers(newTypeMapper(sig.typeParameters, freshTypeParameters), m)
		for _, tp := range freshTypeParameters {
			tp.AsTypeParameter().mapper = m
		}
	}
	// Don't compute resolvedReturnType and resolvedTypePredicate now,
	// because using `mapper` now could trigger inferences to become fixed. (See `createInferenceContext`.)
	// See GH#17600.
	result := c.newSignature(sig.flags&SignatureFlagsPropagatingFlags, sig.declaration, freshTypeParameters,
		c.instantiateSymbol(sig.thisParameter, m), c.instantiateSymbols(sig.parameters, m),
		nil /*resolvedReturnType*/, nil /*resolvedTypePredicate*/, int(sig.minArgumentCount))
	result.target = sig
	result.mapper = m
	return result
}

func (c *Checker) instantiateIndexInfo(info *IndexInfo, m *TypeMapper) *IndexInfo {
	newValueType := c.instantiateType(info.valueType, m)
	if newValueType == info.valueType {
		return info
	}
	return c.newIndexInfo(info.keyType, newValueType, info.isReadonly, info.declaration, info.components)
}

func (c *Checker) resolveAnonymousTypeMembers(t *Type) {
	d := t.AsObjectType()
	if d.target != nil {
		c.setStructuredTypeMembers(t, nil, nil, nil, nil)
		members := c.createInstantiatedSymbolTable(c.getPropertiesOfObjectType(d.target), d.mapper)
		callSignatures := c.instantiateSignatures(c.getSignaturesOfType(d.target, SignatureKindCall), d.mapper)
		constructSignatures := c.instantiateSignatures(c.getSignaturesOfType(d.target, SignatureKindConstruct), d.mapper)
		indexInfos := c.instantiateIndexInfos(c.getIndexInfosOfType(d.target), d.mapper)
		c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos)
		return
	}
	symbol := c.getMergedSymbol(t.symbol)
	if symbol.Flags&ast.SymbolFlagsTypeLiteral != 0 {
		c.setStructuredTypeMembers(t, nil, nil, nil, nil)
		members := c.getMembersOfSymbol(symbol)
		callSignatures := c.getSignaturesOfSymbol(members[ast.InternalSymbolNameCall])
		constructSignatures := c.getSignaturesOfSymbol(members[ast.InternalSymbolNameNew])
		indexInfos := c.getIndexInfosOfSymbol(symbol)
		c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos)
		return
	}
	// Combinations of function, class, enum and module
	members := c.getExportsOfSymbol(symbol)
	var indexInfos []*IndexInfo
	if symbol == c.globalThisSymbol {
		varsOnly := make(ast.SymbolTable)
		for _, p := range members {
			if p.Flags&ast.SymbolFlagsBlockScoped == 0 && !(p.Flags&ast.SymbolFlagsValueModule != 0 && len(p.Declarations) != 0 && core.Every(p.Declarations, ast.IsAmbientModule)) {
				varsOnly[p.Name] = p
			}
		}
		members = varsOnly
	}
	var baseConstructorIndexInfo *IndexInfo
	c.setStructuredTypeMembers(t, members, nil, nil, nil)
	if symbol.Flags&ast.SymbolFlagsClass != 0 {
		classType := c.getDeclaredTypeOfClassOrInterface(symbol)
		baseConstructorType := c.getBaseConstructorTypeOfClass(classType)
		if baseConstructorType.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsTypeVariable) != 0 {
			members = maps.Clone(members)
			c.addInheritedMembers(members, c.getPropertiesOfType(baseConstructorType))
			c.setStructuredTypeMembers(t, members, nil, nil, nil)
		} else if baseConstructorType == c.anyType {
			baseConstructorIndexInfo = c.anyBaseTypeIndexInfo
		}
	}
	indexSymbol := members[ast.InternalSymbolNameIndex]
	if indexSymbol != nil {
		indexInfos = c.getIndexInfosOfIndexSymbol(indexSymbol, slices.Collect(maps.Values(members)))
	} else {
		if baseConstructorIndexInfo != nil {
			indexInfos = append(indexInfos, baseConstructorIndexInfo)
		}
		if symbol.Flags&ast.SymbolFlagsEnum != 0 && (c.getDeclaredTypeOfSymbol(symbol).flags&TypeFlagsEnum != 0 || core.Some(d.properties, func(prop *ast.Symbol) bool {
			return c.getTypeOfSymbol(prop).flags&TypeFlagsNumberLike != 0
		})) {
			indexInfos = append(indexInfos, c.enumNumberIndexInfo)
		}
	}
	d.indexInfos = indexInfos
	// We resolve the members before computing the signatures because a signature may use
	// typeof with a qualified name expression that circularly references the type we are
	// in the process of resolving (see issue #6072). The temporarily empty signature list
	// will never be observed because a qualified name can't reference signatures.
	if symbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0 {
		d.signatures = c.getSignaturesOfSymbol(symbol)
		d.callSignatureCount = len(d.signatures)
	}
	// And likewise for construct signatures for classes
	if symbol.Flags&ast.SymbolFlagsClass != 0 {
		classType := c.getDeclaredTypeOfClassOrInterface(symbol)
		constructSignatures := c.getSignaturesOfSymbol(symbol.Members[ast.InternalSymbolNameConstructor])
		if len(constructSignatures) == 0 {
			constructSignatures = c.getDefaultConstructSignatures(classType)
		}
		d.signatures = append(d.signatures, constructSignatures...)
	}
}

func (c *Checker) createInstantiatedSymbolTable(symbols []*ast.Symbol, m *TypeMapper) ast.SymbolTable {
	if len(symbols) == 0 {
		return nil
	}
	result := make(ast.SymbolTable)
	for _, symbol := range symbols {
		result[symbol.Name] = c.instantiateSymbol(symbol, m)
	}
	return result
}

func (c *Checker) instantiateSymbolTable(symbols ast.SymbolTable, m *TypeMapper) ast.SymbolTable {
	if len(symbols) == 0 {
		return nil
	}
	result := make(ast.SymbolTable, len(symbols))
	for id, symbol := range symbols {
		if c.isNamedMember(symbol, id) {
			result[id] = c.instantiateSymbol(symbol, m)
		}
	}
	return result
}

func (c *Checker) instantiateSymbol(symbol *ast.Symbol, m *TypeMapper) *ast.Symbol {
	if symbol == nil {
		return nil
	}
	links := c.valueSymbolLinks.Get(symbol)
	if m != nil && m.MapsThisOnly() && isThisless(symbol) {
		return symbol
	}
	// If the type of the symbol is already resolved, and if that type could not possibly
	// be affected by instantiation, simply return the symbol itself.
	if links.resolvedType != nil && !c.couldContainTypeVariables(links.resolvedType) {
		if symbol.Flags&ast.SymbolFlagsSetAccessor == 0 {
			return symbol
		}
		// If we're a setter, check writeType.
		if links.writeType != nil && !c.couldContainTypeVariables(links.writeType) {
			return symbol
		}
	}
	if symbol.CheckFlags&ast.CheckFlagsInstantiated != 0 {
		// If symbol being instantiated is itself a instantiation, fetch the original target and combine the
		// type mappers. This ensures that original type identities are properly preserved and that aliases
		// always reference a non-aliases.
		symbol = links.target
		m = c.combineTypeMappers(links.mapper, m)
	}
	// Keep the flags from the symbol we're instantiating.  Mark that is instantiated, and
	// also transient so that we can just store data on it directly.
	result := c.newSymbol(symbol.Flags, symbol.Name)
	result.CheckFlags = ast.CheckFlagsInstantiated | symbol.CheckFlags&(ast.CheckFlagsReadonly|ast.CheckFlagsLate|ast.CheckFlagsOptionalParameter|ast.CheckFlagsRestParameter)
	result.Declarations = symbol.Declarations
	result.Parent = symbol.Parent
	result.ValueDeclaration = symbol.ValueDeclaration
	resultLinks := c.valueSymbolLinks.Get(result)
	resultLinks.target = symbol
	resultLinks.mapper = m
	resultLinks.nameType = links.nameType
	return result
}

// Returns true if the parameter or class/interface member given by the symbol is free of "this" references. The
// function may return false for symbols that are actually free of "this" references because it is not
// feasible to perform a complete analysis in all cases. In particular, property members with types
// inferred from their initializers and function members with inferred return types are conservatively
// assumed not to be free of "this" references.
func isThisless(symbol *ast.Symbol) bool {
	if len(symbol.Declarations) == 1 {
		declaration := symbol.Declarations[0]
		if declaration != nil {
			switch declaration.Kind {
			case ast.KindParameter:
				return isThislessVariableLikeDeclaration(declaration)
			case ast.KindPropertyDeclaration, ast.KindPropertySignature:
				return isThislessVariableLikeDeclaration(declaration)
			case ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor, ast.KindGetAccessor, ast.KindSetAccessor:
				return isThislessFunctionLikeDeclaration(declaration)
			}
		}
	}
	return false
}

// A variable-like declaration is free of this references if it has a type annotation
// that is thisless, or if it has no type annotation and no initializer (and is thus of type any).
func isThislessVariableLikeDeclaration(node *ast.Node) bool {
	typeNode := node.Type()
	if typeNode != nil {
		return isThislessType(typeNode)
	}
	return node.Initializer() == nil
}

// A type is free of this references if it's the any, string, number, boolean, symbol, or void keyword, a string
// literal type, an array with an element type that is free of this references, or a type reference that is
// free of this references.
func isThislessType(node *ast.Node) bool {
	switch node.Kind {
	case ast.KindAnyKeyword, ast.KindUnknownKeyword, ast.KindStringKeyword, ast.KindNumberKeyword, ast.KindBigIntKeyword, ast.KindBooleanKeyword,
		ast.KindSymbolKeyword, ast.KindObjectKeyword, ast.KindVoidKeyword, ast.KindUndefinedKeyword, ast.KindNeverKeyword, ast.KindLiteralType:
		return true
	case ast.KindArrayType:
		return isThislessType(node.AsArrayTypeNode().ElementType)
	case ast.KindTypeReference:
		return core.Every(node.TypeArguments(), isThislessType)
	}
	return false
}

// A function-like declaration is considered free of `this` references if it has a return type
// annotation that is free of this references and if each parameter is thisless and if
// each type parameter (if present) is thisless.
func isThislessFunctionLikeDeclaration(node *ast.Node) bool {
	returnType := node.Type()
	return (ast.IsConstructorDeclaration(node) || returnType != nil && isThislessType(returnType)) &&
		core.Every(node.Parameters(), isThislessVariableLikeDeclaration) &&
		core.Every(node.TypeParameters(), isThislessTypeParameter)
}

// A type parameter is thisless if its constraint is thisless, or if it has no constraint. */
func isThislessTypeParameter(node *ast.Node) bool {
	constraint := node.AsTypeParameterDeclaration().Constraint
	return constraint == nil || isThislessType(constraint)
}

func (c *Checker) getDefaultConstructSignatures(classType *Type) []*Signature {
	baseConstructorType := c.getBaseConstructorTypeOfClass(classType)
	baseSignatures := c.getSignaturesOfType(baseConstructorType, SignatureKindConstruct)
	declaration := ast.GetClassLikeDeclarationOfSymbol(classType.symbol)
	isAbstract := declaration != nil && ast.HasSyntacticModifier(declaration, ast.ModifierFlagsAbstract)
	if len(baseSignatures) == 0 {
		flags := core.IfElse(isAbstract, SignatureFlagsConstruct|SignatureFlagsAbstract, SignatureFlagsConstruct)
		return []*Signature{c.newSignature(flags, nil, classType.AsInterfaceType().LocalTypeParameters(), nil, nil, classType, nil, 0)}
	}
	baseTypeNode := getBaseTypeNodeOfClass(classType)
	isJavaScript := declaration != nil && ast.IsInJSFile(declaration)
	typeArguments := c.getTypeArgumentsFromNode(baseTypeNode)
	typeArgCount := len(typeArguments)
	var result []*Signature
	for _, baseSig := range baseSignatures {
		minTypeArgumentCount := c.getMinTypeArgumentCount(baseSig.typeParameters)
		typeParamCount := len(baseSig.typeParameters)
		if isJavaScript || typeArgCount >= minTypeArgumentCount && typeArgCount <= typeParamCount {
			var sig *Signature
			if typeParamCount != 0 {
				sig = c.createSignatureInstantiation(baseSig, c.fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, isJavaScript))
			} else {
				sig = c.cloneSignature(baseSig)
			}
			sig.typeParameters = classType.AsInterfaceType().LocalTypeParameters()
			sig.resolvedReturnType = classType
			if isAbstract {
				sig.flags |= SignatureFlagsAbstract
			} else {
				sig.flags &^= SignatureFlagsAbstract
			}
			result = append(result, sig)
		}
	}
	return result
}

func (c *Checker) resolveMappedTypeMembers(t *Type) {
	members := make(ast.SymbolTable)
	var indexInfos []*IndexInfo
	// Resolve upfront such that recursive references see an empty object type.
	c.setStructuredTypeMembers(t, nil, nil, nil, nil)
	// In { [P in K]: T }, we refer to P as the type parameter type, K as the constraint type,
	// and T as the template type.
	typeParameter := c.getTypeParameterFromMappedType(t)
	constraintType := c.getConstraintTypeFromMappedType(t)
	mappedType := core.OrElse(t.AsMappedType().target, t)
	nameType := c.getNameTypeFromMappedType(mappedType)
	shouldLinkPropDeclarations := c.getMappedTypeNameTypeKind(mappedType) != MappedTypeNameTypeKindRemapping
	templateType := c.getTemplateTypeFromMappedType(mappedType)
	modifiersType := c.getApparentType(c.getModifiersTypeFromMappedType(t))
	// The 'T' in 'keyof T'
	templateModifiers := getMappedTypeModifiers(t)
	include := TypeFlagsStringOrNumberLiteralOrUnique
	addMemberForKeyTypeWorker := func(keyType *Type, propNameType *Type) {
		// If the current iteration type constituent is a string literal type, create a property.
		// Otherwise, for type string create a string index signature.
		if isTypeUsableAsPropertyName(propNameType) {
			propName := getPropertyNameFromType(propNameType)
			// String enum members from separate enums with identical values
			// are distinct types with the same property name. Make the resulting
			// property symbol's name type be the union of those enum member types.
			if existingProp := members[propName]; existingProp != nil {
				valueLinks := c.valueSymbolLinks.Get(existingProp)
				valueLinks.nameType = c.getUnionType([]*Type{valueLinks.nameType, propNameType})
				mappedLinks := c.mappedSymbolLinks.Get(existingProp)
				mappedLinks.keyType = c.getUnionType([]*Type{mappedLinks.keyType, keyType})
			} else {
				var modifiersProp *ast.Symbol
				if isTypeUsableAsPropertyName(keyType) {
					modifiersProp = c.getPropertyOfType(modifiersType, getPropertyNameFromType(keyType))
				}
				isOptional := templateModifiers&MappedTypeModifiersIncludeOptional != 0 || templateModifiers&MappedTypeModifiersExcludeOptional == 0 && modifiersProp != nil && modifiersProp.Flags&ast.SymbolFlagsOptional != 0
				isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersProp != nil && c.isReadonlySymbol(modifiersProp)
				stripOptional := c.strictNullChecks && !isOptional && modifiersProp != nil && modifiersProp.Flags&ast.SymbolFlagsOptional != 0
				var lateFlag ast.CheckFlags
				if modifiersProp != nil {
					lateFlag = modifiersProp.CheckFlags & ast.CheckFlagsLate
				}
				prop := c.newSymbol(ast.SymbolFlagsProperty|core.IfElse(isOptional, ast.SymbolFlagsOptional, 0), propName)
				prop.CheckFlags = lateFlag | ast.CheckFlagsMapped | core.IfElse(isReadonly, ast.CheckFlagsReadonly, 0) | core.IfElse(stripOptional, ast.CheckFlagsStripOptional, 0)
				valueLinks := c.valueSymbolLinks.Get(prop)
				valueLinks.containingType = t
				valueLinks.nameType = propNameType
				mappedLinks := c.mappedSymbolLinks.Get(prop)
				mappedLinks.keyType = keyType
				if modifiersProp != nil {
					mappedLinks.syntheticOrigin = modifiersProp
					if shouldLinkPropDeclarations {
						prop.Declarations = modifiersProp.Declarations
					}
				}
				members[propName] = prop
			}
		} else if c.isValidIndexKeyType(propNameType) || propNameType.flags&(TypeFlagsAny|TypeFlagsEnum) != 0 {
			indexKeyType := propNameType
			switch {
			case propNameType.flags&(TypeFlagsAny|TypeFlagsString) != 0:
				indexKeyType = c.stringType
			case propNameType.flags&(TypeFlagsNumber|TypeFlagsEnum) != 0:
				indexKeyType = c.numberType
			}
			propType := c.instantiateType(templateType, appendTypeMapping(t.AsMappedType().mapper, typeParameter, keyType))
			modifiersIndexInfo := c.getApplicableIndexInfo(modifiersType, propNameType)
			isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersIndexInfo != nil && modifiersIndexInfo.isReadonly
			indexInfo := c.newIndexInfo(indexKeyType, propType, isReadonly, nil, nil)
			indexInfos = c.appendIndexInfo(indexInfos, indexInfo, true /*union*/)
		}
	}
	addMemberForKeyType := func(keyType *Type) {
		propNameType := keyType
		if nameType != nil {
			propNameType = c.instantiateType(nameType, appendTypeMapping(t.AsMappedType().mapper, typeParameter, keyType))
		}
		forEachType(propNameType, func(t *Type) {
			addMemberForKeyTypeWorker(keyType, t)
		})
	}
	if c.isMappedTypeWithKeyofConstraintDeclaration(t) {
		// We have a { [P in keyof T]: X }
		c.forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, include, false /*stringsOnly*/, addMemberForKeyType)
	} else {
		forEachType(c.getLowerBoundOfKeyType(constraintType), addMemberForKeyType)
	}
	c.setStructuredTypeMembers(t, members, nil, nil, indexInfos)
}

func (c *Checker) getTypeOfMappedSymbol(symbol *ast.Symbol) *Type {
	links := c.valueSymbolLinks.Get(symbol)
	if links.resolvedType == nil {
		mappedType := links.containingType
		if !c.pushTypeResolution(symbol, TypeSystemPropertyNameType) {
			mappedType.AsMappedType().containsError = true
			return c.errorType
		}
		templateType := c.getTemplateTypeFromMappedType(core.OrElse(mappedType.AsMappedType().target, mappedType))
		mapper := appendTypeMapping(mappedType.AsMappedType().mapper, c.getTypeParameterFromMappedType(mappedType), c.mappedSymbolLinks.Get(symbol).keyType)
		propType := c.instantiateType(templateType, mapper)
		// When creating an optional property in strictNullChecks mode, if 'undefined' isn't assignable to the
		// type, we include 'undefined' in the type. Similarly, when creating a non-optional property in strictNullChecks
		// mode, if the underlying property is optional we remove 'undefined' from the type.
		switch {
		case c.strictNullChecks && symbol.Flags&ast.SymbolFlagsOptional != 0 && !c.maybeTypeOfKind(propType, TypeFlagsUndefined|TypeFlagsVoid):
			propType = c.getOptionalType(propType, true /*isProperty*/)
		case symbol.CheckFlags&ast.CheckFlagsStripOptional != 0:
			propType = c.removeMissingOrUndefinedType(propType)
		}
		if c.popTypeResolution() {
			if links.resolvedType == nil {
				links.resolvedType = propType
			}
		} else {
			if links.resolvedType == nil {
				links.resolvedType = c.errorType
			}
			c.error(c.currentNode, diagnostics.Type_of_property_0_circularly_references_itself_in_mapped_type_1, c.symbolToString(symbol), c.TypeToString(mappedType))
		}
	}
	return links.resolvedType
}

// Return the lower bound of the key type in a mapped type. Intuitively, the lower
// bound includes those keys that are known to always be present, for example because
// because of constraints on type parameters (e.g. 'keyof T' for a constrained T).
func (c *Checker) getLowerBoundOfKeyType(t *Type) *Type {
	switch {
	case t.flags&TypeFlagsIndex != 0:
		t := c.getApparentType(t.AsIndexType().target)
		if c.isGenericTupleType(t) {
			return c.getKnownKeysOfTupleType(t)
		}
		return c.getIndexType(t)
	case t.flags&TypeFlagsConditional != 0:
		if t.AsConditionalType().root.isDistributive {
			checkType := t.AsConditionalType().checkType
			constraint := c.getLowerBoundOfKeyType(checkType)
			if constraint != checkType {
				return c.getConditionalTypeInstantiation(t, prependTypeMapping(t.AsConditionalType().root.checkType, constraint, t.AsConditionalType().mapper), false /*forConstraint*/, nil)
			}
		}
		return t
	case t.flags&TypeFlagsUnion != 0:
		return c.mapTypeEx(t, c.getLowerBoundOfKeyType, true /*noReductions*/)
	case t.flags&TypeFlagsIntersection != 0:
		// Similarly to getTypeFromIntersectionTypeNode, we preserve the special string & {}, number & {},
		// and bigint & {} intersections that are used to prevent subtype reduction in union types.
		types := t.Types()
		if len(types) == 2 && types[0].flags&(TypeFlagsString|TypeFlagsNumber|TypeFlagsBigInt) != 0 && types[1] == c.emptyTypeLiteralType {
			return t
		}
		return c.getIntersectionType(core.SameMap(t.Types(), c.getLowerBoundOfKeyType))
	}
	return t
}

func (c *Checker) resolveUnionTypeMembers(t *Type) {
	// The members and properties collections are empty for union types. To get all properties of a union
	// type use getPropertiesOfType (only the language service uses this).
	callSignatures := c.getUnionSignatures(core.Map(t.Types(), func(t *Type) []*Signature {
		if t == c.globalFunctionType {
			return []*Signature{c.unknownSignature}
		}
		return c.getSignaturesOfType(t, SignatureKindCall)
	}))
	if len(callSignatures) == 0 {
		callSignatures = c.getArrayMemberCallSignatures(t)
	}
	constructSignatures := c.getUnionSignatures(core.Map(t.Types(), func(t *Type) []*Signature {
		return c.getSignaturesOfType(t, SignatureKindConstruct)
	}))
	indexInfos := c.getUnionIndexInfos(t.Types())
	c.setStructuredTypeMembers(t, nil, callSignatures, constructSignatures, indexInfos)
}

func (c *Checker) getArrayMemberCallSignatures(t *Type) []*Signature {
	// Check if union is exclusively instantiations of a member of the global Array or ReadonlyArray type.
	var memberName string
	for i, t := range t.Types() {
		if t.objectFlags&ObjectFlagsInstantiated == 0 || t.symbol == nil || t.symbol.Parent == nil || !c.isArrayOrTupleSymbol(t.symbol.Parent) {
			return nil
		}
		if i == 0 {
			memberName = t.symbol.Name
		} else if memberName != t.symbol.Name {
			return nil
		}
	}
	// Transform the type from `(A[] | B[])["member"]` to `(A | B)[]["member"]` (since we pretend array is covariant anyway).
	arrayArg := c.mapType(t, func(t *Type) *Type {
		return t.Mapper().Map(core.IfElse(c.isReadonlyArraySymbol(t.symbol.Parent), c.globalReadonlyArrayType, c.globalArrayType).AsInterfaceType().TypeParameters()[0])
	})
	arrayType := c.createArrayTypeEx(arrayArg, someType(t, func(t *Type) bool {
		return c.isReadonlyArraySymbol(t.symbol.Parent)
	}))
	return c.getSignaturesOfType(c.getTypeOfPropertyOfType(arrayType, memberName), SignatureKindCall)
}

func (c *Checker) isArrayOrTupleSymbol(symbol *ast.Symbol) bool {
	if symbol == nil || c.globalArrayType.symbol == nil || c.globalReadonlyArrayType.symbol == nil {
		return false
	}
	return c.getSymbolIfSameReference(symbol, c.globalArrayType.symbol) != nil || c.getSymbolIfSameReference(symbol, c.globalReadonlyArrayType.symbol) != nil
}

func (c *Checker) isReadonlyArraySymbol(symbol *ast.Symbol) bool {
	if symbol == nil || c.globalReadonlyArrayType.symbol == nil {
		return false
	}
	return c.getSymbolIfSameReference(symbol, c.globalReadonlyArrayType.symbol) != nil
}

// The signatures of a union type are those signatures that are present in each of the constituent types.
// Generic signatures must match exactly, but non-generic signatures are allowed to have extra optional
// parameters and may differ in return types. When signatures differ in return types, the resulting return
// type is the union of the constituent return types.
func (c *Checker) getUnionSignatures(signatureLists [][]*Signature) []*Signature {
	var result []*Signature
	var indexWithLengthOverOne int
	var countLengthOverOne int
	for i := range signatureLists {
		if len(signatureLists[i]) == 0 {
			return nil
		}
		if len(signatureLists[i]) > 1 {
			indexWithLengthOverOne = i
			countLengthOverOne++
		}
		for _, signature := range signatureLists[i] {
			// Only process signatures with parameter lists that aren't already in the result list
			if result == nil || c.findMatchingSignature(result, signature, false /*partialMatch*/, false /*ignoreThisTypes*/, true /*ignoreReturnTypes*/) == nil {
				unionSignatures := c.findMatchingSignatures(signatureLists, signature, i)
				if unionSignatures != nil {
					s := signature
					// Union the result types when more than one signature matches
					if len(unionSignatures) > 1 {
						thisParameter := signature.thisParameter
						firstThisParameterOfUnionSignatures := core.FirstNonNil(unionSignatures, func(sig *Signature) *ast.Symbol {
							return sig.thisParameter
						})
						if firstThisParameterOfUnionSignatures != nil {
							thisType := c.getIntersectionType(core.MapNonNil(unionSignatures, func(sig *Signature) *Type {
								if sig.thisParameter != nil {
									return c.getTypeOfSymbol(sig.thisParameter)
								}
								return nil
							}))
							thisParameter = c.createSymbolWithType(firstThisParameterOfUnionSignatures, thisType)
						}
						s = c.createUnionSignature(signature, unionSignatures)
						s.thisParameter = thisParameter
					}
					result = append(result, s)
				}
			}
		}
	}
	if len(result) == 0 && countLengthOverOne <= 1 {
		// No sufficiently similar signature existed to subsume all the other signatures in the union - time to see if we can make a single
		// signature that handles all of them. We only do this when there are overloads in only one constituent. (Overloads are conditional in
		// nature and having overloads in multiple constituents would necessitate making a power set of signatures from the type, whose
		// ordering would be non-obvious)
		masterList := signatureLists[indexWithLengthOverOne]
		var results []*Signature = slices.Clone(masterList)
		for _, signatures := range signatureLists {
			if !core.Same(signatures, masterList) {
				signature := signatures[0]
				debug.Assert(signature != nil, "getUnionSignatures bails early on empty signature lists and should not have empty lists on second pass")
				if len(signature.typeParameters) != 0 && core.Some(results, func(s *Signature) bool {
					return len(s.typeParameters) != 0 && !c.compareTypeParametersIdentical(signature.typeParameters, s.typeParameters)
				}) {
					results = nil
				} else {
					results = core.Map(results, func(sig *Signature) *Signature {
						return c.combineUnionOrIntersectionMemberSignatures(sig, signature, true /*isUnion*/)
					})
				}
				if results == nil {
					break
				}
			}
		}
		result = results
	}
	return result
}

func (c *Checker) combineUnionOrIntersectionMemberSignatures(left *Signature, right *Signature, isUnion bool) *Signature {
	typeParams := left.typeParameters
	if len(typeParams) == 0 {
		typeParams = right.typeParameters
	}
	var paramMapper *TypeMapper
	if len(left.typeParameters) != 0 && len(right.typeParameters) != 0 {
		// We just use the type parameter defaults from the first signature
		paramMapper = newTypeMapper(right.typeParameters, left.typeParameters)
	}
	flags := (left.flags | right.flags) & (SignatureFlagsPropagatingFlags & ^SignatureFlagsHasRestParameter)
	declaration := left.declaration
	params := c.combineUnionOrIntersectionParameters(left, right, paramMapper, isUnion)
	lastParam := core.LastOrNil(params)
	if lastParam != nil && lastParam.CheckFlags&ast.CheckFlagsRestParameter != 0 {
		flags |= SignatureFlagsHasRestParameter
	}
	thisParam := c.combineUnionOrIntersectionThisParam(left.thisParameter, right.thisParameter, paramMapper, isUnion)
	minArgCount := int(max(left.minArgumentCount, right.minArgumentCount))
	result := c.newSignature(flags, declaration, typeParams, thisParam, params, nil, nil, minArgCount)
	var leftSignatures []*Signature
	if left.composite != nil && left.composite.isUnion {
		leftSignatures = left.composite.signatures
	} else {
		leftSignatures = []*Signature{left}
	}
	result.composite = &CompositeSignature{isUnion: isUnion, signatures: append(leftSignatures, right)}
	if paramMapper != nil {
		if left.composite != nil && left.composite.isUnion == isUnion && left.mapper != nil {
			result.mapper = c.combineTypeMappers(left.mapper, paramMapper)
		} else {
			result.mapper = paramMapper
		}
	} else if left.composite != nil && left.composite.isUnion == isUnion {
		result.mapper = left.mapper
	}
	return result
}

func (c *Checker) combineUnionOrIntersectionParameters(left *Signature, right *Signature, mapper *TypeMapper, isUnion bool) []*ast.Symbol {
	leftCount := c.getParameterCount(left)
	rightCount := c.getParameterCount(right)
	var longestCount int
	var longest, shorter *Signature
	if leftCount >= rightCount {
		longestCount, longest, shorter = leftCount, left, right
	} else {
		longestCount, longest, shorter = rightCount, right, left
	}
	eitherHasEffectiveRest := c.hasEffectiveRestParameter(left) || c.hasEffectiveRestParameter(right)
	needsExtraRestElement := eitherHasEffectiveRest && !c.hasEffectiveRestParameter(longest)
	params := make([]*ast.Symbol, longestCount+core.IfElse(needsExtraRestElement, 1, 0))
	for i := range longestCount {
		longestParamType := c.tryGetTypeAtPosition(longest, i)
		if longest == right {
			longestParamType = c.instantiateType(longestParamType, mapper)
		}
		shorterParamType := core.OrElse(c.tryGetTypeAtPosition(shorter, i), c.unknownType)
		if shorter == right {
			shorterParamType = c.instantiateType(shorterParamType, mapper)
		}
		combinedParamType := c.getUnionOrIntersectionType([]*Type{longestParamType, shorterParamType}, !isUnion, UnionReductionLiteral)
		isRestParam := eitherHasEffectiveRest && !needsExtraRestElement && i == (longestCount-1)
		isOptional := i >= c.getMinArgumentCount(longest) && i >= c.getMinArgumentCount(shorter)
		var leftName, rightName string
		if i < leftCount {
			leftName = c.getParameterNameAtPosition(left, i)
		}
		if i < rightCount {
			rightName = c.getParameterNameAtPosition(right, i)
		}
		var paramName string
		switch {
		case leftName == rightName:
			paramName = leftName
		case leftName == "":
			paramName = rightName
		case rightName == "":
			paramName = leftName
		}
		if paramName == "" {
			paramName = "arg" + strconv.Itoa(i)
		}
		paramSymbol := c.newSymbolEx(ast.SymbolFlagsFunctionScopedVariable|core.IfElse(isOptional && !isRestParam, ast.SymbolFlagsOptional, 0), paramName,
			core.IfElse(isRestParam, ast.CheckFlagsRestParameter, core.IfElse(isOptional, ast.CheckFlagsOptionalParameter, 0)))
		links := c.valueSymbolLinks.Get(paramSymbol)
		if isRestParam {
			links.resolvedType = c.createArrayType(combinedParamType)
		} else {
			links.resolvedType = combinedParamType
		}
		params[i] = paramSymbol
	}
	if needsExtraRestElement {
		restParamSymbol := c.newSymbolEx(ast.SymbolFlagsFunctionScopedVariable, "args", ast.CheckFlagsRestParameter)
		links := c.valueSymbolLinks.Get(restParamSymbol)
		links.resolvedType = c.createArrayType(c.getTypeAtPosition(shorter, longestCount))
		if shorter == right {
			links.resolvedType = c.instantiateType(links.resolvedType, mapper)
		}
		params[longestCount] = restParamSymbol
	}
	return params
}

func (c *Checker) combineUnionOrIntersectionThisParam(left *ast.Symbol, right *ast.Symbol, mapper *TypeMapper, isUnion bool) *ast.Symbol {
	if left == nil {
		return right
	}
	if right == nil {
		return left
	}
	// A signature `this` type might be a read or a write position... It's very possible that it should be invariant
	// and we should refuse to merge signatures if there are `this` types and they do not match. However, so as to be
	// permissive when calling, for now, we'll intersect the `this` types just like we do for param types in union signatures.
	thisType := c.getUnionOrIntersectionType([]*Type{c.getTypeOfSymbol(left), c.instantiateType(c.getTypeOfSymbol(right), mapper)}, !isUnion, UnionReductionLiteral)
	return c.createSymbolWithType(left, thisType)
}

func (c *Checker) resolveIntersectionTypeMembers(t *Type) {
	// The members and properties collections are empty for intersection types. To get all properties of an
	// intersection type use getPropertiesOfType (only the language service uses this).
	var callSignatures []*Signature
	var constructSignatures []*Signature
	var indexInfos []*IndexInfo
	types := t.Types()
	mixinFlags, mixinCount := c.findMixins(types)
	for i, t := range types {
		// When an intersection type contains mixin constructor types, the construct signatures from
		// those types are discarded and their return types are mixed into the return types of all
		// other construct signatures in the intersection type. For example, the intersection type
		// '{ new(...args: any[]) => A } & { new(s: string) => B }' has a single construct signature
		// 'new(s: string) => A & B'.
		if !mixinFlags[i] {
			signatures := c.getSignaturesOfType(t, SignatureKindConstruct)
			if len(signatures) != 0 && mixinCount > 0 {
				signatures = core.Map(signatures, func(s *Signature) *Signature {
					clone := c.cloneSignature(s)
					clone.resolvedReturnType = c.includeMixinType(c.getReturnTypeOfSignature(s), types, mixinFlags, i)
					return clone
				})
			}
			constructSignatures = c.appendSignatures(constructSignatures, signatures)
		}
		callSignatures = c.appendSignatures(callSignatures, c.getSignaturesOfType(t, SignatureKindCall))
		for _, info := range c.getIndexInfosOfType(t) {
			indexInfos = c.appendIndexInfo(indexInfos, info, false /*union*/)
		}
	}
	c.setStructuredTypeMembers(t, nil, callSignatures, constructSignatures, indexInfos)
}

func (c *Checker) appendSignatures(signatures []*Signature, newSignatures []*Signature) []*Signature {
	for _, sig := range newSignatures {
		if len(signatures) == 0 || core.Every(signatures, func(s *Signature) bool {
			return c.compareSignaturesIdentical(s, sig, false /*partialMatch*/, false /*ignoreThisTypes*/, false /*ignoreReturnTypes*/, c.compareTypesIdentical) == TernaryFalse
		}) {
			signatures = append(signatures, sig)
		}
	}
	return signatures
}

func (c *Checker) appendIndexInfo(indexInfos []*IndexInfo, newInfo *IndexInfo, union bool) []*IndexInfo {
	for i, info := range indexInfos {
		if info.keyType == newInfo.keyType {
			var valueType *Type
			var isReadonly bool
			if union {
				valueType = c.getUnionType([]*Type{info.valueType, newInfo.valueType})
				isReadonly = info.isReadonly || newInfo.isReadonly
			} else {
				valueType = c.getIntersectionType([]*Type{info.valueType, newInfo.valueType})
				isReadonly = info.isReadonly && newInfo.isReadonly
			}
			indexInfos[i] = c.newIndexInfo(info.keyType, valueType, isReadonly, nil, nil)
			return indexInfos
		}
	}
	return append(indexInfos, newInfo)
}

func (c *Checker) findMixins(types []*Type) ([]bool, int) {
	mixinFlags := core.Map(types, c.isMixinConstructorType)
	var constructorTypeCount, mixinCount int
	firstMixinIndex := -1
	for i, t := range types {
		if len(c.getSignaturesOfType(t, SignatureKindConstruct)) > 0 {
			constructorTypeCount++
		}
		if mixinFlags[i] {
			if firstMixinIndex < 0 {
				firstMixinIndex = i
			}
			mixinCount++
		}
	}
	if constructorTypeCount > 0 && constructorTypeCount == mixinCount {
		mixinFlags[firstMixinIndex] = false
		mixinCount--
	}
	return mixinFlags, mixinCount
}

func (c *Checker) includeMixinType(t *Type, types []*Type, mixinFlags []bool, index int) *Type {
	var mixedTypes []*Type
	for i := range types {
		if i == index {
			mixedTypes = append(mixedTypes, t)
		} else if mixinFlags[i] {
			mixedTypes = append(mixedTypes, c.getReturnTypeOfSignature(c.getSignaturesOfType(types[i], SignatureKindConstruct)[0]))
		}
	}
	return c.getIntersectionType(mixedTypes)
}

/**
 * If the given type is an object type and that type has a property by the given name,
 * return the symbol for that property. Otherwise return undefined.
 */
func (c *Checker) getPropertyOfObjectType(t *Type, name string) *ast.Symbol {
	if t.flags&TypeFlagsObject != 0 {
		resolved := c.resolveStructuredTypeMembers(t)
		symbol := resolved.members[name]
		if symbol != nil && c.symbolIsValue(symbol) {
			return symbol
		}
	}
	return nil
}

func (c *Checker) getPropertyOfUnionOrIntersectionType(t *Type, name string, skipObjectFunctionPropertyAugment bool) *ast.Symbol {
	prop := c.getUnionOrIntersectionProperty(t, name, skipObjectFunctionPropertyAugment)
	// We need to filter out partial properties in union types
	if prop != nil && prop.CheckFlags&ast.CheckFlagsReadPartial != 0 {
		return nil
	}
	return prop
}

// Return the symbol for a given property in a union or intersection type, or undefined if the property
// does not exist in any constituent type. Note that the returned property may only be present in some
// constituents, in which case the isPartial flag is set when the containing type is union type. We need
// these partial properties when identifying discriminant properties, but otherwise they are filtered out
// and do not appear to be present in the union type.
func (c *Checker) getUnionOrIntersectionProperty(t *Type, name string, skipObjectFunctionPropertyAugment bool) *ast.Symbol {
	var cache ast.SymbolTable
	if skipObjectFunctionPropertyAugment {
		cache = ast.GetSymbolTable(&t.AsUnionOrIntersectionType().propertyCacheWithoutFunctionPropertyAugment)
	} else {
		cache = ast.GetSymbolTable(&t.AsUnionOrIntersectionType().propertyCache)
	}
	if prop := cache[name]; prop != nil {
		return prop
	}
	prop := c.createUnionOrIntersectionProperty(t, name, skipObjectFunctionPropertyAugment)
	if prop != nil {
		cache[name] = prop
		// Propagate an entry from the non-augmented cache to the augmented cache unless the property is partial.
		if skipObjectFunctionPropertyAugment && prop.CheckFlags&ast.CheckFlagsPartial == 0 {
			augmentedCache := ast.GetSymbolTable(&t.AsUnionOrIntersectionType().propertyCache)
			if augmentedCache[name] == nil {
				augmentedCache[name] = prop
			}
		}
	}
	return prop
}

func (c *Checker) createUnionOrIntersectionProperty(containingType *Type, name string, skipObjectFunctionPropertyAugment bool) *ast.Symbol {
	propFlags := ast.SymbolFlagsNone
	var singleProp *ast.Symbol
	var propSet collections.OrderedSet[*ast.Symbol]
	var indexTypes []*Type
	isUnion := containingType.flags&TypeFlagsUnion != 0
	// Flags we want to propagate to the result if they exist in all source symbols
	var checkFlags ast.CheckFlags
	var optionalFlag ast.SymbolFlags
	if !isUnion {
		checkFlags = ast.CheckFlagsReadonly
		optionalFlag = ast.SymbolFlagsOptional
	}
	syntheticFlag := ast.CheckFlagsSyntheticMethod
	mergedInstantiations := false
	for _, current := range containingType.Types() {
		t := c.getApparentType(current)
		if !c.isErrorType(t) && t.flags&TypeFlagsNever == 0 {
			prop := c.getPropertyOfTypeEx(t, name, skipObjectFunctionPropertyAugment, false)
			var modifiers ast.ModifierFlags
			if prop != nil {
				modifiers = getDeclarationModifierFlagsFromSymbol(prop)
				if prop.Flags&ast.SymbolFlagsClassMember != 0 {
					if isUnion {
						optionalFlag |= prop.Flags & ast.SymbolFlagsOptional
					} else {
						optionalFlag &= prop.Flags
					}
				}
				if singleProp == nil {
					singleProp = prop
					propFlags = core.OrElse(prop.Flags&ast.SymbolFlagsAccessor, ast.SymbolFlagsProperty)
				} else if prop != singleProp {
					isInstantiation := c.getTargetSymbol(prop) == c.getTargetSymbol(singleProp)
					// If the symbols are instances of one another with identical types - consider the symbols
					// equivalent and just use the first one, which thus allows us to avoid eliding private
					// members when intersecting a (this-)instantiations of a class with its raw base or another instance
					if isInstantiation && c.compareProperties(singleProp, prop, compareTypesEqual) == TernaryTrue {
						// If we merged instantiations of a generic type, we replicate the symbol parent resetting behavior we used
						// to do when we recorded multiple distinct symbols so that we still get, eg, `Array<T>.length` printed
						// back and not `Array<string>.length` when we're looking at a `.length` access on a `string[] | number[]`
						mergedInstantiations = singleProp.Parent != nil && len(c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(singleProp.Parent)) != 0
					} else {
						if propSet.Size() == 0 {
							propSet.Add(singleProp)
						}
						propSet.Add(prop)
					}
					// classes created by mixins are represented as intersections
					// and overriding a property in a derived class redefines it completely at runtime
					// so a get accessor can't be merged with a set accessor in a base class,
					// for that reason the accessor flags are only used when they are the same in all constituents
					if propFlags&ast.SymbolFlagsAccessor != 0 && (prop.Flags&ast.SymbolFlagsAccessor != (propFlags & ast.SymbolFlagsAccessor)) {
						propFlags = (propFlags &^ ast.SymbolFlagsAccessor) | ast.SymbolFlagsProperty
					}
				}
				if isUnion && c.isReadonlySymbol(prop) {
					checkFlags |= ast.CheckFlagsReadonly
				} else if !isUnion && !c.isReadonlySymbol(prop) {
					checkFlags &^= ast.CheckFlagsReadonly
				}
				if modifiers&ast.ModifierFlagsNonPublicAccessibilityModifier == 0 {
					checkFlags |= ast.CheckFlagsContainsPublic
				}
				if modifiers&ast.ModifierFlagsProtected != 0 {
					checkFlags |= ast.CheckFlagsContainsProtected
				}
				if modifiers&ast.ModifierFlagsPrivate != 0 {
					checkFlags |= ast.CheckFlagsContainsPrivate
				}
				if modifiers&ast.ModifierFlagsStatic != 0 {
					checkFlags |= ast.CheckFlagsContainsStatic
				}
				if !isPrototypeProperty(prop) {
					syntheticFlag = ast.CheckFlagsSyntheticProperty
				}
			} else if isUnion {
				var indexInfo *IndexInfo
				if !isLateBoundName(name) {
					indexInfo = c.getApplicableIndexInfoForName(t, name)
				}
				if indexInfo != nil {
					propFlags = propFlags&^ast.SymbolFlagsAccessor | ast.SymbolFlagsProperty
					checkFlags |= ast.CheckFlagsWritePartial | core.IfElse(indexInfo.isReadonly, ast.CheckFlagsReadonly, 0)
					if isTupleType(t) {
						indexType := c.getRestTypeOfTupleType(t)
						if indexType == nil {
							indexType = c.undefinedType
						}
						indexTypes = append(indexTypes, indexType)
					} else {
						indexTypes = append(indexTypes, indexInfo.valueType)
					}
				} else if isObjectLiteralType(t) && t.objectFlags&ObjectFlagsContainsSpread == 0 {
					checkFlags |= ast.CheckFlagsWritePartial
					indexTypes = append(indexTypes, c.undefinedType)
				} else {
					checkFlags |= ast.CheckFlagsReadPartial
				}
			}
		}
	}
	if singleProp == nil || isUnion &&
		(propSet.Size() != 0 || checkFlags&ast.CheckFlagsPartial != 0) &&
		checkFlags&(ast.CheckFlagsContainsPrivate|ast.CheckFlagsContainsProtected) != 0 &&
		!(propSet.Size() != 0 && c.hasCommonDeclaration(&propSet)) {
		// No property was found, or, in a union, a property has a private or protected declaration in one
		// constituent, but is missing or has a different declaration in another constituent.
		return nil
	}
	if propSet.Size() == 0 && checkFlags&ast.CheckFlagsReadPartial == 0 && len(indexTypes) == 0 {
		if !mergedInstantiations {
			return singleProp
		}
		// No symbol from a union/intersection should have a `.parent` set (since unions/intersections don't act as symbol parents)
		// Unless that parent is "reconstituted" from the "first value declaration" on the symbol (which is likely different than its instantiated parent!)
		// They also have a `.containingType` set, which affects some services endpoints behavior, like `getRootSymbol`
		var singlePropType *Type
		var singlePropMapper *TypeMapper
		if singleProp.Flags&ast.SymbolFlagsTransient != 0 {
			links := c.valueSymbolLinks.Get(singleProp)
			singlePropType = links.resolvedType
			singlePropMapper = links.mapper
		}
		clone := c.createSymbolWithType(singleProp, singlePropType)
		if singleProp.ValueDeclaration != nil {
			clone.Parent = singleProp.ValueDeclaration.Symbol().Parent
		}
		links := c.valueSymbolLinks.Get(clone)
		links.containingType = containingType
		links.mapper = singlePropMapper
		links.writeType = c.getWriteTypeOfSymbol(singleProp)
		return clone
	}
	if propSet.Size() == 0 {
		propSet.Add(singleProp)
	}
	var declarations []*ast.Node
	var firstType *Type
	var nameType *Type
	var propTypes []*Type
	var writeTypes []*Type
	var firstValueDeclaration *ast.Node
	var hasNonUniformValueDeclaration bool
	for prop := range propSet.Values() {
		if firstValueDeclaration == nil {
			firstValueDeclaration = prop.ValueDeclaration
		} else if prop.ValueDeclaration != nil && prop.ValueDeclaration != firstValueDeclaration {
			hasNonUniformValueDeclaration = true
		}
		declarations = append(declarations, prop.Declarations...)
		t := c.getTypeOfSymbol(prop)
		if firstType == nil {
			firstType = t
			nameType = c.valueSymbolLinks.Get(prop).nameType
		}
		writeType := c.getWriteTypeOfSymbol(prop)
		if writeTypes != nil || writeType != t {
			if writeTypes == nil {
				writeTypes = slices.Clone(propTypes)
			}
			writeTypes = append(writeTypes, writeType)
		}
		if t != firstType {
			checkFlags |= ast.CheckFlagsHasNonUniformType
		}
		if isLiteralType(t) || c.isPatternLiteralType(t) {
			checkFlags |= ast.CheckFlagsHasLiteralType
		}
		if t.flags&TypeFlagsNever != 0 && t != c.uniqueLiteralType {
			checkFlags |= ast.CheckFlagsHasNeverType
		}
		propTypes = append(propTypes, t)
	}
	propTypes = append(propTypes, indexTypes...)
	result := c.newSymbolEx(propFlags|optionalFlag, name, checkFlags|syntheticFlag)
	result.Declarations = declarations
	if !hasNonUniformValueDeclaration && firstValueDeclaration != nil {
		result.ValueDeclaration = firstValueDeclaration
		// Inherit information about parent type.
		result.Parent = firstValueDeclaration.Symbol().Parent
	}
	links := c.valueSymbolLinks.Get(result)
	links.containingType = containingType
	links.nameType = nameType
	if len(propTypes) > 2 {
		// When `propTypes` has the potential to explode in size when normalized, defer normalization until absolutely needed
		result.CheckFlags |= ast.CheckFlagsDeferredType
		deferred := c.deferredSymbolLinks.Get(result)
		deferred.parent = containingType
		deferred.constituents = propTypes
		deferred.writeConstituents = writeTypes
		return result
	}
	if isUnion {
		links.resolvedType = c.getUnionType(propTypes)
	} else {
		links.resolvedType = c.getIntersectionType(propTypes)
	}
	if writeTypes != nil {
		if isUnion {
			links.writeType = c.getUnionType(writeTypes)
		} else {
			links.writeType = c.getIntersectionType(writeTypes)
		}
	}
	return result
}

func (c *Checker) getTargetSymbol(s *ast.Symbol) *ast.Symbol {
	// if symbol is instantiated its flags are not copied from the 'target'
	// so we'll need to get back original 'target' symbol to work with correct set of flags
	// NOTE: cast to TransientSymbol should be safe because only TransientSymbols have CheckFlags.Instantiated
	if s != nil && s.CheckFlags&ast.CheckFlagsInstantiated != 0 {
		return c.valueSymbolLinks.Get(s).target
	}
	return s
}

/**
 * Return whether this symbol is a member of a prototype somewhere
 * Note that this is not tracked well within the compiler, so the answer may be incorrect.
 */
func isPrototypeProperty(symbol *ast.Symbol) bool {
	return symbol.Flags&ast.SymbolFlagsMethod != 0 || symbol.CheckFlags&ast.CheckFlagsSyntheticMethod != 0
}

func (c *Checker) hasCommonDeclaration(symbols *collections.OrderedSet[*ast.Symbol]) bool {
	var commonDeclarations collections.Set[*ast.Node]
	for symbol := range symbols.Values() {
		if len(symbol.Declarations) == 0 {
			return false
		}
		if commonDeclarations.Len() == 0 {
			for _, d := range symbol.Declarations {
				commonDeclarations.Add(d)
			}
			continue
		}
		for d := range commonDeclarations.Keys() {
			if !slices.Contains(symbol.Declarations, d) {
				commonDeclarations.Delete(d)
			}
		}
		if commonDeclarations.Len() == 0 {
			return false
		}
	}
	return commonDeclarations.Len() != 0
}

func (c *Checker) createSymbolWithType(source *ast.Symbol, t *Type) *ast.Symbol {
	symbol := c.newSymbolEx(source.Flags, source.Name, source.CheckFlags&ast.CheckFlagsReadonly)
	symbol.Declarations = source.Declarations
	symbol.Parent = source.Parent
	symbol.ValueDeclaration = source.ValueDeclaration
	links := c.valueSymbolLinks.Get(symbol)
	links.resolvedType = t
	links.target = source
	links.nameType = c.valueSymbolLinks.Get(source).nameType
	return symbol
}

func (c *Checker) isMappedTypeGenericIndexedAccess(t *Type) bool {
	if t.flags&TypeFlagsIndexedAccess != 0 {
		objectType := t.AsIndexedAccessType().objectType
		return objectType.objectFlags&ObjectFlagsMapped != 0 && !c.isGenericMappedType(objectType) && c.isGenericIndexType(t.AsIndexedAccessType().indexType) &&
			getMappedTypeModifiers(objectType)&MappedTypeModifiersExcludeOptional == 0 && objectType.AsMappedType().declaration.NameType == nil
	}
	return false
}

/**
 * For a type parameter, return the base constraint of the type parameter. For the string, number,
 * boolean, and symbol primitive types, return the corresponding object types. Otherwise return the
 * type itself.
 */
func (c *Checker) getApparentType(t *Type) *Type {
	originalType := t
	if t.flags&TypeFlagsInstantiable != 0 {
		t = c.getBaseConstraintOfType(t)
		if t == nil {
			t = c.unknownType
		}
	}
	switch {
	case t.objectFlags&ObjectFlagsMapped != 0:
		return c.getApparentTypeOfMappedType(t)
	case t.objectFlags&ObjectFlagsReference != 0 && t != originalType:
		return c.getTypeWithThisArgument(t, originalType, false /*needsApparentType*/)
	case t.flags&TypeFlagsIntersection != 0:
		return c.getApparentTypeOfIntersectionType(t, originalType)
	case t.flags&TypeFlagsStringLike != 0:
		return c.globalStringType
	case t.flags&TypeFlagsNumberLike != 0:
		return c.globalNumberType
	case t.flags&TypeFlagsBigIntLike != 0:
		return c.getGlobalBigIntType()
	case t.flags&TypeFlagsBooleanLike != 0:
		return c.globalBooleanType
	case t.flags&TypeFlagsESSymbolLike != 0:
		return c.getGlobalESSymbolType()
	case t.flags&TypeFlagsNonPrimitive != 0:
		return c.emptyObjectType
	case t.flags&TypeFlagsIndex != 0:
		return c.stringNumberSymbolType
	case t.flags&TypeFlagsUnknown != 0 && !c.strictNullChecks:
		return c.emptyObjectType
	}
	return t
}

func (c *Checker) getApparentTypeOfMappedType(t *Type) *Type {
	m := t.AsMappedType()
	if m.resolvedApparentType == nil {
		m.resolvedApparentType = c.getResolvedApparentTypeOfMappedType(t)
	}
	return m.resolvedApparentType
}

func (c *Checker) getResolvedApparentTypeOfMappedType(t *Type) *Type {
	target := core.OrElse(t.AsMappedType().target, t)
	typeVariable := c.getHomomorphicTypeVariable(target)
	if typeVariable != nil && target.AsMappedType().declaration.NameType == nil {
		// We have a homomorphic mapped type or an instantiation of a homomorphic mapped type, i.e. a type
		// of the form { [P in keyof T]: X }. Obtain the modifiers type (the T of the keyof T), and if it is
		// another generic mapped type, recursively obtain its apparent type. Otherwise, obtain its base
		// constraint. Then, if every constituent of the base constraint is an array or tuple type, apply
		// this mapped type to the base constraint. It is safe to recurse when the modifiers type is a
		// mapped type because we protect again circular constraints in getTypeFromMappedTypeNode.
		modifiersType := c.getModifiersTypeFromMappedType(t)
		var baseConstraint *Type
		if c.isGenericMappedType(modifiersType) {
			baseConstraint = c.getApparentTypeOfMappedType(modifiersType)
		} else {
			baseConstraint = c.getBaseConstraintOfType(modifiersType)
		}
		if baseConstraint != nil && everyType(baseConstraint, func(t *Type) bool { return c.isArrayOrTupleType(t) || c.isArrayOrTupleOrIntersection(t) }) {
			return c.instantiateType(target, prependTypeMapping(typeVariable, baseConstraint, t.AsMappedType().mapper))
		}
	}
	return t
}

func (c *Checker) getApparentTypeOfIntersectionType(t *Type, thisArgument *Type) *Type {
	if t == thisArgument {
		d := t.AsIntersectionType()
		if d.resolvedApparentType == nil {
			d.resolvedApparentType = c.getTypeWithThisArgument(t, thisArgument, true /*needApparentType*/)
		}
		return d.resolvedApparentType
	}
	key := CachedTypeKey{kind: CachedTypeKindApparentType, typeId: thisArgument.id}
	result := c.cachedTypes[key]
	if result == nil {
		result = c.getTypeWithThisArgument(t, thisArgument, true /*needApparentType*/)
		c.cachedTypes[key] = result
	}
	return result
}

/**
 * Return the reduced form of the given type. For a union type, it is a union of the normalized constituent types.
 * For an intersection of types containing one or more mututally exclusive discriminant properties, it is 'never'.
 * For all other types, it is simply the type itself. Discriminant properties are considered mutually exclusive when
 * no constituent property has type 'never', but the intersection of the constituent property types is 'never'.
 */
func (c *Checker) getReducedType(t *Type) *Type {
	switch {
	case t.flags&TypeFlagsUnion != 0:
		if t.objectFlags&ObjectFlagsContainsIntersections != 0 {
			if reducedType := t.AsUnionType().resolvedReducedType; reducedType != nil {
				return reducedType
			}
			reducedType := c.getReducedUnionType(t)
			t.AsUnionType().resolvedReducedType = reducedType
			return reducedType
		}
	case t.flags&TypeFlagsIntersection != 0:
		if t.objectFlags&ObjectFlagsIsNeverIntersectionComputed == 0 {
			t.objectFlags |= ObjectFlagsIsNeverIntersectionComputed
			if core.Some(c.getPropertiesOfUnionOrIntersectionType(t), c.isNeverReducedProperty) {
				t.objectFlags |= ObjectFlagsIsNeverIntersection
			}
		}
		if t.objectFlags&ObjectFlagsIsNeverIntersection != 0 {
			return c.neverType
		}
	}
	return t
}

func (c *Checker) getReducedUnionType(unionType *Type) *Type {
	reducedTypes := core.SameMap(unionType.Types(), c.getReducedType)
	if core.Same(reducedTypes, unionType.Types()) {
		return unionType
	}
	reduced := c.getUnionType(reducedTypes)
	if reduced.flags&TypeFlagsUnion != 0 {
		reduced.AsUnionType().resolvedReducedType = reduced
	}
	return reduced
}

func (c *Checker) isNeverReducedProperty(prop *ast.Symbol) bool {
	return c.isDiscriminantWithNeverType(prop) || isConflictingPrivateProperty(prop)
}

func (c *Checker) getReducedApparentType(t *Type) *Type {
	// Since getApparentType may return a non-reduced union or intersection type, we need to perform
	// type reduction both before and after obtaining the apparent type. For example, given a type parameter
	// 'T extends A | B', the type 'T & X' becomes 'A & X | B & X' after obtaining the apparent type, and
	// that type may need further reduction to remove empty intersections.
	return c.getReducedType(c.getApparentType(c.getReducedType(t)))
}

func (c *Checker) elaborateNeverIntersection(chain *ast.Diagnostic, node *ast.Node, t *Type) *ast.Diagnostic {
	if t.flags&TypeFlagsIntersection != 0 && t.objectFlags&ObjectFlagsIsNeverIntersection != 0 {
		neverProp := core.Find(c.getPropertiesOfUnionOrIntersectionType(t), c.isDiscriminantWithNeverType)
		if neverProp != nil {
			return NewDiagnosticChainForNode(chain, node, diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_has_conflicting_types_in_some_constituents, c.typeToStringEx(t, nil, TypeFormatFlagsNoTypeReduction, nil), c.symbolToString(neverProp))
		}
		privateProp := core.Find(c.getPropertiesOfUnionOrIntersectionType(t), isConflictingPrivateProperty)
		if privateProp != nil {
			return NewDiagnosticChainForNode(chain, node, diagnostics.The_intersection_0_was_reduced_to_never_because_property_1_exists_in_multiple_constituents_and_is_private_in_some, c.typeToStringEx(t, nil, TypeFormatFlagsNoTypeReduction, nil), c.symbolToString(privateProp))
		}
	}
	return chain
}

func (c *Checker) isDiscriminantWithNeverType(prop *ast.Symbol) bool {
	// Return true for a synthetic non-optional property with non-uniform types, where at least one is
	// a literal type and none is never, that reduces to never.
	return prop.Flags&ast.SymbolFlagsOptional == 0 && prop.CheckFlags&(ast.CheckFlagsNonUniformAndLiteral|ast.CheckFlagsHasNeverType) == ast.CheckFlagsNonUniformAndLiteral && c.getTypeOfSymbol(prop).flags&TypeFlagsNever != 0
}

func isConflictingPrivateProperty(prop *ast.Symbol) bool {
	// Return true for a synthetic property with multiple declarations, at least one of which is private.
	return prop.ValueDeclaration == nil && prop.CheckFlags&ast.CheckFlagsContainsPrivate != 0
}

func (c *Checker) getTypeArguments(t *Type) []*Type {
	d := t.AsTypeReference()
	if d.resolvedTypeArguments == nil {
		n := d.target.AsInterfaceType()
		if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedTypeArguments) {
			return slices.Repeat([]*Type{c.errorType}, len(n.TypeParameters()))
		}
		var typeArguments []*Type
		node := t.AsTypeReference().node
		if node != nil {
			switch node.Kind {
			case ast.KindTypeReference:
				typeArguments = append(n.OuterTypeParameters(), c.getEffectiveTypeArguments(node, n.LocalTypeParameters())...)
			case ast.KindArrayType:
				typeArguments = []*Type{c.getTypeFromTypeNode(node.AsArrayTypeNode().ElementType)}
			case ast.KindTupleType:
				typeArguments = core.Map(node.Elements(), c.getTypeFromTypeNode)
			default:
				panic("Unhandled case in getTypeArguments")
			}
		}
		if c.popTypeResolution() {
			if d.resolvedTypeArguments == nil {
				d.resolvedTypeArguments = c.instantiateTypes(typeArguments, d.mapper)
			}
		} else {
			if d.resolvedTypeArguments == nil {
				d.resolvedTypeArguments = slices.Repeat([]*Type{c.errorType}, len(n.TypeParameters()))
			}
			errorNode := core.IfElse(node != nil, node, c.currentNode)
			if d.target.symbol != nil {
				c.error(errorNode, diagnostics.Type_arguments_for_0_circularly_reference_themselves, c.symbolToString(d.target.symbol))
			} else {
				c.error(errorNode, diagnostics.Tuple_type_arguments_circularly_reference_themselves)
			}
		}
	}
	return d.resolvedTypeArguments
}

func (c *Checker) getEffectiveTypeArguments(node *ast.Node, typeParameters []*Type) []*Type {
	return c.fillMissingTypeArguments(core.Map(node.TypeArguments(), c.getTypeFromTypeNode), typeParameters, c.getMinTypeArgumentCount(typeParameters), ast.IsInJSFile(node))
}

// Gets the minimum number of type arguments needed to satisfy all non-optional type parameters.
func (c *Checker) getMinTypeArgumentCount(typeParameters []*Type) int {
	minTypeArgumentCount := 0
	for i, typeParameter := range typeParameters {
		if !c.hasTypeParameterDefault(typeParameter) {
			minTypeArgumentCount = i + 1
		}
	}
	return minTypeArgumentCount
}

func (c *Checker) hasTypeParameterDefault(t *Type) bool {
	return t.symbol != nil && core.Some(t.symbol.Declarations, func(d *ast.Node) bool {
		return ast.IsTypeParameterDeclaration(d) && d.AsTypeParameterDeclaration().DefaultType != nil
	})
}

func (c *Checker) fillMissingTypeArguments(typeArguments []*Type, typeParameters []*Type, minTypeArgumentCount int, isJavaScriptImplicitAny bool) []*Type {
	numTypeParameters := len(typeParameters)
	if numTypeParameters == 0 {
		return nil
	}
	numTypeArguments := len(typeArguments)
	if isJavaScriptImplicitAny || numTypeArguments < numTypeParameters {
		result := make([]*Type, numTypeParameters)
		copy(result, typeArguments)
		// Map invalid forward references in default types to the error type
		for i := numTypeArguments; i < numTypeParameters; i++ {
			result[i] = c.errorType
		}
		baseDefaultType := c.getDefaultTypeArgumentType(isJavaScriptImplicitAny)
		for i := numTypeArguments; i < numTypeParameters; i++ {
			defaultType := c.getDefaultFromTypeParameter(typeParameters[i])

			if isJavaScriptImplicitAny && defaultType != nil && (c.isTypeIdenticalTo(defaultType, c.unknownType) || c.isTypeIdenticalTo(defaultType, c.emptyObjectType)) {
				defaultType = c.anyType
			}

			if defaultType != nil {
				result[i] = c.instantiateType(defaultType, newTypeMapper(typeParameters, result))
			} else {
				result[i] = baseDefaultType
			}
		}
		return result
	}
	return typeArguments
}

func (c *Checker) getDefaultTypeArgumentType(isInJavaScriptFile bool) *Type {
	if isInJavaScriptFile {
		return c.anyType
	}
	return c.unknownType
}

// Gets the default type for a type parameter. If the type parameter is the result of an instantiation,
// this gets the instantiated default type of its target. If the type parameter has no default type or
// the default is circular, `undefined` is returned.
func (c *Checker) getDefaultFromTypeParameter(t *Type) *Type {
	if t.flags&TypeFlagsTypeParameter == 0 {
		return nil
	}
	defaultType := c.getResolvedTypeParameterDefault(t)
	if defaultType != c.noConstraintType && defaultType != c.circularConstraintType {
		return defaultType
	}
	return nil
}

func (c *Checker) getResolvedTypeParameterDefault(t *Type) *Type {
	d := t.AsTypeParameter()
	if d.resolvedDefaultType == nil {
		if d.target != nil {
			targetDefault := c.getResolvedTypeParameterDefault(d.target)
			if targetDefault != nil {
				d.resolvedDefaultType = c.instantiateType(targetDefault, d.mapper)
			} else {
				d.resolvedDefaultType = c.noConstraintType
			}
		} else {
			// To block recursion, set the initial value to the resolvingDefaultType.
			d.resolvedDefaultType = c.resolvingDefaultType
			defaultType := c.noConstraintType
			if t.symbol != nil {
				defaultDeclaration := core.FirstNonNil(t.symbol.Declarations, func(decl *ast.Node) *ast.Node {
					if ast.IsTypeParameterDeclaration(decl) {
						return decl.AsTypeParameterDeclaration().DefaultType
					}
					return nil
				})
				if defaultDeclaration != nil {
					defaultType = c.getTypeFromTypeNode(defaultDeclaration)
				}
			}
			if d.resolvedDefaultType == c.resolvingDefaultType {
				// If we have not been called recursively, set the correct default type.
				d.resolvedDefaultType = defaultType
			}
		}
	} else if d.resolvedDefaultType == c.resolvingDefaultType {
		// If we are called recursively for this type parameter, mark the default as circular.
		d.resolvedDefaultType = c.circularConstraintType
	}
	return d.resolvedDefaultType
}

func (c *Checker) getDefaultOrUnknownFromTypeParameter(t *Type) *Type {
	result := c.getDefaultFromTypeParameter(t)
	return core.IfElse(result != nil, result, c.unknownType)
}

func (c *Checker) getNamedMembers(members ast.SymbolTable, container *ast.Symbol) []*ast.Symbol {
	if len(members) == 0 {
		return nil
	}
	// For classes and interfaces, we store explicitly declared members ahead of inherited members. This ensures we process
	// explicitly declared members first in type relations, which is beneficial because explicitly declared members are more
	// likely to contain discriminating differences. See for example https://github.com/microsoft/typescript-go/issues/1968.
	result := make([]*ast.Symbol, 0, len(members))
	var containedCount int
	if container != nil && container.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0 {
		for id, symbol := range members {
			if c.isNamedMember(symbol, id) && c.isDeclarationContainedBy(symbol, container) {
				result = append(result, symbol)
			}
		}
		containedCount = len(result)
	}
	for id, symbol := range members {
		if c.isNamedMember(symbol, id) && (container == nil || container.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) == 0 || !c.isDeclarationContainedBy(symbol, container)) {
			result = append(result, symbol)
		}
	}
	c.sortSymbols(result[:containedCount])
	c.sortSymbols(result[containedCount:])
	return result
}

func (c *Checker) isDeclarationContainedBy(symbol *ast.Symbol, container *ast.Symbol) bool {
	if declaration := symbol.ValueDeclaration; declaration != nil {
		for _, d := range container.Declarations {
			if declaration.Loc.ContainedBy(d.Loc) {
				return true
			}
		}
	}
	return false
}

func (c *Checker) isNamedMember(symbol *ast.Symbol, id string) bool {
	return !isReservedMemberName(id) && c.symbolIsValue(symbol)
}

func (c *Checker) symbolIsValue(symbol *ast.Symbol) bool {
	return c.symbolIsValueEx(symbol, false /*includeTypeOnlyMembers*/)
}

func (c *Checker) symbolIsValueEx(symbol *ast.Symbol, includeTypeOnlyMembers bool) bool {
	return symbol.Flags&ast.SymbolFlagsValue != 0 || symbol.Flags&ast.SymbolFlagsAlias != 0 &&
		c.getSymbolFlagsEx(symbol, !includeTypeOnlyMembers, false /*excludeLocalMeanings*/)&ast.SymbolFlagsValue != 0
}

func (c *Checker) instantiateType(t *Type, m *TypeMapper) *Type {
	return c.instantiateTypeWithAlias(t, m, nil /*alias*/)
}

func (c *Checker) instantiateTypeWithAlias(t *Type, m *TypeMapper, alias *TypeAlias) *Type {
	// Check for type variables in the alias, so things like `type Brand<T> = number & {}` can potentially be copied with new alias type args, despite them being unreferenced.
	// This is the behavior most people using aliases expect, and prevents the cache from leaking type parameters outside their scope of validity.
	// tests/cases/compiler/declarationEmitArrowFunctionNoRenaming.ts contains an example of this, which previously only worked in strada via some input node reuse logic instead.
	if t == nil || m == nil || !(c.couldContainTypeVariables(t) || (t.alias != nil && len(t.alias.typeArguments) > 0 && core.Some(t.alias.typeArguments, c.couldContainTypeVariables))) {
		return t
	}
	if c.instantiationDepth == 100 || c.instantiationCount >= 5_000_000 {
		// We have reached 100 recursive type instantiations, or 5M type instantiations caused by the same statement
		// or expression. There is a very high likelihood we're dealing with a combination of infinite generic types
		// that perpetually generate new type identities, so we stop the recursion here by yielding the error type.
		if tr := c.tracer; tr != nil {
			tr.Instant(tracing.PhaseCheckTypes, "instantiateType_DepthLimit", map[string]any{"typeId": t.id, "instantiationDepth": c.instantiationDepth, "instantiationCount": c.instantiationCount})
		}
		c.error(c.currentNode, diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite)
		return c.errorType
	}
	index := c.findActiveMapper(m)
	if index == -1 {
		c.pushActiveMapper(m)
	}
	var b keyBuilder
	b.writeType(t)
	b.writeAlias(alias)
	key := b.hash()
	cache := c.activeTypeMappersCaches[core.IfElse(index != -1, index, len(c.activeTypeMappersCaches)-1)]
	if cachedType, ok := cache[key]; ok {
		return cachedType
	}
	c.TotalInstantiationCount++
	c.instantiationCount++
	c.instantiationDepth++
	result := c.instantiateTypeWorker(t, m, alias)
	if index == -1 {
		c.popActiveMapper()
	} else {
		cache[key] = result
	}
	c.instantiationDepth--
	return result
}

func (c *Checker) pushActiveMapper(mapper *TypeMapper) {
	c.activeMappers = append(c.activeMappers, mapper)

	lastIndex := len(c.activeTypeMappersCaches)
	if cap(c.activeTypeMappersCaches) > lastIndex {
		// The cap may contain an empty map from popActiveMapper; reuse it.
		c.activeTypeMappersCaches = c.activeTypeMappersCaches[:lastIndex+1]
		if c.activeTypeMappersCaches[lastIndex] == nil {
			c.activeTypeMappersCaches[lastIndex] = make(map[CacheHashKey]*Type, 1)
		}
	} else {
		c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, make(map[CacheHashKey]*Type, 1))
	}
}

func (c *Checker) popActiveMapper() {
	c.activeMappers[len(c.activeMappers)-1] = nil
	c.activeMappers = c.activeMappers[:len(c.activeMappers)-1]

	// Clear the map, but leave it in the list for later reuse.
	lastIndex := len(c.activeTypeMappersCaches) - 1
	clear(c.activeTypeMappersCaches[lastIndex])
	c.activeTypeMappersCaches = c.activeTypeMappersCaches[:lastIndex]
}

func (c *Checker) findActiveMapper(mapper *TypeMapper) int {
	return core.FindLastIndex(c.activeMappers, func(m *TypeMapper) bool { return m == mapper })
}

func (c *Checker) clearActiveMapperCaches() {
	for _, cache := range c.activeTypeMappersCaches {
		clear(cache)
	}
}

// Return true if the given type could possibly reference a type parameter for which
// we perform type inference (i.e. a type parameter of a generic function). We cache
// results for union and intersection types for performance reasons.
func (c *Checker) couldContainTypeVariablesWorker(t *Type) bool {
	if t.flags&TypeFlagsStructuredOrInstantiable == 0 {
		return false
	}
	objectFlags := t.objectFlags
	if objectFlags&ObjectFlagsCouldContainTypeVariablesComputed != 0 {
		return objectFlags&ObjectFlagsCouldContainTypeVariables != 0
	}
	result := t.flags&TypeFlagsInstantiable != 0 ||
		t.flags&TypeFlagsObject != 0 && !c.isNonGenericTopLevelType(t) && (objectFlags&ObjectFlagsReference != 0 && (t.AsTypeReference().node != nil || core.Some(c.getTypeArguments(t), c.couldContainTypeVariables)) ||
			objectFlags&ObjectFlagsAnonymous != 0 && t.symbol != nil && t.symbol.Flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod|ast.SymbolFlagsClass|ast.SymbolFlagsTypeLiteral|ast.SymbolFlagsObjectLiteral) != 0 && t.symbol.Declarations != nil ||
			objectFlags&(ObjectFlagsMapped|ObjectFlagsReverseMapped|ObjectFlagsObjectRestType|ObjectFlagsInstantiationExpressionType) != 0) ||
		t.flags&TypeFlagsUnionOrIntersection != 0 && t.flags&TypeFlagsEnumLiteral == 0 && !c.isNonGenericTopLevelType(t) && core.Some(t.Types(), c.couldContainTypeVariables)
	t.objectFlags |= ObjectFlagsCouldContainTypeVariablesComputed | core.IfElse(result, ObjectFlagsCouldContainTypeVariables, 0)
	return result
}

func (c *Checker) isNonGenericTopLevelType(t *Type) bool {
	if t.alias != nil && len(t.alias.typeArguments) == 0 {
		declaration := ast.GetDeclarationOfKind(t.alias.symbol, ast.KindTypeAliasDeclaration)
		if declaration == nil {
			declaration = ast.GetDeclarationOfKind(t.alias.symbol, ast.KindJSTypeAliasDeclaration)
		}
		return declaration != nil && ast.FindAncestorOrQuit(declaration.Parent, func(n *ast.Node) ast.FindAncestorResult {
			switch n.Kind {
			case ast.KindSourceFile:
				return ast.FindAncestorTrue
			case ast.KindModuleDeclaration:
				return ast.FindAncestorFalse
			}
			return ast.FindAncestorQuit
		}) != nil
	}
	return false
}

func (c *Checker) instantiateTypeWorker(t *Type, m *TypeMapper, alias *TypeAlias) *Type {
	flags := t.flags
	switch {
	case flags&TypeFlagsTypeParameter != 0:
		return m.Map(t)
	case flags&TypeFlagsObject != 0:
		objectFlags := t.objectFlags
		if objectFlags&(ObjectFlagsReference|ObjectFlagsAnonymous|ObjectFlagsMapped) != 0 {
			if objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().node == nil {
				resolvedTypeArguments := t.AsTypeReference().resolvedTypeArguments
				newTypeArguments := c.instantiateTypes(resolvedTypeArguments, m)
				if core.Same(newTypeArguments, resolvedTypeArguments) {
					return t
				}
				return c.createNormalizedTypeReference(t.Target(), newTypeArguments)
			}
			if objectFlags&ObjectFlagsReverseMapped != 0 {
				return c.instantiateReverseMappedType(t, m)
			}
			return c.getObjectTypeInstantiation(t, m, alias)
		}
		return t
	case flags&TypeFlagsUnionOrIntersection != 0:
		source := t
		if t.flags&TypeFlagsUnion != 0 {
			origin := t.AsUnionType().origin
			if origin != nil && origin.flags&TypeFlagsUnionOrIntersection != 0 {
				source = origin
			}
		}
		types := source.Types()
		newTypes := c.instantiateTypes(types, m)
		if core.Same(newTypes, types) && alias.Symbol() == t.alias.Symbol() {
			return t
		}
		if alias == nil {
			alias = c.instantiateTypeAlias(t.alias, m)
		}
		if source.flags&TypeFlagsIntersection != 0 {
			return c.getIntersectionTypeEx(newTypes, IntersectionFlagsNone, alias)
		}
		return c.getUnionTypeEx(newTypes, UnionReductionLiteral, alias, nil /*origin*/)
	case flags&TypeFlagsIndex != 0:
		return c.getIndexType(c.instantiateType(t.Target(), m))
	case flags&TypeFlagsIndexedAccess != 0:
		if alias == nil {
			alias = c.instantiateTypeAlias(t.alias, m)
		}
		d := t.AsIndexedAccessType()
		return c.getIndexedAccessTypeEx(c.instantiateType(d.objectType, m), c.instantiateType(d.indexType, m), d.accessFlags, nil /*accessNode*/, alias)
	case flags&TypeFlagsTemplateLiteral != 0:
		return c.getTemplateLiteralType(t.AsTemplateLiteralType().texts, c.instantiateTypes(t.AsTemplateLiteralType().types, m))
	case flags&TypeFlagsStringMapping != 0:
		return c.getStringMappingType(t.symbol, c.instantiateType(t.AsStringMappingType().target, m))
	case flags&TypeFlagsConditional != 0:
		return c.getConditionalTypeInstantiation(t, c.combineTypeMappers(t.AsConditionalType().mapper, m), false /*forConstraint*/, alias)
	case flags&TypeFlagsSubstitution != 0:
		newBaseType := c.instantiateType(t.AsSubstitutionType().baseType, m)
		if c.isNoInferType(t) {
			return c.getNoInferType(newBaseType)
		}
		newConstraint := c.instantiateType(t.AsSubstitutionType().constraint, m)
		// A substitution type originates in the true branch of a conditional type and can be resolved
		// to just the base type in the same cases as the conditional type resolves to its true branch
		// (because the base type is then known to satisfy the constraint).
		if newBaseType.flags&TypeFlagsTypeVariable != 0 && c.isGenericType(newConstraint) {
			return c.getSubstitutionType(newBaseType, newConstraint)
		}
		if newConstraint.flags&TypeFlagsAnyOrUnknown != 0 || c.isTypeAssignableTo(c.getRestrictiveInstantiation(newBaseType), c.getRestrictiveInstantiation(newConstraint)) {
			return newBaseType
		}
		if newBaseType.flags&TypeFlagsTypeVariable != 0 {
			return c.getSubstitutionType(newBaseType, newConstraint)
		}
		return c.getIntersectionType([]*Type{newConstraint, newBaseType})
	}
	return t
}

// Handles instantiation of the following object types:
// AnonymousType (ObjectFlagsAnonymous|ObjectFlagsSingleSignatureType)
// TypeReference with node != nil (ObjectFlagsReference)
// InstantiationExpressionType (ObjectFlagsInstantiationExpressionType)
// MappedType (ObjectFlagsMapped)
func (c *Checker) getObjectTypeInstantiation(t *Type, m *TypeMapper, alias *TypeAlias) *Type {
	var declaration *ast.Node
	var target *Type
	var typeParameters []*Type
	switch {
	case t.objectFlags&ObjectFlagsReference != 0: // Deferred type reference
		declaration = t.AsTypeReference().node
	case t.objectFlags&ObjectFlagsInstantiationExpressionType != 0:
		declaration = t.AsInstantiationExpressionType().node
	default:
		declaration = t.symbol.Declarations[0]
	}
	links := c.typeNodeLinks.Get(declaration)
	switch {
	case t.objectFlags&ObjectFlagsReference != 0: // Deferred type reference
		target = links.resolvedType
	case t.objectFlags&ObjectFlagsInstantiated != 0:
		target = t.Target()
	default:
		target = t
	}
	typeParameters = links.outerTypeParameters
	if typeParameters == nil {
		// The first time an anonymous type is instantiated we compute and store a list of the type
		// parameters that are in scope (and therefore potentially referenced). For type literals that
		// aren't the right hand side of a generic type alias declaration we optimize by reducing the
		// set of type parameters to those that are possibly referenced in the literal.
		typeParameters = c.getOuterTypeParameters(declaration, true /*includeThisTypes*/)
		if len(target.alias.TypeArguments()) == 0 {
			if t.objectFlags&(ObjectFlagsReference|ObjectFlagsInstantiationExpressionType) != 0 {
				typeParameters = core.Filter(typeParameters, func(tp *Type) bool {
					return c.isTypeParameterPossiblyReferenced(tp, declaration)
				})
			} else if target.symbol.Flags&(ast.SymbolFlagsMethod|ast.SymbolFlagsTypeLiteral) != 0 {
				typeParameters = core.Filter(typeParameters, func(tp *Type) bool {
					return core.Some(t.symbol.Declarations, func(d *ast.Node) bool {
						return c.isTypeParameterPossiblyReferenced(tp, d)
					})
				})
			}
		}
		if typeParameters == nil {
			typeParameters = []*Type{}
		}
		links.outerTypeParameters = typeParameters
	}
	if len(typeParameters) == 0 {
		return t
	}
	// We are instantiating an anonymous type that has one or more type parameters in scope. Apply the
	// mapper to the type parameters to produce the effective list of type arguments, and compute the
	// instantiation cache key from the type IDs of the type arguments.
	combinedMapper := c.combineTypeMappers(t.Mapper(), m)
	typeArguments := make([]*Type, len(typeParameters))
	for i, tp := range typeParameters {
		typeArguments[i] = combinedMapper.Map(tp)
	}
	newAlias := alias
	if newAlias == nil {
		newAlias = c.instantiateTypeAlias(t.alias, m)
	}
	data := target.AsObjectType()
	key := getTypeInstantiationKey(typeArguments, newAlias, t.objectFlags&ObjectFlagsSingleSignatureType != 0)
	if data.instantiations == nil {
		data.instantiations = make(map[CacheHashKey]*Type)
		data.instantiations[getTypeInstantiationKey(typeParameters, target.alias, false)] = target
	}
	result := data.instantiations[key]
	if result == nil {
		newMapper := newTypeMapper(typeParameters, typeArguments)
		if target.objectFlags&ObjectFlagsSingleSignatureType != 0 && m != nil {
			newMapper = c.combineTypeMappers(newMapper, m)
		}
		switch {
		case target.objectFlags&ObjectFlagsReference != 0:
			result = c.createDeferredTypeReference(t.Target(), t.AsTypeReference().node, newMapper, newAlias)
		case target.objectFlags&ObjectFlagsMapped != 0:
			result = c.instantiateMappedType(target, newMapper, newAlias)
		default:
			result = c.instantiateAnonymousType(target, newMapper, newAlias)
		}
		data.instantiations[key] = result
		if result.flags&TypeFlagsObjectFlagsType != 0 && result.objectFlags&ObjectFlagsCouldContainTypeVariablesComputed == 0 {
			// if `result` is one of the object types we tried to make (it may not be, due to how `instantiateMappedType` works), we can carry forward the type variable containment check from the input type arguments
			resultCouldContainObjectFlags := core.Some(typeArguments, c.couldContainTypeVariables)
			if result.objectFlags&ObjectFlagsCouldContainTypeVariablesComputed == 0 {
				if result.objectFlags&(ObjectFlagsMapped|ObjectFlagsAnonymous|ObjectFlagsReference) != 0 {
					result.objectFlags |= ObjectFlagsCouldContainTypeVariablesComputed | core.IfElse(resultCouldContainObjectFlags, ObjectFlagsCouldContainTypeVariables, 0)
				} else {
					// If none of the type arguments for the outer type parameters contain type variables, it follows
					// that the instantiated type doesn't reference type variables.
					// Intrinsics have `CouldContainTypeVariablesComputed` pre-set, so this should only cover unions and intersections resulting from `instantiateMappedType`
					result.objectFlags |= core.IfElse(!resultCouldContainObjectFlags, ObjectFlagsCouldContainTypeVariablesComputed, 0)
				}
			}
		}
	}
	return result
}

func (c *Checker) isTypeParameterPossiblyReferenced(tp *Type, node *ast.Node) bool {
	var containsReference func(*ast.Node) bool
	containsReference = func(node *ast.Node) bool {
		switch node.Kind {
		case ast.KindThisType:
			return tp.AsTypeParameter().isThisType
		case ast.KindTypeReference:
			// use worker because we're looking for === equality
			if !tp.AsTypeParameter().isThisType && len(node.TypeArguments()) == 0 && c.getSymbolFromTypeReference(node) == tp.symbol {
				return true
			}
		case ast.KindTypeQuery:
			entityName := node.AsTypeQueryNode().ExprName
			firstIdentifier := ast.GetFirstIdentifier(entityName)
			if !ast.IsThisIdentifier(firstIdentifier) {
				firstIdentifierSymbol := c.getResolvedSymbol(firstIdentifier)
				tpDeclaration := tp.symbol.Declarations[0] // There is exactly one declaration, otherwise `containsReference` is not called
				var tpScope *ast.Node
				switch {
				case ast.IsTypeParameterDeclaration(tpDeclaration):
					tpScope = tpDeclaration.Parent // Type parameter is a regular type parameter, e.g. foo<T>
				case tp.AsTypeParameter().isThisType:
					tpScope = tpDeclaration // Type parameter is the this type, and its declaration is the class declaration.
				}
				if tpScope != nil {
					return core.Some(firstIdentifierSymbol.Declarations, func(d *ast.Node) bool { return isNodeDescendantOf(d, tpScope) }) ||
						core.Some(node.TypeArguments(), containsReference)
				}
			}
			return true
		case ast.KindMethodDeclaration, ast.KindMethodSignature:
			returnType := node.Type()
			return returnType == nil && node.Body() != nil ||
				core.Some(node.TypeParameters(), containsReference) ||
				core.Some(node.Parameters(), containsReference) ||
				returnType != nil && containsReference(returnType)
		}
		return node.ForEachChild(containsReference)
	}
	// If the type parameter doesn't have exactly one declaration, if there are intervening statement blocks
	// between the node and the type parameter declaration, if the node contains actual references to the
	// type parameter, or if the node contains type queries that we can't prove couldn't contain references to the type parameter,
	// we consider the type parameter possibly referenced.
	if tp.symbol != nil && len(tp.symbol.Declarations) == 1 {
		container := tp.symbol.Declarations[0].Parent
		for n := node; n != container; n = n.Parent {
			if n == nil || ast.IsBlock(n) || ast.IsConditionalTypeNode(n) && containsReference(n.AsConditionalTypeNode().ExtendsType) {
				return true
			}
		}
		return containsReference(node)
	}
	return true
}

func (c *Checker) instantiateAnonymousType(t *Type, m *TypeMapper, alias *TypeAlias) *Type {
	result := c.newObjectType(t.objectFlags&^(ObjectFlagsCouldContainTypeVariablesComputed|ObjectFlagsCouldContainTypeVariables)|ObjectFlagsInstantiated, t.symbol)
	switch {
	case t.objectFlags&ObjectFlagsMapped != 0:
		result.AsMappedType().declaration = t.AsMappedType().declaration
		// C.f. instantiateSignature
		origTypeParameter := c.getTypeParameterFromMappedType(t)
		freshTypeParameter := c.cloneTypeParameter(origTypeParameter)
		result.AsMappedType().typeParameter = freshTypeParameter
		m = c.combineTypeMappers(newSimpleTypeMapper(origTypeParameter, freshTypeParameter), m)
		freshTypeParameter.AsTypeParameter().mapper = m
	case t.objectFlags&ObjectFlagsInstantiationExpressionType != 0:
		result.AsInstantiationExpressionType().node = t.AsInstantiationExpressionType().node
	}
	if alias == nil {
		alias = c.instantiateTypeAlias(t.alias, m)
	}
	result.alias = alias
	if alias != nil && len(alias.typeArguments) != 0 {
		result.objectFlags |= c.getPropagatingFlagsOfTypes(result.alias.typeArguments, TypeFlagsNone)
	}
	d := result.AsObjectType()
	d.target = t
	d.mapper = m
	return result
}

func (c *Checker) getConditionalTypeInstantiation(t *Type, mapper *TypeMapper, forConstraint bool, alias *TypeAlias) *Type {
	root := t.AsConditionalType().root
	if len(root.outerTypeParameters) != 0 {
		// We are instantiating a conditional type that has one or more type parameters in scope. Apply the
		// mapper to the type parameters to produce the effective list of type arguments, and compute the
		// instantiation cache key from the type IDs of the type arguments.
		typeArguments := core.Map(root.outerTypeParameters, mapper.Map)
		key := getConditionalTypeKey(typeArguments, alias, forConstraint)
		result := root.instantiations[key]
		if result == nil {
			newMapper := newTypeMapper(root.outerTypeParameters, typeArguments)
			checkType := root.checkType
			var distributionType *Type
			if root.isDistributive {
				distributionType = c.getReducedType(newMapper.Map(checkType))
			}
			// Distributive conditional types are distributed over union types. For example, when the
			// distributive conditional type T extends U ? X : Y is instantiated with A | B for T, the
			// result is (A extends U ? X : Y) | (B extends U ? X : Y).
			if distributionType != nil && checkType != distributionType && distributionType.flags&(TypeFlagsUnion|TypeFlagsNever) != 0 {
				result = c.mapTypeWithAlias(distributionType, func(t *Type) *Type {
					return c.getConditionalType(root, prependTypeMapping(checkType, t, newMapper), forConstraint, nil)
				}, alias)
			} else {
				result = c.getConditionalType(root, newMapper, forConstraint, alias)
			}
			root.instantiations[key] = result
		}
		return result
	}
	return t
}

func (c *Checker) cloneTypeParameter(tp *Type) *Type {
	result := c.newTypeParameter(tp.symbol)
	result.AsTypeParameter().target = tp
	return result
}

func (c *Checker) getHomomorphicTypeVariable(t *Type) *Type {
	constraintType := c.getConstraintTypeFromMappedType(t)
	if constraintType.flags&TypeFlagsIndex != 0 {
		typeVariable := c.getActualTypeVariable(constraintType.AsIndexType().target)
		if typeVariable.flags&TypeFlagsTypeParameter != 0 {
			return typeVariable
		}
	}
	return nil
}

func (c *Checker) instantiateMappedType(t *Type, m *TypeMapper, alias *TypeAlias) *Type {
	// For a homomorphic mapped type { [P in keyof T]: X }, where T is some type variable, the mapping
	// operation depends on T as follows:
	// * If T is a primitive type no mapping is performed and the result is simply T.
	// * If T is a union type we distribute the mapped type over the union.
	// * If T is an array we map to an array where the element type has been transformed.
	// * If T is a tuple we map to a tuple where the element types have been transformed.
	// * If T is an intersection of array or tuple types we map to an intersection of transformed array or tuple types.
	// * Otherwise we map to an object type where the type of each property has been transformed.
	// For example, when T is instantiated to a union type A | B, we produce { [P in keyof A]: X } |
	// { [P in keyof B]: X }, and when when T is instantiated to a union type A | undefined, we produce
	// { [P in keyof A]: X } | undefined.
	d := t.AsMappedType()
	typeVariable := c.getHomomorphicTypeVariable(t)
	var instantiateConstituent func(*Type) *Type
	instantiateConstituent = func(s *Type) *Type {
		if s.flags&(TypeFlagsAnyOrUnknown|TypeFlagsInstantiableNonPrimitive|TypeFlagsObject|TypeFlagsIntersection) == 0 || s == c.wildcardType || c.isErrorType(s) {
			return s
		}
		if d.declaration.NameType == nil {
			if c.isArrayType(s) || s.flags&TypeFlagsAny != 0 && c.findResolutionCycleStartIndex(typeVariable, TypeSystemPropertyNameResolvedBaseConstraint) < 0 && c.hasArrayOrTypeTypeConstraint(typeVariable) {
				return c.instantiateMappedArrayType(s, t, prependTypeMapping(typeVariable, s, m))
			}
			if isTupleType(s) {
				return c.instantiateMappedTupleType(s, t, typeVariable, m)
			}
			if c.isArrayOrTupleOrIntersection(s) {
				return c.getIntersectionType(core.Map(s.Types(), instantiateConstituent))
			}
		}
		return c.instantiateAnonymousType(t, prependTypeMapping(typeVariable, s, m), nil)
	}
	if typeVariable != nil {
		mappedTypeVariable := c.instantiateType(typeVariable, m)
		if typeVariable != mappedTypeVariable {
			return c.mapTypeWithAlias(c.getReducedType(mappedTypeVariable), instantiateConstituent, alias)
		}
	}
	// If the constraint type of the instantiation is the wildcard type, return the wildcard type.
	if c.instantiateType(c.getConstraintTypeFromMappedType(t), m) == c.wildcardType {
		return c.wildcardType
	}
	return c.instantiateAnonymousType(t, m, alias)
}

func (c *Checker) hasArrayOrTypeTypeConstraint(typeVariable *Type) bool {
	constraint := c.getConstraintOfTypeParameter(typeVariable)
	return constraint != nil && everyType(constraint, c.isArrayOrTupleType)
}

func (c *Checker) instantiateMappedArrayType(arrayType *Type, mappedType *Type, m *TypeMapper) *Type {
	elementType := c.instantiateMappedTypeTemplate(mappedType, c.numberType, true /*isOptional*/, m)
	if c.isErrorType(elementType) {
		return c.errorType
	}
	return c.createArrayTypeEx(elementType, getModifiedReadonlyState(c.isReadonlyArrayType(arrayType), getMappedTypeModifiers(mappedType)))
}

func (c *Checker) instantiateMappedTupleType(tupleType *Type, mappedType *Type, typeVariable *Type, m *TypeMapper) *Type {
	// We apply the mapped type's template type to each of the fixed part elements. For variadic elements, we
	// apply the mapped type itself to the variadic element type. For other elements in the variable part of the
	// tuple, we surround the element type with an array type and apply the mapped type to that. This ensures
	// that we get sequential property key types for the fixed part of the tuple, and property key type number
	// for the remaining elements. For example
	//
	//   type Keys<T> = { [K in keyof T]: K };
	//   type Foo<T extends any[]> = Keys<[string, string, ...T, string]>; // ["0", "1", ...Keys<T>, number]
	//
	elementInfos := tupleType.TargetTupleType().elementInfos
	fixedLength := tupleType.TargetTupleType().fixedLength
	fixedMapper := m
	if fixedLength != 0 {
		fixedMapper = prependTypeMapping(typeVariable, tupleType, m)
	}
	modifiers := getMappedTypeModifiers(mappedType)
	elementTypes := c.getElementTypes(tupleType)
	newElementTypes := make([]*Type, len(elementTypes))
	newElementInfos := slices.Clone(elementInfos)
	for i, e := range elementTypes {
		flags := elementInfos[i].flags
		var mapped *Type
		switch {
		case i < fixedLength:
			mapped = c.instantiateMappedTypeTemplate(mappedType, c.getStringLiteralType(strconv.Itoa(i)), flags&ElementFlagsOptional != 0, fixedMapper)
		case flags&ElementFlagsVariadic != 0:
			mapped = c.instantiateType(mappedType, prependTypeMapping(typeVariable, e, m))
		default:
			mapped = c.getElementTypeOfArrayType(c.instantiateType(mappedType, prependTypeMapping(typeVariable, c.createArrayType(e), m)))
			if mapped == nil {
				mapped = c.unknownType
			}
		}
		switch {
		case modifiers&MappedTypeModifiersIncludeOptional != 0:
			if flags&ElementFlagsRequired != 0 {
				newElementInfos[i].flags = ElementFlagsOptional
			}
		case modifiers&MappedTypeModifiersExcludeOptional != 0:
			if flags&ElementFlagsOptional != 0 {
				newElementInfos[i].flags = ElementFlagsRequired
			}
		}
		newElementTypes[i] = mapped
	}
	newReadonly := getModifiedReadonlyState(tupleType.TargetTupleType().readonly, getMappedTypeModifiers(mappedType))
	if slices.Contains(newElementTypes, c.errorType) {
		return c.errorType
	}
	return c.createTupleTypeEx(newElementTypes, newElementInfos, newReadonly)
}

func (c *Checker) instantiateMappedTypeTemplate(t *Type, key *Type, isOptional bool, m *TypeMapper) *Type {
	templateMapper := appendTypeMapping(m, c.getTypeParameterFromMappedType(t), key)
	propType := c.instantiateType(c.getTemplateTypeFromMappedType(core.OrElse(t.AsMappedType().target, t)), templateMapper)
	modifiers := getMappedTypeModifiers(t)
	switch {
	case c.strictNullChecks && modifiers&MappedTypeModifiersIncludeOptional != 0 && !c.maybeTypeOfKind(propType, TypeFlagsUndefined|TypeFlagsVoid):
		return c.getOptionalType(propType, true /*isProperty*/)
	case c.strictNullChecks && modifiers&MappedTypeModifiersExcludeOptional != 0 && isOptional:
		return c.getTypeWithFacts(propType, TypeFactsNEUndefined)
	default:
		return propType
	}
}

func getModifiedReadonlyState(state bool, modifiers MappedTypeModifiers) bool {
	switch {
	case modifiers&MappedTypeModifiersIncludeReadonly != 0:
		return true
	case modifiers&MappedTypeModifiersExcludeReadonly != 0:
		return false
	}
	return state
}

func (c *Checker) getTypeParameterFromMappedType(t *Type) *Type {
	m := t.AsMappedType()
	if m.typeParameter == nil {
		m.typeParameter = c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(m.declaration.TypeParameter))
	}
	return m.typeParameter
}

func (c *Checker) getConstraintTypeFromMappedType(t *Type) *Type {
	m := t.AsMappedType()
	if m.constraintType == nil {
		m.constraintType = core.OrElse(c.getConstraintOfTypeParameter(c.getTypeParameterFromMappedType(t)), c.errorType)
	}
	return m.constraintType
}

func (c *Checker) getNameTypeFromMappedType(t *Type) *Type {
	m := t.AsMappedType()
	if m.declaration.NameType == nil {
		return nil
	}
	if m.nameType == nil {
		m.nameType = c.instantiateType(c.getTypeFromTypeNode(m.declaration.NameType), m.mapper)
	}
	return m.nameType
}

func (c *Checker) getTemplateTypeFromMappedType(t *Type) *Type {
	m := t.AsMappedType()
	if m.templateType == nil {
		if m.declaration.Type != nil {
			m.templateType = c.instantiateType(c.addOptionalityEx(c.getTypeFromTypeNode(m.declaration.Type) /*isProperty*/, true, getMappedTypeModifiers(t)&MappedTypeModifiersIncludeOptional != 0), m.mapper)
		} else {
			m.templateType = c.errorType
		}
	}
	return m.templateType
}

func (c *Checker) isMappedTypeWithKeyofConstraintDeclaration(t *Type) bool {
	constraintDeclaration := c.getConstraintDeclarationForMappedType(t)
	return ast.IsTypeOperatorNode(constraintDeclaration) && constraintDeclaration.AsTypeOperatorNode().Operator == ast.KindKeyOfKeyword
}

func (c *Checker) getConstraintDeclarationForMappedType(t *Type) *ast.Node {
	return t.AsMappedType().declaration.TypeParameter.AsTypeParameterDeclaration().Constraint
}

func (c *Checker) getApparentMappedTypeKeys(nameType *Type, targetType *Type) *Type {
	modifiersType := c.getApparentType(c.getModifiersTypeFromMappedType(targetType))
	var mappedKeys []*Type
	c.forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, TypeFlagsStringOrNumberLiteralOrUnique, false, func(t *Type) {
		mappedKeys = append(mappedKeys, c.instantiateType(nameType, appendTypeMapping(targetType.Mapper(), c.getTypeParameterFromMappedType(targetType), t)))
	})
	return c.getUnionType(mappedKeys)
}

func (c *Checker) forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(t *Type, include TypeFlags, stringsOnly bool, cb func(keyType *Type)) {
	for _, prop := range c.getPropertiesOfType(t) {
		cb(c.getLiteralTypeFromProperty(prop, include, false))
	}
	if t.flags&TypeFlagsAny != 0 {
		cb(c.stringType)
	} else {
		for _, info := range c.getIndexInfosOfType(t) {
			if !stringsOnly || info.keyType.flags&(TypeFlagsString|TypeFlagsTemplateLiteral) != 0 {
				cb(info.keyType)
			}
		}
	}
}

func (c *Checker) instantiateReverseMappedType(t *Type, m *TypeMapper) *Type {
	r := t.AsReverseMappedType()
	innerMappedType := c.instantiateType(r.mappedType, m)
	if innerMappedType.objectFlags&ObjectFlagsMapped == 0 {
		return t
	}
	innerIndexType := c.instantiateType(r.constraintType, m)
	if innerIndexType.flags&TypeFlagsIndex == 0 {
		return t
	}
	instantiated := c.inferTypeForHomomorphicMappedType(c.instantiateType(r.source, m), innerMappedType, innerIndexType)
	if instantiated != nil {
		return instantiated
	}
	return t
	// Nested invocation of `inferTypeForHomomorphicMappedType` or the `source` instantiated into something unmappable
}

func (c *Checker) instantiateTypeAlias(alias *TypeAlias, m *TypeMapper) *TypeAlias {
	if alias == nil {
		return nil
	}
	return &TypeAlias{symbol: alias.symbol, typeArguments: c.instantiateTypes(alias.typeArguments, m)}
}

func (c *Checker) instantiateTypes(types []*Type, m *TypeMapper) []*Type {
	return instantiateList(c, types, m, (*Checker).instantiateType)
}

func (c *Checker) instantiateSymbols(symbols []*ast.Symbol, m *TypeMapper) []*ast.Symbol {
	return instantiateList(c, symbols, m, (*Checker).instantiateSymbol)
}

func (c *Checker) instantiateSignatures(signatures []*Signature, m *TypeMapper) []*Signature {
	return instantiateList(c, signatures, m, (*Checker).instantiateSignature)
}

func (c *Checker) instantiateIndexInfos(indexInfos []*IndexInfo, m *TypeMapper) []*IndexInfo {
	return instantiateList(c, indexInfos, m, (*Checker).instantiateIndexInfo)
}

func instantiateList[T comparable](c *Checker, values []T, m *TypeMapper, instantiator func(c *Checker, value T, m *TypeMapper) T) []T {
	for i, value := range values {
		mapped := instantiator(c, value, m)
		if mapped != value {
			result := make([]T, len(values))
			copy(result, values[:i])
			result[i] = mapped
			for j := i + 1; j < len(values); j++ {
				result[j] = instantiator(c, values[j], m)
			}
			return result
		}
	}
	return values
}

func (c *Checker) tryGetTypeFromTypeNode(node *ast.Node) *Type {
	typeNode := node.Type()
	if typeNode != nil {
		return c.getTypeFromTypeNode(typeNode)
	}
	return nil
}

func (c *Checker) getTypeFromTypeNode(node *ast.Node) *Type {
	return c.getConditionalFlowTypeOfType(c.getTypeFromTypeNodeWorker(node), node)
}

func (c *Checker) getTypeFromTypeNodeWorker(node *ast.Node) *Type {
	switch node.Kind {
	case ast.KindAnyKeyword, ast.KindJSDocAllType:
		return c.anyType
	case ast.KindJSDocNonNullableType:
		return c.getTypeFromTypeNode(node.Type())
	case ast.KindJSDocNullableType:
		t := c.getTypeFromTypeNode(node.Type())
		if c.strictNullChecks {
			return c.getNullableType(t, TypeFlagsNull)
		} else {
			return t
		}
	case ast.KindJSDocVariadicType:
		return c.createArrayType(c.getTypeFromTypeNode(node.AsJSDocVariadicType().Type))
	case ast.KindJSDocOptionalType:
		return c.addOptionality(c.getTypeFromTypeNode(node.Type()))
	case ast.KindUnknownKeyword:
		return c.unknownType
	case ast.KindStringKeyword:
		return c.stringType
	case ast.KindNumberKeyword:
		return c.numberType
	case ast.KindBigIntKeyword:
		return c.bigintType
	case ast.KindBooleanKeyword:
		return c.booleanType
	case ast.KindSymbolKeyword:
		return c.esSymbolType
	case ast.KindVoidKeyword:
		return c.voidType
	case ast.KindUndefinedKeyword:
		return c.undefinedType
	case ast.KindNullKeyword:
		return c.nullType
	case ast.KindNeverKeyword:
		return c.neverType
	case ast.KindObjectKeyword:
		return c.nonPrimitiveType
	case ast.KindIntrinsicKeyword:
		return c.intrinsicMarkerType
	case ast.KindThisType, ast.KindThisKeyword:
		return c.getTypeFromThisTypeNode(node)
	case ast.KindLiteralType:
		return c.getTypeFromLiteralTypeNode(node)
	case ast.KindTypeReference, ast.KindExpressionWithTypeArguments:
		return c.getTypeFromTypeReference(node)
	case ast.KindTypePredicate:
		if node.AsTypePredicateNode().AssertsModifier != nil {
			return c.voidType
		}
		return c.booleanType
	case ast.KindTypeQuery:
		return c.getTypeFromTypeQueryNode(node)
	case ast.KindArrayType, ast.KindTupleType:
		return c.getTypeFromArrayOrTupleTypeNode(node)
	case ast.KindOptionalType:
		return c.getTypeFromOptionalTypeNode(node)
	case ast.KindUnionType:
		return c.getTypeFromUnionTypeNode(node)
	case ast.KindIntersectionType:
		return c.getTypeFromIntersectionTypeNode(node)
	case ast.KindNamedTupleMember:
		return c.getTypeFromNamedTupleTypeNode(node)
	case ast.KindParenthesizedType:
		return c.getTypeFromTypeNode(node.Type())
	case ast.KindRestType:
		return c.getTypeFromRestTypeNode(node)
	case ast.KindFunctionType, ast.KindConstructorType, ast.KindTypeLiteral:
		return c.getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node)
	case ast.KindTypeOperator:
		return c.getTypeFromTypeOperatorNode(node)
	case ast.KindIndexedAccessType:
		return c.getTypeFromIndexedAccessTypeNode(node)
	case ast.KindTemplateLiteralType:
		return c.getTypeFromTemplateTypeNode(node)
	case ast.KindMappedType:
		return c.getTypeFromMappedTypeNode(node)
	case ast.KindConditionalType:
		return c.getTypeFromConditionalTypeNode(node)
	case ast.KindInferType:
		return c.getTypeFromInferTypeNode(node)
	case ast.KindImportType:
		return c.getTypeFromImportTypeNode(node)
	default:
		return c.errorType
	}
}

func (c *Checker) getTypeFromThisTypeNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		links.resolvedType = c.getThisType(node)
	}
	return links.resolvedType
}

func (c *Checker) getThisType(node *ast.Node) *Type {
	container := ast.GetThisContainer(node /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/, false)
	if container != nil {
		parent := container.Parent
		if parent != nil && (ast.IsClassLike(parent) || ast.IsInterfaceDeclaration(parent)) {
			if !ast.IsStatic(container) && (!ast.IsConstructorDeclaration(container) || isNodeDescendantOf(node, container.Body())) {
				return core.Coalesce(c.getDeclaredTypeOfClassOrInterface(c.getSymbolOfDeclaration(parent)).AsInterfaceType().thisType, c.errorType)
			}
		}
	}
	c.error(node, diagnostics.A_this_type_is_available_only_in_a_non_static_member_of_a_class_or_interface)
	return c.errorType
}

func (c *Checker) getTypeFromLiteralTypeNode(node *ast.Node) *Type {
	if node.AsLiteralTypeNode().Literal.Kind == ast.KindNullKeyword {
		return c.nullType
	}
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		links.resolvedType = c.getRegularTypeOfLiteralType(c.checkExpression(node.AsLiteralTypeNode().Literal))
	}
	return links.resolvedType
}

func (c *Checker) getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		// Deferred resolution of members is handled by resolveObjectTypeMembers
		alias := c.getAliasForTypeNode(node)
		if sym := node.Symbol(); sym == nil || len(c.getMembersOfSymbol(sym)) == 0 && alias == nil {
			links.resolvedType = c.emptyTypeLiteralType
		} else {
			t := c.newObjectType(ObjectFlagsAnonymous, node.Symbol())
			t.alias = alias
			links.resolvedType = t
		}
	}
	return links.resolvedType
}

func (c *Checker) getTypeFromIndexedAccessTypeNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		objectType := c.getTypeFromTypeNode(node.AsIndexedAccessTypeNode().ObjectType)
		indexType := c.getTypeFromTypeNode(node.AsIndexedAccessTypeNode().IndexType)
		potentialAlias := c.getAliasForTypeNode(node)
		links.resolvedType = c.getIndexedAccessTypeEx(objectType, indexType, AccessFlagsNone, node, potentialAlias)
	}
	return links.resolvedType
}

func (c *Checker) getTypeFromTypeOperatorNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		argType := node.Type()
		switch node.AsTypeOperatorNode().Operator {
		case ast.KindKeyOfKeyword:
			links.resolvedType = c.getIndexType(c.getTypeFromTypeNode(argType))
		case ast.KindUniqueKeyword:
			if argType.Kind == ast.KindSymbolKeyword {
				links.resolvedType = c.getESSymbolLikeTypeForNode(ast.WalkUpParenthesizedTypes(node.Parent))
			} else {
				links.resolvedType = c.errorType
			}
		case ast.KindReadonlyKeyword:
			links.resolvedType = c.getTypeFromTypeNode(argType)
		default:
			panic("Unhandled case in getTypeFromTypeOperatorNode")
		}
	}
	return links.resolvedType
}

func (c *Checker) getESSymbolLikeTypeForNode(node *ast.Node) *Type {
	if isValidESSymbolDeclaration(node) {
		symbol := c.getSymbolOfNode(node)
		if symbol != nil {
			uniqueType := c.uniqueESSymbolTypes[symbol]
			if uniqueType == nil {
				var b strings.Builder
				b.WriteString(ast.InternalSymbolNamePrefix)
				b.WriteByte('@')
				b.WriteString(symbol.Name)
				b.WriteByte('@')
				b.WriteString(strconv.FormatUint(uint64(ast.GetSymbolId(symbol)), 10))
				uniqueType = c.newUniqueESSymbolType(symbol, b.String())
				c.uniqueESSymbolTypes[symbol] = uniqueType
			}
			return uniqueType
		}
	}
	return c.esSymbolType
}

func (c *Checker) getTypeFromTypeReference(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		// Cache both the resolved symbol and the resolved type. The resolved symbol is needed when we check the
		// type reference in checkTypeReferenceNode.
		// handle LS queries on the `const` in `x as const` by resolving to the type of `x`
		if isConstTypeReference(node) && ast.IsAssertionExpression(node.Parent) {
			links.resolvedType = c.checkExpressionCached(node.Parent.Expression())
		} else if t := c.getIntendedTypeFromJSDocTypeReference(node); t != nil {
			links.resolvedType = t
		} else {
			links.resolvedType = c.getTypeReferenceType(node, c.getSymbolFromTypeReference(node))
		}
	}
	return links.resolvedType
}

func (c *Checker) getIntendedTypeFromJSDocTypeReference(node *ast.Node) *Type {
	if node.Flags&ast.NodeFlagsJSDoc != 0 && ast.IsTypeReferenceNode(node) {
		typeName := node.AsTypeReferenceNode().TypeName
		if ast.IsIdentifier(typeName) {
			typeArgs := node.TypeArguments()
			switch typeName.Text() {
			case "String":
				c.checkNoTypeArguments(node, nil)
				return c.stringType
			case "Number":
				c.checkNoTypeArguments(node, nil)
				return c.numberType
			case "BigInt":
				c.checkNoTypeArguments(node, nil)
				return c.bigintType
			case "Boolean":
				c.checkNoTypeArguments(node, nil)
				return c.booleanType
			case "Void":
				c.checkNoTypeArguments(node, nil)
				return c.voidType
			case "Undefined":
				c.checkNoTypeArguments(node, nil)
				return c.undefinedType
			case "Null":
				c.checkNoTypeArguments(node, nil)
				return c.nullType
			case "Function", "function":
				c.checkNoTypeArguments(node, nil)
				return c.globalFunctionType
			case "array":
				if len(typeArgs) == 0 && !c.noImplicitAny {
					return c.anyArrayType
				}
			case "promise":
				if len(typeArgs) == 0 && !c.noImplicitAny {
					return c.createPromiseType(c.anyType)
				}
			case "Object":
				if len(typeArgs) == 2 {
					if recordSymbol := c.getGlobalRecordSymbol(); recordSymbol != nil {
						if indexType := c.getTypeFromTypeNode(typeArgs[0]); c.isValidIndexKeyType(indexType) {
							return c.getTypeAliasInstantiation(recordSymbol, []*Type{indexType, c.getTypeFromTypeNode(typeArgs[1])}, nil)
						}
					}
					return c.anyType
				}
				if !c.noImplicitAny {
					c.checkNoTypeArguments(node, nil)
					return c.anyType
				}
			}
		}
	}
	return nil
}

func (c *Checker) getSymbolFromTypeReference(node *ast.Node) *ast.Symbol {
	links := c.symbolNodeLinks.Get(node)
	if links.resolvedSymbol == nil {
		if isConstTypeReference(node) && ast.IsAssertionExpression(node.Parent) {
			links.resolvedSymbol = c.unknownSymbol
		} else {
			links.resolvedSymbol = c.resolveTypeReferenceName(node, ast.SymbolFlagsType, false /*ignoreErrors*/)
		}
	}
	return links.resolvedSymbol
}

func (c *Checker) resolveTypeReferenceName(typeReference *ast.Node, meaning ast.SymbolFlags, ignoreErrors bool) *ast.Symbol {
	name := getTypeReferenceName(typeReference)
	if name == nil {
		return c.unknownSymbol
	}
	symbol := c.resolveEntityName(name, meaning, ignoreErrors, false /*dontResolveAlias*/, nil /*location*/)
	if symbol != nil && symbol != c.unknownSymbol {
		return symbol
	}
	if ignoreErrors {
		return c.unknownSymbol
	}
	return c.getUnresolvedSymbolForEntityName(name)
}

func (c *Checker) getUnresolvedSymbolForEntityName(name *ast.Node) *ast.Symbol {
	var identifier *ast.Node
	switch name.Kind {
	case ast.KindQualifiedName:
		identifier = name.AsQualifiedName().Right
	case ast.KindPropertyAccessExpression:
		identifier = name.Name()
	default:
		identifier = name
	}
	text := identifier.Text()
	if text != "" {
		var parentSymbol *ast.Symbol
		switch name.Kind {
		case ast.KindQualifiedName:
			parentSymbol = c.getUnresolvedSymbolForEntityName(name.AsQualifiedName().Left)
		case ast.KindPropertyAccessExpression:
			parentSymbol = c.getUnresolvedSymbolForEntityName(name.Expression())
		}
		var path string
		if parentSymbol != nil {
			path = getSymbolPath(parentSymbol) + "." + text
		} else {
			path = text
		}
		result := c.unresolvedSymbols[path]
		if result == nil {
			result = c.newSymbolEx(ast.SymbolFlagsTypeAlias, text, ast.CheckFlagsUnresolved)
			c.unresolvedSymbols[path] = result
			result.Parent = parentSymbol
			c.typeAliasLinks.Get(result).declaredType = c.unresolvedType
		}
		return result
	}
	return c.unknownSymbol
}

func getSymbolPath(symbol *ast.Symbol) string {
	if symbol.Parent != nil {
		return getSymbolPath(symbol.Parent) + "." + symbol.Name
	}
	return symbol.Name
}

func (c *Checker) getTypeReferenceType(node *ast.Node, symbol *ast.Symbol) *Type {
	if symbol == c.unknownSymbol {
		return c.errorType
	}
	if symbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0 {
		return c.getTypeFromClassOrInterfaceReference(node, symbol)
	}
	if symbol.Flags&ast.SymbolFlagsTypeAlias != 0 {
		return c.getTypeFromTypeAliasReference(node, symbol)
	}
	// Get type from reference to named type that cannot be generic (enum or type parameter)
	res := c.tryGetDeclaredTypeOfSymbol(symbol)
	if res != nil && c.checkNoTypeArguments(node, symbol) {
		return c.getRegularTypeOfLiteralType(res)
	}

	// !!! Resolving values as types for JS
	return c.errorType
}

/**
 * Get type from type-reference that reference to class or interface
 */
func (c *Checker) getTypeFromClassOrInterfaceReference(node *ast.Node, symbol *ast.Symbol) *Type {
	t := c.getDeclaredTypeOfClassOrInterface(c.getMergedSymbol(symbol))
	d := t.AsInterfaceType()
	typeParameters := d.LocalTypeParameters()
	if len(typeParameters) != 0 {
		numTypeArguments := len(node.TypeArguments())
		minTypeArgumentCount := c.getMinTypeArgumentCount(typeParameters)
		isJs := ast.IsInJSFile(node)
		isJsImplicitAny := !c.noImplicitAny && isJs
		if !isJsImplicitAny && (numTypeArguments < minTypeArgumentCount || numTypeArguments > len(typeParameters)) {
			var message *diagnostics.Message

			missingAugmentsTag := isJs && ast.IsExpressionWithTypeArguments(node) && !ast.IsJSDocAugmentsTag(node.Parent)
			if missingAugmentsTag {
				message = diagnostics.Expected_0_type_arguments_provide_these_with_an_extends_tag
				if minTypeArgumentCount < len(typeParameters) {
					message = diagnostics.Expected_0_1_type_arguments_provide_these_with_an_extends_tag
				}
			} else {
				message = diagnostics.Generic_type_0_requires_1_type_argument_s
				if minTypeArgumentCount < len(typeParameters) {
					message = diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments
				}
			}
			typeStr := c.TypeToStringEx(t, nil /*enclosingDeclaration*/, TypeFormatFlagsWriteArrayAsGenericType, nil)
			c.error(node, message, typeStr, minTypeArgumentCount, len(typeParameters))
			if !isJs {
				// TODO: Adopt same permissive behavior in TS as in JS to reduce follow-on editing experience failures (requires editing fillMissingTypeArguments)
				return c.errorType
			}
		}
		if node.Kind == ast.KindTypeReference && c.isDeferredTypeReferenceNode(node, numTypeArguments != len(typeParameters)) {
			return c.createDeferredTypeReference(t, node, nil /*mapper*/, nil /*alias*/)
		}
		// In a type reference, the outer type parameters of the referenced class or interface are automatically
		// supplied as type arguments and the type reference only specifies arguments for the local type parameters
		// of the class or interface.
		localTypeArguments := c.fillMissingTypeArguments(c.getTypeArgumentsFromNode(node), typeParameters, minTypeArgumentCount, isJs)
		typeArguments := append(d.OuterTypeParameters(), localTypeArguments...)
		return c.createTypeReferenceEx(t, typeArguments, ObjectFlagsFromTypeNode)
	}
	if c.checkNoTypeArguments(node, symbol) {
		return t
	}
	return c.errorType
}

func (c *Checker) getTypeArgumentsFromNode(node *ast.Node) []*Type {
	return core.Map(node.TypeArguments(), c.getTypeFromTypeNode)
}

func (c *Checker) checkNoTypeArguments(node *ast.Node, symbol *ast.Symbol) bool {
	if len(node.TypeArguments()) != 0 {
		var typeName string
		if symbol != nil {
			typeName = c.symbolToString(symbol)
		} else {
			typeName = scanner.DeclarationNameToString(node.AsTypeReferenceNode().TypeName)
		}
		c.error(node, diagnostics.Type_0_is_not_generic, typeName)
		return false
	}
	return true
}

// Return true if the given type reference node is directly aliased or if it needs to be deferred
// because it is possibly contained in a circular chain of eagerly resolved types.
func (c *Checker) isDeferredTypeReferenceNode(node *ast.Node, hasDefaultTypeArguments bool) bool {
	if c.getAliasSymbolForTypeNode(node) != nil {
		return true
	}
	if c.isResolvedByTypeAlias(node) {
		switch node.Kind {
		case ast.KindArrayType:
			return c.mayResolveTypeAlias(node.AsArrayTypeNode().ElementType)
		case ast.KindTupleType:
			return core.Some(node.Elements(), c.mayResolveTypeAlias)
		case ast.KindTypeReference:
			return hasDefaultTypeArguments || core.Some(node.TypeArguments(), c.mayResolveTypeAlias)
		}
		panic("Unhandled case in isDeferredTypeReferenceNode")
	}
	return false
}

// Return true when the given node is transitively contained in type constructs that eagerly
// resolve their constituent types. We include SyntaxKind.TypeReference because type arguments
// of type aliases are eagerly resolved.
func (c *Checker) isResolvedByTypeAlias(node *ast.Node) bool {
	parent := node.Parent
	switch parent.Kind {
	case ast.KindParenthesizedType, ast.KindNamedTupleMember, ast.KindTypeReference, ast.KindUnionType, ast.KindIntersectionType,
		ast.KindIndexedAccessType, ast.KindConditionalType, ast.KindTypeOperator, ast.KindArrayType, ast.KindTupleType:
		return c.isResolvedByTypeAlias(parent)
	case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration:
		return true
	}
	return false
}

// Return true if resolving the given node (i.e. getTypeFromTypeNode) possibly causes resolution
// of a type alias.
func (c *Checker) mayResolveTypeAlias(node *ast.Node) bool {
	switch node.Kind {
	case ast.KindTypeReference:
		return c.resolveTypeReferenceName(node, ast.SymbolFlagsType, false).Flags&ast.SymbolFlagsTypeAlias != 0
	case ast.KindTypeQuery:
		return true
	case ast.KindTypeOperator:
		return node.AsTypeOperatorNode().Operator != ast.KindUniqueKeyword && c.mayResolveTypeAlias(node.Type())
	case ast.KindParenthesizedType, ast.KindOptionalType, ast.KindNamedTupleMember:
		return c.mayResolveTypeAlias(node.Type())
	case ast.KindRestType:
		return node.Type().Kind != ast.KindArrayType || c.mayResolveTypeAlias(node.Type().AsArrayTypeNode().ElementType)
	case ast.KindUnionType:
		return core.Some(node.AsUnionTypeNode().Types.Nodes, c.mayResolveTypeAlias)
	case ast.KindIntersectionType:
		return core.Some(node.AsIntersectionTypeNode().Types.Nodes, c.mayResolveTypeAlias)
	case ast.KindIndexedAccessType:
		return c.mayResolveTypeAlias(node.AsIndexedAccessTypeNode().ObjectType) || c.mayResolveTypeAlias(node.AsIndexedAccessTypeNode().IndexType)
	case ast.KindConditionalType:
		return c.mayResolveTypeAlias(node.AsConditionalTypeNode().CheckType) || c.mayResolveTypeAlias(node.AsConditionalTypeNode().ExtendsType) ||
			c.mayResolveTypeAlias(node.AsConditionalTypeNode().TrueType) || c.mayResolveTypeAlias(node.AsConditionalTypeNode().FalseType)
	}
	return false
}

func (c *Checker) createNormalizedTypeReference(target *Type, typeArguments []*Type) *Type {
	if target.objectFlags&ObjectFlagsTuple != 0 {
		return c.createNormalizedTupleType(target, typeArguments)
	}
	return c.createTypeReference(target, typeArguments)
}

func (c *Checker) createNormalizedTupleTypeEx(target *Type, elementTypes []*Type, objectFlags ObjectFlags) *Type {
	d := target.AsTupleType()
	if d.combinedFlags&ElementFlagsNonRequired == 0 {
		// No need to normalize when we only have regular required elements
		return c.createTypeReferenceEx(target, elementTypes, objectFlags)
	}
	if d.combinedFlags&ElementFlagsVariadic != 0 {
		for i, e := range elementTypes {
			if i < len(d.elementInfos) && d.elementInfos[i].flags&ElementFlagsVariadic != 0 && e.flags&(TypeFlagsNever|TypeFlagsUnion) != 0 {
				// Transform [A, ...(X | Y | Z)] into [A, ...X] | [A, ...Y] | [A, ...Z]
				checkTypes := core.MapIndex(elementTypes, func(t *Type, i int) *Type {
					if i < len(d.elementInfos) && d.elementInfos[i].flags&ElementFlagsVariadic != 0 {
						return t
					}
					return c.unknownType
				})
				if c.checkCrossProductUnion(checkTypes) {
					return c.mapType(e, func(t *Type) *Type {
						return c.createNormalizedTupleTypeEx(target, core.ReplaceElement(elementTypes, i, t), objectFlags)
					})
				}
			}
		}
	}
	// We have optional, rest, or variadic elements that may need normalizing. Normalization ensures that all variadic
	// elements are generic and that the tuple type has one of the following layouts, disregarding variadic elements:
	// (1) Zero or more required elements, followed by zero or more optional elements, followed by zero or one rest element.
	// (2) Zero or more required elements, followed by a rest element, followed by zero or more required elements.
	// In either layout, zero or more generic variadic elements may be present at any location.
	// Note that the element types may contain an extra 'this' type argument that we want to ignore during normalization
	// and then just append to the normalized element types.
	n := &TupleNormalizer{}
	if !n.normalize(c, elementTypes[:len(d.elementInfos)], d.elementInfos) {
		return c.errorType
	}
	if len(elementTypes) > len(d.elementInfos) {
		n.types = append(n.types, elementTypes[len(d.elementInfos)])
	}
	tupleTarget := c.getTupleTargetType(n.infos, d.readonly)
	switch {
	case tupleTarget == c.emptyGenericType:
		return c.emptyObjectType
	case len(n.types) != 0:
		return c.createTypeReferenceEx(tupleTarget, n.types, objectFlags)
	}
	return tupleTarget
}

func (c *Checker) createNormalizedTupleType(target *Type, elementTypes []*Type) *Type {
	return c.createNormalizedTupleTypeEx(target, elementTypes, ObjectFlagsNone)
}

type TupleNormalizer struct {
	c                       *Checker
	types                   []*Type
	infos                   []TupleElementInfo
	lastRequiredIndex       int
	firstRestIndex          int
	lastOptionalOrRestIndex int
}

func (n *TupleNormalizer) normalize(c *Checker, elementTypes []*Type, elementInfos []TupleElementInfo) bool {
	n.c = c
	n.lastRequiredIndex = -1
	n.firstRestIndex = -1
	n.lastOptionalOrRestIndex = -1
	for i, t := range elementTypes {
		info := elementInfos[i]
		if info.flags&ElementFlagsVariadic != 0 {
			if t.flags&TypeFlagsAny != 0 {
				n.add(t, TupleElementInfo{flags: ElementFlagsRest, labeledDeclaration: info.labeledDeclaration})
			} else if t.flags&TypeFlagsInstantiableNonPrimitive != 0 || c.isGenericMappedType(t) {
				// Generic variadic elements stay as they are.
				n.add(t, info)
			} else if isTupleType(t) {
				spreadTypes := c.getElementTypes(t)
				if len(spreadTypes)+len(n.types) >= 10_000 {
					message := core.IfElse(ast.IsPartOfTypeNode(c.currentNode),
						diagnostics.Type_produces_a_tuple_type_that_is_too_large_to_represent,
						diagnostics.Expression_produces_a_tuple_type_that_is_too_large_to_represent)
					c.error(c.currentNode, message)
					return false
				}
				// Spread variadic elements with tuple types into the resulting tuple.
				spreadInfos := t.TargetTupleType().elementInfos
				for j, s := range spreadTypes {
					n.add(s, spreadInfos[j])
				}
			} else {
				// Treat everything else as an array type and create a rest element.
				var s *Type
				if c.isArrayLikeType(t) {
					s = c.getIndexTypeOfType(t, c.numberType)
				}
				if s == nil {
					s = c.errorType
				}
				n.add(s, TupleElementInfo{flags: ElementFlagsRest, labeledDeclaration: info.labeledDeclaration})
			}
		} else {
			// Copy other element kinds with no change.
			n.add(t, info)
		}
	}
	// Turn optional elements preceding the last required element into required elements
	for i := range n.lastRequiredIndex {
		if n.infos[i].flags&ElementFlagsOptional != 0 {
			n.infos[i].flags = ElementFlagsRequired
		}
	}
	if n.firstRestIndex >= 0 && n.firstRestIndex < n.lastOptionalOrRestIndex {
		// Turn elements between first rest and last optional/rest into a single rest element
		var types []*Type
		for i := n.firstRestIndex; i <= n.lastOptionalOrRestIndex; i++ {
			t := n.types[i]
			if n.infos[i].flags&ElementFlagsVariadic != 0 {
				t = c.getIndexedAccessType(t, c.numberType)
			}
			types = append(types, t)
		}
		n.types[n.firstRestIndex] = c.getUnionType(types)
		n.types = slices.Delete(n.types, n.firstRestIndex+1, n.lastOptionalOrRestIndex+1)
		n.infos = slices.Delete(n.infos, n.firstRestIndex+1, n.lastOptionalOrRestIndex+1)
	}
	return true
}

func (n *TupleNormalizer) add(t *Type, info TupleElementInfo) {
	if info.flags&ElementFlagsRequired != 0 {
		n.lastRequiredIndex = len(n.types)
	}
	if info.flags&ElementFlagsRest != 0 && n.firstRestIndex < 0 {
		n.firstRestIndex = len(n.types)
	}
	if info.flags&(ElementFlagsOptional|ElementFlagsRest) != 0 {
		n.lastOptionalOrRestIndex = len(n.types)
	}
	n.types = append(n.types, n.c.addOptionalityEx(t, true /*isProperty*/, info.flags&ElementFlagsOptional != 0))
	n.infos = append(n.infos, info)
}

// Return count of starting consecutive tuple elements of the given kind(s)
func getStartElementCount(t *TupleType, flags ElementFlags) int {
	for i, info := range t.elementInfos {
		if info.flags&flags == 0 {
			return i
		}
	}
	return len(t.elementInfos)
}

// Return count of ending consecutive tuple elements of the given kind(s)
func getEndElementCount(t *TupleType, flags ElementFlags) int {
	for i := len(t.elementInfos); i > 0; i-- {
		if t.elementInfos[i-1].flags&flags == 0 {
			return len(t.elementInfos) - i
		}
	}
	return len(t.elementInfos)
}

func getTotalFixedElementCount(t *TupleType) int {
	return t.fixedLength + getEndElementCount(t, ElementFlagsFixed)
}

func (c *Checker) getElementTypes(t *Type) []*Type {
	typeArguments := c.getTypeArguments(t)
	arity := c.getTypeReferenceArity(t)
	if len(typeArguments) == arity {
		return typeArguments
	}
	return typeArguments[0:arity]
}

func (c *Checker) getTypeReferenceArity(t *Type) int {
	return len(t.TargetInterfaceType().TypeParameters())
}

func (c *Checker) isArrayType(t *Type) bool {
	return t.objectFlags&ObjectFlagsReference != 0 && (t.Target() == c.globalArrayType || t.Target() == c.globalReadonlyArrayType)
}

func (c *Checker) isReadonlyArrayType(t *Type) bool {
	return t.objectFlags&ObjectFlagsReference != 0 && t.Target() == c.globalReadonlyArrayType
}

func isTupleType(t *Type) bool {
	return t.objectFlags&ObjectFlagsReference != 0 && t.Target().objectFlags&ObjectFlagsTuple != 0
}

func isMutableTupleType(t *Type) bool {
	return isTupleType(t) && !t.TargetTupleType().readonly
}

func isGenericTupleType(t *Type) bool {
	return isTupleType(t) && t.TargetTupleType().combinedFlags&ElementFlagsVariadic != 0
}

func isSingleElementGenericTupleType(t *Type) bool {
	return isGenericTupleType(t) && len(t.TargetTupleType().elementInfos) == 1
}

func (c *Checker) isArrayOrTupleType(t *Type) bool {
	return c.isArrayType(t) || isTupleType(t)
}

func (c *Checker) isMutableArrayOrTuple(t *Type) bool {
	return c.isArrayType(t) && !c.isReadonlyArrayType(t) || isTupleType(t) && !t.TargetTupleType().readonly
}

func (c *Checker) getElementTypeOfArrayType(t *Type) *Type {
	if c.isArrayType(t) {
		return c.getTypeArguments(t)[0]
	}
	return nil
}

func (c *Checker) isArrayLikeType(t *Type) bool {
	// A type is array-like if it is a reference to the global Array or global ReadonlyArray type,
	// or if it is not the undefined or null type and if it is assignable to ReadonlyArray<any>
	return c.isArrayType(t) || t.flags&TypeFlagsNullable == 0 && c.isTypeAssignableTo(t, c.anyReadonlyArrayType)
}

func (c *Checker) isMutableArrayLikeType(t *Type) bool {
	// A type is mutable-array-like if it is a reference to the global Array type, or if it is not the
	// any, undefined or null type and if it is assignable to Array<any>
	return c.isMutableArrayOrTuple(t) || t.flags&(TypeFlagsAny|TypeFlagsNullable) == 0 && c.isTypeAssignableTo(t, c.anyArrayType)
}

func (c *Checker) isEmptyArrayLiteralType(t *Type) bool {
	elementType := c.getElementTypeOfArrayType(t)
	return elementType != nil && c.isEmptyLiteralType(elementType)
}

func (c *Checker) isEmptyLiteralType(t *Type) bool {
	if c.strictNullChecks {
		return t == c.implicitNeverType
	}
	return t == c.undefinedWideningType
}

func (c *Checker) isTupleLikeType(t *Type) bool {
	if isTupleType(t) || c.getPropertyOfType(t, "0") != nil {
		return true
	}
	if c.isArrayLikeType(t) {
		if lengthType := c.getTypeOfPropertyOfType(t, "length"); lengthType != nil {
			return everyType(lengthType, func(t *Type) bool { return t.flags&TypeFlagsNumberLiteral != 0 })
		}
	}
	return false
}

func (c *Checker) isArrayOrTupleLikeType(t *Type) bool {
	return c.isArrayLikeType(t) || c.isTupleLikeType(t)
}

func (c *Checker) isArrayOrTupleOrIntersection(t *Type) bool {
	return t.flags&TypeFlagsIntersection != 0 && core.Every(t.Types(), c.isArrayOrTupleType)
}

func (c *Checker) getTupleElementType(t *Type, index int) *Type {
	propType := c.getTypeOfPropertyOfType(t, strconv.Itoa(index))
	if propType != nil {
		return propType
	}
	if everyType(t, isTupleType) {
		return c.getTupleElementTypeOutOfStartCount(t, jsnum.Number(index), core.IfElse(c.compilerOptions.NoUncheckedIndexedAccess == core.TSTrue, c.undefinedType, nil))
	}
	return nil
}

/**
 * Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include
 * references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the
 * declared type. Instantiations are cached using the type identities of the type arguments as the key.
 */
func (c *Checker) getTypeFromTypeAliasReference(node *ast.Node, symbol *ast.Symbol) *Type {
	typeArguments := node.TypeArguments()
	if symbol.CheckFlags&ast.CheckFlagsUnresolved != 0 {
		alias := &TypeAlias{symbol: symbol, typeArguments: core.Map(typeArguments, c.getTypeFromTypeNode)}
		key := getAliasKey(alias)
		errorType := c.errorTypes[key]
		if errorType == nil {
			errorType = c.newIntrinsicType(TypeFlagsAny, "error")
			errorType.alias = alias
			c.errorTypes[key] = errorType
		}
		return errorType
	}
	t := c.getDeclaredTypeOfSymbol(symbol)
	typeParameters := c.typeAliasLinks.Get(symbol).typeParameters
	if len(typeParameters) != 0 {
		numTypeArguments := len(typeArguments)
		minTypeArgumentCount := c.getMinTypeArgumentCount(typeParameters)
		if numTypeArguments < minTypeArgumentCount || numTypeArguments > len(typeParameters) {
			message := core.IfElse(minTypeArgumentCount == len(typeParameters),
				diagnostics.Generic_type_0_requires_1_type_argument_s,
				diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments)
			c.error(node, message, c.symbolToString(symbol), minTypeArgumentCount, len(typeParameters))
			return c.errorType
		}
		// We refrain from associating a local type alias with an instantiation of a top-level type alias
		// because the local alias may end up being referenced in an inferred return type where it is not
		// accessible--which in turn may lead to a large structural expansion of the type when generating
		// a .d.ts file. See #43622 for an example.
		aliasSymbol := c.getAliasSymbolForTypeNode(node)
		var newAliasSymbol *ast.Symbol
		if aliasSymbol != nil && (isLocalTypeAlias(symbol) || !isLocalTypeAlias(aliasSymbol)) {
			newAliasSymbol = aliasSymbol
		}
		var aliasTypeArguments []*Type
		if newAliasSymbol != nil {
			aliasTypeArguments = c.getTypeArgumentsForAliasSymbol(newAliasSymbol)
		} else if ast.IsTypeReferenceType(node) {
			aliasSymbol := c.resolveTypeReferenceName(node, ast.SymbolFlagsAlias, true /*ignoreErrors*/)
			// refers to an alias import/export/reexport - by making sure we use the target as an aliasSymbol,
			// we ensure the exported symbol is used to refer to the type when it is reserialized later
			if aliasSymbol != nil && aliasSymbol != c.unknownSymbol {
				resolved := c.resolveAlias(aliasSymbol)
				if resolved != nil && resolved.Flags&ast.SymbolFlagsTypeAlias != 0 {
					newAliasSymbol = resolved
					aliasTypeArguments = c.getTypeArgumentsFromNode(node)
				}
			}
		}
		var newAlias *TypeAlias
		if newAliasSymbol != nil {
			newAlias = &TypeAlias{symbol: newAliasSymbol, typeArguments: aliasTypeArguments}
		}
		return c.getTypeAliasInstantiation(symbol, c.getTypeArgumentsFromNode(node), newAlias)
	}
	if c.checkNoTypeArguments(node, symbol) {
		return t
	}
	return c.errorType
}

func (c *Checker) getTypeAliasInstantiation(symbol *ast.Symbol, typeArguments []*Type, alias *TypeAlias) *Type {
	t := c.getDeclaredTypeOfSymbol(symbol)
	if t == c.intrinsicMarkerType {
		if typeKind, ok := intrinsicTypeKinds[symbol.Name]; ok && len(typeArguments) == 1 {
			switch typeKind {
			case IntrinsicTypeKindNoInfer:
				return c.getNoInferType(typeArguments[0])
			default:
				return c.getStringMappingType(symbol, typeArguments[0])
			}
		}
	}
	links := c.typeAliasLinks.Get(symbol)
	typeParameters := links.typeParameters
	key := getTypeAliasInstantiationKey(typeArguments, alias)
	instantiation := links.instantiations[key]
	if instantiation == nil {
		mapper := newTypeMapper(typeParameters, c.fillMissingTypeArguments(typeArguments, typeParameters, c.getMinTypeArgumentCount(typeParameters), ast.IsInJSFile(symbol.ValueDeclaration)))
		instantiation = c.instantiateTypeWithAlias(t, mapper, alias)
		links.instantiations[key] = instantiation
	}
	return instantiation
}

func isLocalTypeAlias(symbol *ast.Symbol) bool {
	declaration := core.Find(symbol.Declarations, isTypeAlias)
	return declaration != nil && ast.GetContainingFunction(declaration) != nil
}

func (c *Checker) getDeclaredTypeOfSymbol(symbol *ast.Symbol) *Type {
	result := c.tryGetDeclaredTypeOfSymbol(symbol)
	if result == nil {
		result = c.errorType
	}
	return result
}

func (c *Checker) tryGetDeclaredTypeOfSymbol(symbol *ast.Symbol) *Type {
	switch {
	case symbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0:
		return c.getDeclaredTypeOfClassOrInterface(symbol)
	case symbol.Flags&ast.SymbolFlagsTypeParameter != 0:
		return c.getDeclaredTypeOfTypeParameter(symbol)
	case symbol.Flags&ast.SymbolFlagsTypeAlias != 0:
		return c.getDeclaredTypeOfTypeAlias(symbol)
	case symbol.Flags&ast.SymbolFlagsEnum != 0:
		return c.getDeclaredTypeOfEnum(symbol)
	case symbol.Flags&ast.SymbolFlagsEnumMember != 0:
		return c.getDeclaredTypeOfEnumMember(symbol)
	case symbol.Flags&ast.SymbolFlagsAlias != 0:
		return c.getDeclaredTypeOfAlias(symbol)
	}
	return nil
}

func getTypeReferenceName(node *ast.Node) *ast.Node {
	switch node.Kind {
	case ast.KindTypeReference:
		return node.AsTypeReferenceNode().TypeName
	case ast.KindExpressionWithTypeArguments:
		// We only support expressions that are simple qualified names. For other
		// expressions this produces nil
		expr := node.Expression()
		if ast.IsEntityNameExpression(expr) {
			return expr
		}
	}
	return nil
}

func (c *Checker) getAliasForTypeNode(node *ast.Node) *TypeAlias {
	symbol := c.getAliasSymbolForTypeNode(node)
	if symbol != nil {
		return &TypeAlias{symbol: symbol, typeArguments: c.getTypeArgumentsForAliasSymbol(symbol)}
	}
	return nil
}

func (c *Checker) getAliasSymbolForTypeNode(node *ast.Node) *ast.Symbol {
	host := node.Parent
	for ast.IsParenthesizedTypeNode(host) || ast.IsTypeOperatorNode(host) && host.AsTypeOperatorNode().Operator == ast.KindReadonlyKeyword {
		host = host.Parent
	}
	if isTypeAlias(host) {
		return c.getSymbolOfDeclaration(host)
	}
	return nil
}

func (c *Checker) getTypeArgumentsForAliasSymbol(symbol *ast.Symbol) []*Type {
	if symbol != nil {
		return c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
	}
	return nil
}

func (c *Checker) getOuterTypeParametersOfClassOrInterface(symbol *ast.Symbol) []*Type {
	declaration := symbol.ValueDeclaration
	if symbol.Flags&(ast.SymbolFlagsClass|ast.SymbolFlagsFunction) == 0 {
		declaration = core.Find(symbol.Declarations, func(d *ast.Node) bool {
			if ast.IsInterfaceDeclaration(d) {
				return true
			}
			if !ast.IsVariableDeclaration(d) {
				return false
			}
			initializer := d.Initializer()
			return initializer != nil && ast.IsFunctionExpressionOrArrowFunction(initializer)
		})
	}
	debug.Assert(declaration != nil, "Class was missing valueDeclaration -OR- non-class had no interface declarations")
	return c.getOuterTypeParameters(declaration, false /*includeThisTypes*/)
}

// Return the outer type parameters of a node or undefined if the node has no outer type parameters.
func (c *Checker) getOuterTypeParameters(node *ast.Node, includeThisTypes bool) []*Type {
	for {
		node = node.Parent
		if node == nil {
			return nil
		}
		kind := node.Kind
		switch kind {
		case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration, ast.KindCallSignature, ast.KindConstructSignature,
			ast.KindMethodSignature, ast.KindFunctionType, ast.KindConstructorType, ast.KindFunctionDeclaration,
			ast.KindMethodDeclaration, ast.KindFunctionExpression, ast.KindArrowFunction, ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindMappedType,
			ast.KindConditionalType:
			outerTypeParameters := c.getOuterTypeParameters(node, includeThisTypes)
			if (kind == ast.KindFunctionExpression || kind == ast.KindArrowFunction || ast.IsObjectLiteralMethod(node)) && c.isContextSensitive(node) {
				signature := core.FirstOrNil(c.getSignaturesOfType(c.getTypeOfSymbol(c.getSymbolOfDeclaration(node)), SignatureKindCall))
				if signature != nil && len(signature.typeParameters) != 0 {
					return append(outerTypeParameters, signature.typeParameters...)
				}
			}
			if kind == ast.KindMappedType {
				return append(outerTypeParameters, c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(node.AsMappedTypeNode().TypeParameter)))
			}
			if kind == ast.KindConditionalType {
				return append(outerTypeParameters, c.getInferTypeParameters(node)...)
			}
			outerAndOwnTypeParameters := c.appendTypeParameters(outerTypeParameters, node.TypeParameters())
			var thisType *Type
			if includeThisTypes && (kind == ast.KindClassDeclaration || kind == ast.KindClassExpression || kind == ast.KindInterfaceDeclaration) {
				thisType = c.getDeclaredTypeOfClassOrInterface(c.getSymbolOfDeclaration(node)).AsInterfaceType().thisType
			}
			if thisType != nil {
				return append(outerAndOwnTypeParameters, thisType)
			}
			return outerAndOwnTypeParameters
		}
	}
}

func (c *Checker) getInferTypeParameters(node *ast.Node) []*Type {
	var result []*Type
	for _, symbol := range node.Locals() {
		if symbol.Flags&ast.SymbolFlagsTypeParameter != 0 {
			result = append(result, c.getDeclaredTypeOfSymbol(symbol))
		}
	}
	return result
}

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

func (c *Checker) appendLocalTypeParametersOfClassOrInterfaceOrTypeAlias(types []*Type, symbol *ast.Symbol) []*Type {
	for _, node := range symbol.Declarations {
		if ast.NodeKindIs(node, ast.KindInterfaceDeclaration, ast.KindClassDeclaration, ast.KindClassExpression) || isTypeAlias(node) {
			types = c.appendTypeParameters(types, node.TypeParameters())
		}
	}
	return types
}

// Appends the type parameters given by a list of declarations to a set of type parameters and returns the resulting set.
// The function allocates a new array if the input type parameter set is undefined, but otherwise it modifies the set
// in-place and returns the same array.
func (c *Checker) appendTypeParameters(typeParameters []*Type, declarations []*ast.Node) []*Type {
	for _, declaration := range declarations {
		typeParameters = core.AppendIfUnique(typeParameters, c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(declaration)))
	}
	return typeParameters
}

func (c *Checker) getDeclaredTypeOfTypeParameter(symbol *ast.Symbol) *Type {
	links := c.declaredTypeLinks.Get(symbol)
	if links.declaredType == nil {
		links.declaredType = c.newTypeParameter(symbol)
	}
	return links.declaredType
}

func (c *Checker) getDeclaredTypeOfTypeAlias(symbol *ast.Symbol) *Type {
	links := c.typeAliasLinks.Get(symbol)
	if links.declaredType == nil {
		// Note that we use the links object as the target here because the symbol object is used as the unique
		// identity for resolution of the 'type' property in SymbolLinks.
		if !c.pushTypeResolution(symbol, TypeSystemPropertyNameDeclaredType) {
			return c.errorType
		}
		declaration := core.Find(symbol.Declarations, ast.IsTypeOrJSTypeAliasDeclaration)
		typeNode := declaration.Type()
		t := c.getTypeFromTypeNode(typeNode)
		if c.popTypeResolution() {
			typeParameters := c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)
			if len(typeParameters) != 0 {
				// Initialize the instantiation cache for generic type aliases. The declared type corresponds to
				// an instantiation of the type alias with the type parameters supplied as type arguments.
				links.typeParameters = typeParameters
				links.instantiations = make(map[CacheHashKey]*Type)
				links.instantiations[getTypeListKey(typeParameters)] = t
			}
			if t == c.intrinsicMarkerType && symbol.Name == "BuiltinIteratorReturn" {
				t = c.getBuiltinIteratorReturnType()
			}
		} else {
			errorNode := declaration.Name()
			if errorNode == nil {
				errorNode = declaration
			}
			c.error(errorNode, diagnostics.Type_alias_0_circularly_references_itself, c.symbolToString(symbol))
			t = c.errorType
		}
		if links.declaredType == nil {
			links.declaredType = t
		}
	}
	return links.declaredType
}

func (c *Checker) getDeclaredTypeOfEnum(symbol *ast.Symbol) *Type {
	links := c.declaredTypeLinks.Get(symbol)
	if !(links.declaredType != nil) {
		var memberTypeList []*Type
		for _, declaration := range symbol.Declarations {
			if declaration.Kind == ast.KindEnumDeclaration {
				for _, member := range declaration.Members() {
					if c.hasBindableName(member) {
						memberSymbol := c.getSymbolOfDeclaration(member)
						value := c.getEnumMemberValue(member).Value
						var memberType *Type
						if value != nil {
							memberType = c.getEnumLiteralType(value, symbol, memberSymbol)
						} else {
							memberType = c.createComputedEnumType(memberSymbol)
						}
						c.declaredTypeLinks.Get(memberSymbol).declaredType = c.getFreshTypeOfLiteralType(memberType)
						memberTypeList = append(memberTypeList, memberType)
					}
				}
			}
		}
		var enumType *Type
		if len(memberTypeList) != 0 {
			enumType = c.getUnionTypeEx(memberTypeList, UnionReductionLiteral, &TypeAlias{symbol: symbol}, nil /*origin*/)
		} else {
			enumType = c.createComputedEnumType(symbol)
		}
		if enumType.flags&TypeFlagsUnion != 0 {
			enumType.flags |= TypeFlagsEnumLiteral
			enumType.symbol = symbol
		}
		links.declaredType = enumType
	}
	return links.declaredType
}

func (c *Checker) getEnumMemberValue(node *ast.Node) evaluator.Result {
	c.computeEnumMemberValues(node.Parent)
	return c.enumMemberLinks.Get(node).value
}

func (c *Checker) createComputedEnumType(symbol *ast.Symbol) *Type {
	regularType := c.newLiteralType(TypeFlagsEnum, nil, nil)
	regularType.symbol = symbol
	freshType := c.newLiteralType(TypeFlagsEnum, nil, regularType)
	freshType.symbol = symbol
	regularType.AsLiteralType().freshType = freshType
	freshType.AsLiteralType().freshType = freshType
	return regularType
}

func (c *Checker) getDeclaredTypeOfEnumMember(symbol *ast.Symbol) *Type {
	links := c.declaredTypeLinks.Get(symbol)
	if !(links.declaredType != nil) {
		enumType := c.getDeclaredTypeOfEnum(c.getParentOfSymbol(symbol))
		if links.declaredType == nil {
			links.declaredType = enumType
		}
	}
	return links.declaredType
}

func (c *Checker) computeEnumMemberValues(node *ast.Node) {
	nodeLinks := c.nodeLinks.Get(node)
	if !(nodeLinks.flags&NodeCheckFlagsEnumValuesComputed != 0) {
		nodeLinks.flags |= NodeCheckFlagsEnumValuesComputed
		autoValue := new(jsnum.Number)
		var previous *ast.Node
		for _, member := range node.Members() {
			result := c.computeEnumMemberValue(member, autoValue, previous)
			c.enumMemberLinks.Get(member).value = result
			if value, isNumber := result.Value.(jsnum.Number); isNumber {
				nextValue := value + 1
				autoValue = &nextValue
			} else {
				autoValue = nil
			}
			previous = member
		}
	}
}

func (c *Checker) computeEnumMemberValue(member *ast.Node, autoValue *jsnum.Number, previous *ast.Node) evaluator.Result {
	if ast.IsComputedNonLiteralName(member.Name()) {
		c.error(member.Name(), diagnostics.Computed_property_names_are_not_allowed_in_enums)
	} else if ast.IsBigIntLiteral(member.Name()) {
		c.error(member.Name(), diagnostics.An_enum_member_cannot_have_a_numeric_name)
	} else {
		text := ast.GetTextOfPropertyName(member.Name())
		if isNumericLiteralName(text) && !ast.IsInfinityOrNaNString(text) {
			c.error(member.Name(), diagnostics.An_enum_member_cannot_have_a_numeric_name)
		}
	}
	if member.Initializer() != nil {
		return c.computeConstantEnumMemberValue(member)
	}
	// In ambient non-const numeric enum declarations, enum members without initializers are
	// considered computed members (as opposed to having auto-incremented values).
	if member.Parent.Flags&ast.NodeFlagsAmbient != 0 && !ast.IsEnumConst(member.Parent) {
		return evaluator.NewResult(nil, false, false, false)
	}
	// If the member declaration specifies no value, the member is considered a constant enum member.
	// If the member is the first member in the enum declaration, it is assigned the value zero.
	// Otherwise, it is assigned the value of the immediately preceding member plus one, and an error
	// occurs if the immediately preceding member is not a constant enum member.
	if autoValue == nil {
		c.error(member.Name(), diagnostics.Enum_member_must_have_initializer)
		return evaluator.NewResult(nil, false, false, false)
	}
	if c.compilerOptions.GetIsolatedModules() && previous != nil && previous.Initializer() != nil {
		prevValue := c.getEnumMemberValue(previous)
		_, prevIsNum := prevValue.Value.(jsnum.Number)
		if !prevIsNum || prevValue.ResolvedOtherFiles {
			c.error(member.Name(), diagnostics.Enum_member_following_a_non_literal_numeric_member_must_have_an_initializer_when_isolatedModules_is_enabled)
		}
	}
	return evaluator.NewResult(*autoValue, false, false, false)
}

func (c *Checker) computeConstantEnumMemberValue(member *ast.Node) evaluator.Result {
	isConstEnum := ast.IsEnumConst(member.Parent)
	initializer := member.Initializer()
	result := c.evaluate(initializer, member)
	switch {
	case result.Value != nil:
		if isConstEnum {
			if numValue, isNumber := result.Value.(jsnum.Number); isNumber && (numValue.IsInf() || numValue.IsNaN()) {
				c.error(initializer, core.IfElse(numValue.IsNaN(),
					diagnostics.X_const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN,
					diagnostics.X_const_enum_member_initializer_was_evaluated_to_a_non_finite_value))
			}
		}
		if c.compilerOptions.GetIsolatedModules() {
			if _, isString := result.Value.(string); isString && !result.IsSyntacticallyString {
				memberName := member.Parent.Name().Text() + "." + member.Name().Text()
				c.error(initializer, diagnostics.X_0_has_a_string_type_but_must_have_syntactically_recognizable_string_syntax_when_isolatedModules_is_enabled, memberName)
			}
		}
	case isConstEnum:
		c.error(initializer, diagnostics.X_const_enum_member_initializers_must_be_constant_expressions)
	case member.Parent.Flags&ast.NodeFlagsAmbient != 0:
		c.error(initializer, diagnostics.In_ambient_enum_declarations_member_initializer_must_be_constant_expression)
	default:
		c.checkTypeAssignableTo(c.checkExpression(initializer), c.numberType, initializer, diagnostics.Type_0_is_not_assignable_to_type_1_as_required_for_computed_enum_member_values)
	}
	return result
}

func (c *Checker) evaluateEntity(expr *ast.Node, location *ast.Node) evaluator.Result {
	switch expr.Kind {
	case ast.KindIdentifier, ast.KindPropertyAccessExpression:
		symbol := c.resolveEntityName(expr, ast.SymbolFlagsValue, true /*ignoreErrors*/, false, nil)
		if symbol == nil {
			return evaluator.NewResult(nil, false, false, false)
		}
		if expr.Kind == ast.KindIdentifier {
			if ast.IsInfinityOrNaNString(expr.Text()) && (symbol == c.getGlobalSymbol(expr.Text(), ast.SymbolFlagsValue, nil /*diagnostic*/)) {
				// Technically we resolved a global lib file here, but the decision to treat this as numeric
				// is more predicated on the fact that the single-file resolution *didn't* resolve to a
				// different meaning of `Infinity` or `NaN`. Transpilers handle this no problem.
				return evaluator.NewResult(jsnum.FromString(expr.Text()), false, false, false)
			}
		}
		if symbol.Flags&ast.SymbolFlagsEnumMember != 0 {
			if location != nil {
				return c.evaluateEnumMember(expr, symbol, location)
			}
			return c.getEnumMemberValue(symbol.ValueDeclaration)
		}
		if c.isConstantVariable(symbol) {
			declaration := symbol.ValueDeclaration
			if declaration != nil && ast.IsVariableDeclaration(declaration) && declaration.Type() == nil && declaration.Initializer() != nil &&
				(location == nil || declaration != location && c.isBlockScopedNameDeclaredBeforeUse(declaration, location)) {
				result := c.evaluate(declaration.Initializer(), declaration)
				if location != nil && ast.GetSourceFileOfNode(location) != ast.GetSourceFileOfNode(declaration) {
					return evaluator.NewResult(result.Value, false, true, true)
				}
				return evaluator.NewResult(result.Value, result.IsSyntacticallyString, result.ResolvedOtherFiles, true /*hasExternalReferences*/)
			}
		}
		return evaluator.NewResult(nil, false, false, false)
	case ast.KindElementAccessExpression:
		root := expr.Expression()
		if ast.IsEntityNameExpression(root) && ast.IsStringLiteralLike(expr.AsElementAccessExpression().ArgumentExpression) {
			rootSymbol := c.resolveEntityName(root, ast.SymbolFlagsValue, true /*ignoreErrors*/, false, nil)
			if rootSymbol != nil && rootSymbol.Flags&ast.SymbolFlagsEnum != 0 {
				name := expr.AsElementAccessExpression().ArgumentExpression.Text()
				member := rootSymbol.Exports[name]
				if member != nil {
					if location != nil {
						return c.evaluateEnumMember(expr, member, location)
					}
					return c.getEnumMemberValue(member.ValueDeclaration)
				}
			}
		}
		return evaluator.NewResult(nil, false, false, false)
	}
	panic("Unhandled case in evaluateEntity")
}

func (c *Checker) evaluateEnumMember(expr *ast.Node, symbol *ast.Symbol, location *ast.Node) evaluator.Result {
	declaration := symbol.ValueDeclaration
	if declaration == nil || declaration == location {
		c.error(expr, diagnostics.Property_0_is_used_before_being_assigned, c.symbolToString(symbol))
		return evaluator.NewResult(nil, false, false, false)
	}
	if !c.isBlockScopedNameDeclaredBeforeUse(declaration, location) {
		c.error(expr, diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums)
		return evaluator.NewResult(jsnum.Number(0), false, false, false)
	}
	value := c.getEnumMemberValue(declaration)
	if location.Parent != declaration.Parent {
		return evaluator.NewResult(value.Value, value.IsSyntacticallyString, value.ResolvedOtherFiles, true /*hasExternalReferences*/)
	}
	return value
}

func (c *Checker) getDeclaredTypeOfAlias(symbol *ast.Symbol) *Type {
	links := c.declaredTypeLinks.Get(symbol)
	if links.declaredType == nil {
		links.declaredType = c.getDeclaredTypeOfSymbol(c.resolveAlias(symbol))
	}
	return links.declaredType
}

func (c *Checker) getTypeFromTypeQueryNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		// TypeScript 1.0 spec (April 2014): 3.6.3
		// The expression is processed as an identifier expression (section 4.3)
		// or property access expression(section 4.10),
		// the widened type(section 3.9) of which becomes the result.
		t := c.checkExpressionWithTypeArguments(node)
		links.resolvedType = c.getRegularTypeOfLiteralType(c.getWidenedType(t))
	}
	return links.resolvedType
}

func (c *Checker) getTypeFromArrayOrTupleTypeNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		target := c.getArrayOrTupleTargetType(node)
		if target == c.emptyGenericType {
			links.resolvedType = c.emptyObjectType
		} else if !(node.Kind == ast.KindTupleType && core.Some(node.Elements(), c.isVariadicTupleElement)) && c.isDeferredTypeReferenceNode(node, false) {
			if node.Kind == ast.KindTupleType && len(node.Elements()) == 0 {
				links.resolvedType = target
			} else {
				links.resolvedType = c.createDeferredTypeReference(target, node, nil /*mapper*/, nil /*alias*/)
			}
		} else {
			var elementTypes []*Type
			if node.Kind == ast.KindArrayType {
				elementTypes = []*Type{c.getTypeFromTypeNode(node.AsArrayTypeNode().ElementType)}
			} else {
				elementTypes = core.Map(node.Elements(), c.getTypeFromTypeNode)
			}
			if target.objectFlags&ObjectFlagsTuple != 0 {
				links.resolvedType = c.createNormalizedTupleTypeEx(target, elementTypes, ObjectFlagsFromTypeNode)
			} else {
				links.resolvedType = c.createTypeReferenceEx(target, elementTypes, ObjectFlagsFromTypeNode)
			}
		}
	}
	return links.resolvedType
}

func (c *Checker) isVariadicTupleElement(node *ast.Node) bool {
	return c.getTupleElementFlags(node)&ElementFlagsVariadic != 0
}

func (c *Checker) getArrayOrTupleTargetType(node *ast.Node) *Type {
	readonly := c.isReadonlyTypeOperator(node.Parent)
	elementType := c.getArrayElementTypeNode(node)
	if elementType != nil {
		if readonly {
			return c.globalReadonlyArrayType
		}
		return c.globalArrayType
	}
	return c.getTupleTargetType(core.Map(node.Elements(), c.getTupleElementInfo), readonly)
}

func (c *Checker) isReadonlyTypeOperator(node *ast.Node) bool {
	return ast.IsTypeOperatorNode(node) && node.AsTypeOperatorNode().Operator == ast.KindReadonlyKeyword
}

func (c *Checker) getTypeFromNamedTupleTypeNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		if node.AsNamedTupleMember().DotDotDotToken != nil {
			links.resolvedType = c.getTypeFromRestTypeNode(node)
		} else {
			links.resolvedType = c.addOptionalityEx(c.getTypeFromTypeNode(node.Type()), true /*isProperty*/, node.QuestionToken() != nil)
		}
	}
	return links.resolvedType
}

func (c *Checker) getTypeFromRestTypeNode(node *ast.Node) *Type {
	typeNode := node.Type()
	elementTypeNode := c.getArrayElementTypeNode(typeNode)
	if elementTypeNode != nil {
		typeNode = elementTypeNode
	}
	return c.getTypeFromTypeNode(typeNode)
}

func (c *Checker) getArrayElementTypeNode(node *ast.Node) *ast.Node {
	switch node.Kind {
	case ast.KindParenthesizedType:
		return c.getArrayElementTypeNode(node.Type())
	case ast.KindTupleType:
		if len(node.Elements()) == 1 {
			node = node.Elements()[0]
			if node.Kind == ast.KindRestType {
				return c.getArrayElementTypeNode(node.Type())
			}
			if node.Kind == ast.KindNamedTupleMember && node.AsNamedTupleMember().DotDotDotToken != nil {
				return c.getArrayElementTypeNode(node.Type())
			}
		}
	case ast.KindArrayType:
		return node.AsArrayTypeNode().ElementType
	}
	return nil
}

func (c *Checker) getTypeFromOptionalTypeNode(node *ast.Node) *Type {
	return c.addOptionalityEx(c.getTypeFromTypeNode(node.Type()), true /*isProperty*/, true /*isOptional*/)
}

func (c *Checker) getTypeFromUnionTypeNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		alias := c.getAliasForTypeNode(node)
		links.resolvedType = c.getUnionTypeEx(core.Map(node.AsUnionTypeNode().Types.Nodes, c.getTypeFromTypeNode), UnionReductionLiteral, alias, nil /*origin*/)
	}
	return links.resolvedType
}

func (c *Checker) getTypeFromIntersectionTypeNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		alias := c.getAliasForTypeNode(node)
		types := core.Map(node.AsIntersectionTypeNode().Types.Nodes, c.getTypeFromTypeNode)
		// We perform no supertype reduction for X & {} or {} & X, where X is one of string, number, bigint,
		// or a pattern literal template type. This enables union types like "a" | "b" | string & {} or
		// "aa" | "ab" | `a${string}` which preserve the literal types for purposes of statement completion.
		noSupertypeReduction := false
		if len(types) == 2 {
			emptyIndex := slices.Index(types, c.emptyTypeLiteralType)
			if emptyIndex >= 0 {
				t := types[1-emptyIndex]
				noSupertypeReduction = t.flags&(TypeFlagsString|TypeFlagsNumber|TypeFlagsBigInt) != 0 || t.flags&TypeFlagsTemplateLiteral != 0 && c.isPatternLiteralType(t)
			}
		}
		links.resolvedType = c.getIntersectionTypeEx(types, core.IfElse(noSupertypeReduction, IntersectionFlagsNoSupertypeReduction, 0), alias)
	}
	return links.resolvedType
}

func (c *Checker) getTypeFromTemplateTypeNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		spans := node.AsTemplateLiteralTypeNode().TemplateSpans
		texts := make([]string, len(spans.Nodes)+1)
		types := make([]*Type, len(spans.Nodes))
		texts[0] = node.AsTemplateLiteralTypeNode().Head.Text()
		for i, span := range spans.Nodes {
			texts[i+1] = span.AsTemplateLiteralTypeSpan().Literal.Text()
			types[i] = c.getTypeFromTypeNode(span.Type())
		}
		links.resolvedType = c.getTemplateLiteralType(texts, types)
	}
	return links.resolvedType
}

func (c *Checker) getTypeFromMappedTypeNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		t := c.newObjectType(ObjectFlagsMapped, node.Symbol())
		t.AsMappedType().declaration = node.AsMappedTypeNode()
		t.alias = c.getAliasForTypeNode(node)
		links.resolvedType = t
		// Eagerly resolve the constraint type which forces an error if the constraint type circularly
		// references itself through one or more type aliases.
		c.getConstraintTypeFromMappedType(t)
	}
	return links.resolvedType
}

func (c *Checker) getTypeFromConditionalTypeNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		checkType := c.getTypeFromTypeNode(node.AsConditionalTypeNode().CheckType)
		alias := c.getAliasForTypeNode(node)
		allOuterTypeParameters := c.getOuterTypeParameters(node, true /*includeThisTypes*/)
		var outerTypeParameters []*Type
		if alias != nil && len(alias.typeArguments) != 0 {
			outerTypeParameters = allOuterTypeParameters
		} else {
			outerTypeParameters = core.Filter(allOuterTypeParameters, func(tp *Type) bool { return c.isTypeParameterPossiblyReferenced(tp, node) })
		}
		root := &ConditionalRoot{
			node:                node.AsConditionalTypeNode(),
			checkType:           checkType,
			extendsType:         c.getTypeFromTypeNode(node.AsConditionalTypeNode().ExtendsType),
			isDistributive:      checkType.flags&TypeFlagsTypeParameter != 0,
			inferTypeParameters: c.getInferTypeParameters(node),
			outerTypeParameters: outerTypeParameters,
			instantiations:      nil,
			alias:               alias,
		}
		links.resolvedType = c.getConditionalType(root, nil /*mapper*/, false /*forConstraint*/, nil)
		if outerTypeParameters != nil {
			root.instantiations = make(map[CacheHashKey]*Type)
			root.instantiations[getConditionalTypeKey(outerTypeParameters, nil /*alias*/, false /*forConstraint*/)] = links.resolvedType
		}
	}
	return links.resolvedType
}

func (c *Checker) getConditionalType(root *ConditionalRoot, mapper *TypeMapper, forConstraint bool, alias *TypeAlias) *Type {
	var result *Type
	var extraTypes []*Type
	tailCount := 0
	// We loop here for an immediately nested conditional type in the false position, effectively treating
	// types of the form 'A extends B ? X : C extends D ? Y : E extends F ? Z : ...' as a single construct for
	// purposes of resolution. We also loop here when resolution of a conditional type ends in resolution of
	// another (or, through recursion, possibly the same) conditional type. In the potentially tail-recursive
	// cases we increment the tail recursion counter and stop after 1000 iterations.
	for {
		if tailCount == 1000 {
			c.error(c.currentNode, diagnostics.Type_instantiation_is_excessively_deep_and_possibly_infinite)
			return c.errorType
		}
		checkType := c.instantiateType(c.getActualTypeVariable(root.checkType), mapper)
		extendsType := c.instantiateType(root.extendsType, mapper)
		if checkType == c.errorType || extendsType == c.errorType {
			return c.errorType
		}
		if checkType == c.wildcardType || extendsType == c.wildcardType {
			return c.wildcardType
		}
		checkTypeNode := ast.SkipTypeParentheses(root.node.CheckType)
		extendsTypeNode := ast.SkipTypeParentheses(root.node.ExtendsType)
		// When the check and extends types are simple tuple types of the same arity, we defer resolution of the
		// conditional type when any tuple elements are generic. This is such that non-distributable conditional
		// types can be written `[X] extends [Y] ? ...` and be deferred similarly to `X extends Y ? ...`.
		checkTuples := c.isSimpleTupleType(checkTypeNode) && c.isSimpleTupleType(extendsTypeNode) && len(checkTypeNode.Elements()) == len(extendsTypeNode.Elements())
		checkTypeDeferred := c.isDeferredType(checkType, checkTuples)
		var combinedMapper *TypeMapper
		if len(root.inferTypeParameters) != 0 {
			// When we're looking at making an inference for an infer type, when we get its constraint, it'll automagically be
			// instantiated with the context, so it doesn't need the mapper for the inference context - however the constraint
			// may refer to another _root_, _uncloned_ `infer` type parameter [1], or to something mapped by `mapper` [2].
			// [1] Eg, if we have `Foo<T, U extends T>` and `Foo<number, infer B>` - `B` is constrained to `T`, which, in turn, has been instantiated
			// as `number`
			// Conversely, if we have `Foo<infer A, infer B>`, `B` is still constrained to `T` and `T` is instantiated as `A`
			// [2] Eg, if we have `Foo<T, U extends T>` and `Foo<Q, infer B>` where `Q` is mapped by `mapper` into `number` - `B` is constrained to `T`
			// which is in turn instantiated as `Q`, which is in turn instantiated as `number`.
			// So we need to:
			//    * combine `context.nonFixingMapper` with `mapper` so their constraints can be instantiated in the context of `mapper` (otherwise they'd only get inference context information)
			//    * incorporate all of the component mappers into the combined mapper for the true and false members
			// This means we have two mappers that need applying:
			//    * The original `mapper` used to create this conditional
			//    * The mapper that maps the infer type parameter to its inference result (`context.mapper`)
			context := c.newInferenceContext(root.inferTypeParameters, nil /*signature*/, InferenceFlagsNone, nil)
			if mapper != nil {
				context.nonFixingMapper = c.combineTypeMappers(context.nonFixingMapper, mapper)
			}
			if !checkTypeDeferred {
				// We don't want inferences from constraints as they may cause us to eagerly resolve the
				// conditional type instead of deferring resolution. Also, we always want strict function
				// types rules (i.e. proper contravariance) for inferences.
				c.inferTypes(context.inferences, checkType, extendsType, InferencePriorityNoConstraints|InferencePriorityAlwaysStrict, false)
			}
			// It's possible for 'infer T' type parameters to be given uninstantiated constraints when the
			// those type parameters are used in type references (see getInferredTypeParameterConstraint). For
			// that reason we need context.mapper to be first in the combined mapper. See #42636 for examples.
			if mapper != nil {
				combinedMapper = c.combineTypeMappers(context.mapper, mapper)
			} else {
				combinedMapper = context.mapper
			}
		}
		// Instantiate the extends type including inferences for 'infer T' type parameters
		var inferredExtendsType *Type
		if combinedMapper != nil {
			inferredExtendsType = c.instantiateType(root.extendsType, combinedMapper)
		} else {
			inferredExtendsType = extendsType
		}
		// We attempt to resolve the conditional type only when the check and extends types are non-generic
		if !checkTypeDeferred && !c.isDeferredType(inferredExtendsType, checkTuples) {
			// Return falseType for a definitely false extends check. We check an instantiations of the two
			// types with type parameters mapped to the wildcard type, the most permissive instantiations
			// possible (the wildcard type is assignable to and from all types). If those are not related,
			// then no instantiations will be and we can just return the false branch type.
			if inferredExtendsType.flags&TypeFlagsAnyOrUnknown == 0 && (checkType.flags&TypeFlagsAny != 0 || !c.isTypeAssignableTo(c.getPermissiveInstantiation(checkType), c.getPermissiveInstantiation(inferredExtendsType))) {
				// Return union of trueType and falseType for 'any' since it matches anything. Furthermore, for a
				// distributive conditional type applied to the constraint of a type variable, include trueType if
				// there are possible values of the check type that are also possible values of the extends type.
				// We use a reverse assignability check as it is less expensive than the comparable relationship
				// and avoids false positives of a non-empty intersection check.
				if checkType.flags&TypeFlagsAny != 0 || forConstraint && inferredExtendsType.flags&TypeFlagsNever == 0 && someType(c.getPermissiveInstantiation(inferredExtendsType), func(t *Type) bool {
					return c.isTypeAssignableTo(t, c.getPermissiveInstantiation(checkType))
				}) {
					extraTypes = append(extraTypes, c.instantiateType(c.getTypeFromTypeNode(root.node.TrueType), core.OrElse(combinedMapper, mapper)))
				}
				// If falseType is an immediately nested conditional type that isn't distributive or has an
				// identical checkType, switch to that type and loop.
				falseType := c.getTypeFromTypeNode(root.node.FalseType)
				if falseType.flags&TypeFlagsConditional != 0 {
					newRoot := falseType.AsConditionalType().root
					if newRoot.node.Parent == root.node.AsNode() && (!newRoot.isDistributive || newRoot.checkType == root.checkType) {
						root = newRoot
						continue
					}
					if newRoot, newRootMapper := c.getTailRecursionRoot(falseType, mapper); newRoot != nil {
						root = newRoot
						mapper = newRootMapper
						alias = nil
						if newRoot.alias != nil {
							tailCount++
						}
						continue
					}
				}
				result = c.instantiateType(falseType, mapper)
				break
			}
			// Return trueType for a definitely true extends check. We check instantiations of the two
			// types with type parameters mapped to their restrictive form, i.e. a form of the type parameter
			// that has no constraint. This ensures that, for example, the type
			//   type Foo<T extends { x: any }> = T extends { x: string } ? string : number
			// doesn't immediately resolve to 'string' instead of being deferred.
			if inferredExtendsType.flags&TypeFlagsAnyOrUnknown != 0 || c.isTypeAssignableTo(c.getRestrictiveInstantiation(checkType), c.getRestrictiveInstantiation(inferredExtendsType)) {
				trueType := c.getTypeFromTypeNode(root.node.TrueType)
				trueMapper := core.OrElse(combinedMapper, mapper)
				if newRoot, newRootMapper := c.getTailRecursionRoot(trueType, trueMapper); newRoot != nil {
					root = newRoot
					mapper = newRootMapper
					alias = nil
					if newRoot.alias != nil {
						tailCount++
					}
					continue
				}
				result = c.instantiateType(trueType, trueMapper)
				break
			}
		}
		// Return a deferred type for a check that is neither definitely true nor definitely false
		result = c.newConditionalType(root, mapper, combinedMapper)
		if alias != nil {
			result.alias = alias
		} else {
			result.alias = c.instantiateTypeAlias(root.alias, mapper)
		}
		break
	}
	if extraTypes != nil {
		return c.getUnionType(append(extraTypes, result))
	}
	return result
}

// We tail-recurse for generic conditional types that (a) have not already been evaluated and cached, and
// (b) are non distributive, have a check type that is unaffected by instantiation, or have a non-union check
// type. Note that recursion is possible only through aliased conditional types, so we only increment the tail
// recursion counter for those.
func (c *Checker) getTailRecursionRoot(newType *Type, newMapper *TypeMapper) (*ConditionalRoot, *TypeMapper) {
	if newType.flags&TypeFlagsConditional != 0 && newMapper != nil {
		newRoot := newType.AsConditionalType().root
		if len(newRoot.outerTypeParameters) != 0 {
			typeParamMapper := c.combineTypeMappers(newType.AsConditionalType().mapper, newMapper)
			typeArguments := core.Map(newRoot.outerTypeParameters, func(t *Type) *Type { return typeParamMapper.Map(t) })
			newRootMapper := newTypeMapper(newRoot.outerTypeParameters, typeArguments)
			var newCheckType *Type
			if newRoot.isDistributive {
				newCheckType = newRootMapper.Map(newRoot.checkType)
			}
			if newCheckType == nil || newCheckType == newRoot.checkType || newCheckType.flags&(TypeFlagsUnion|TypeFlagsNever) == 0 {
				return newRoot, newRootMapper
			}
		}
	}
	return nil, nil
}

func (c *Checker) isSimpleTupleType(node *ast.Node) bool {
	return ast.IsTupleTypeNode(node) && len(node.Elements()) > 0 && !core.Some(node.Elements(), func(e *ast.Node) bool {
		return ast.IsOptionalTypeNode(e) || ast.IsRestTypeNode(e) || ast.IsNamedTupleMember(e) && (e.QuestionToken() != nil || e.AsNamedTupleMember().DotDotDotToken != nil)
	})
}

func (c *Checker) isDeferredType(t *Type, checkTuples bool) bool {
	return c.isGenericType(t) || checkTuples && isTupleType(t) && core.Some(c.getElementTypes(t), c.isGenericType)
}

func (c *Checker) getPermissiveInstantiation(t *Type) *Type {
	if t.flags&(TypeFlagsPrimitive|TypeFlagsAnyOrUnknown|TypeFlagsNever) != 0 {
		return t
	}
	key := CachedTypeKey{kind: CachedTypeKindPermissiveInstantiation, typeId: t.id}
	if cached := c.cachedTypes[key]; cached != nil {
		return cached
	}
	result := c.instantiateType(t, c.permissiveMapper)
	c.cachedTypes[key] = result
	return result
}

func (c *Checker) getRestrictiveInstantiation(t *Type) *Type {
	if t.flags&(TypeFlagsPrimitive|TypeFlagsAnyOrUnknown|TypeFlagsNever) != 0 {
		return t
	}
	key := CachedTypeKey{kind: CachedTypeKindRestrictiveInstantiation, typeId: t.id}
	if cached := c.cachedTypes[key]; cached != nil {
		return cached
	}
	result := c.instantiateType(t, c.restrictiveMapper)
	c.cachedTypes[key] = result
	// We set the following so we don't attempt to set the restrictive instance of a restrictive instance
	// which is redundant - we'll produce new type identities, but all type params have already been mapped.
	// This also gives us a way to detect restrictive instances upon comparisons and _disable_ the "distributeive constraint"
	// assignability check for them, which is distinctly unsafe, as once you have a restrctive instance, all the type parameters
	// are constrained to `unknown` and produce tons of false positives/negatives!
	c.cachedTypes[CachedTypeKey{kind: CachedTypeKindRestrictiveInstantiation, typeId: result.id}] = result
	return result
}

func (c *Checker) getRestrictiveTypeParameter(t *Type) *Type {
	if t.AsTypeParameter().constraint == nil && c.getConstraintDeclaration(t) == nil || t.AsTypeParameter().constraint == c.noConstraintType {
		return t
	}
	key := CachedTypeKey{kind: CachedTypeKindRestrictiveTypeParameter, typeId: t.id}
	if cached := c.cachedTypes[key]; cached != nil {
		return cached
	}
	result := c.newTypeParameter(t.symbol)
	result.AsTypeParameter().constraint = c.noConstraintType
	c.cachedTypes[key] = result
	return result
}

func (c *Checker) restrictiveMapperWorker(t *Type) *Type {
	if t.flags&TypeFlagsTypeParameter != 0 {
		return c.getRestrictiveTypeParameter(t)
	}
	return t
}

func (c *Checker) permissiveMapperWorker(t *Type) *Type {
	if t.flags&TypeFlagsTypeParameter != 0 {
		return c.wildcardType
	}
	return t
}

func (c *Checker) getTrueTypeFromConditionalType(t *Type) *Type {
	d := t.AsConditionalType()
	if d.resolvedTrueType == nil {
		d.resolvedTrueType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.TrueType), d.mapper)
	}
	return d.resolvedTrueType
}

func (c *Checker) getFalseTypeFromConditionalType(t *Type) *Type {
	d := t.AsConditionalType()
	if d.resolvedFalseType == nil {
		d.resolvedFalseType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.FalseType), d.mapper)
	}
	return d.resolvedFalseType
}

func (c *Checker) getInferredTrueTypeFromConditionalType(t *Type) *Type {
	d := t.AsConditionalType()
	if d.resolvedInferredTrueType == nil {
		if d.combinedMapper != nil {
			d.resolvedInferredTrueType = c.instantiateType(c.getTypeFromTypeNode(d.root.node.TrueType), d.combinedMapper)
		} else {
			d.resolvedInferredTrueType = c.getTrueTypeFromConditionalType(t)
		}
	}
	return d.resolvedInferredTrueType
}

func (c *Checker) getTypeFromInferTypeNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		links.resolvedType = c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(node.AsInferTypeNode().TypeParameter))
	}
	return links.resolvedType
}

func (c *Checker) getTypeFromImportTypeNode(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node)
	if links.resolvedType == nil {
		n := node.AsImportTypeNode()
		if !ast.IsLiteralImportTypeNode(node) {
			c.error(n.Argument, diagnostics.String_literal_expected)
			c.symbolNodeLinks.Get(node).resolvedSymbol = c.unknownSymbol
			links.resolvedType = c.errorType
			return links.resolvedType
		}
		targetMeaning := core.IfElse(n.IsTypeOf, ast.SymbolFlagsValue, ast.SymbolFlagsType)
		// TODO: Future work: support unions/generics/whatever via a deferred import-type
		innerModuleSymbol := c.resolveExternalModuleName(node, n.Argument.AsLiteralTypeNode().Literal, false /*ignoreErrors*/)
		if innerModuleSymbol == nil {
			c.symbolNodeLinks.Get(node).resolvedSymbol = c.unknownSymbol
			links.resolvedType = c.errorType
			return links.resolvedType
		}
		moduleSymbol := c.resolveExternalModuleSymbol(innerModuleSymbol, false /*dontResolveAlias*/)
		if !ast.NodeIsMissing(n.Qualifier) {
			nameChain := c.getIdentifierChain(n.Qualifier)
			currentNamespace := moduleSymbol
			for i, current := range nameChain {
				meaning := ast.SymbolFlagsNamespace
				if i == len(nameChain)-1 {
					meaning = targetMeaning
				}
				// typeof a.b.c is normally resolved using `checkExpression` which in turn defers to `checkQualifiedName`
				// That, in turn, ultimately uses `getPropertyOfType` on the type of the symbol, which differs slightly from
				// the `exports` lookup process that only looks up namespace members which is used for most type references
				mergedResolvedSymbol := c.getMergedSymbol(c.resolveSymbol(currentNamespace))
				var symbolFromVariable *ast.Symbol
				var symbolFromModule *ast.Symbol
				if n.IsTypeOf {
					symbolFromVariable = c.getPropertyOfTypeEx(c.getTypeOfSymbol(mergedResolvedSymbol), current.Text(), false /*skipObjectFunctionPropertyAugment*/, true /*includeTypeOnlyMembers*/)
				} else {
					symbolFromModule = c.getSymbol(c.getExportsOfSymbol(mergedResolvedSymbol), current.Text(), meaning)
					if symbolFromModule == nil {
						// a CommonJS module might have typedefs exported alongside an export=
						// !!!
						immediateModuleSymbol := c.resolveExternalModuleSymbol(innerModuleSymbol, true /*dontResolveAlias*/)
						if immediateModuleSymbol != nil && core.Some(immediateModuleSymbol.Declarations, func(d *ast.Node) bool {
							return ast.GetAssignmentDeclarationKind(d) == ast.JSDeclarationKindModuleExports
						}) {
							symbolFromModule = c.getSymbol(c.getExportsOfSymbol(immediateModuleSymbol.Parent), current.Text(), meaning)
						}
					}
				}
				next := core.OrElse(symbolFromModule, symbolFromVariable)
				if next == nil {
					c.error(current, diagnostics.Namespace_0_has_no_exported_member_1, c.getFullyQualifiedName(currentNamespace, nil), scanner.DeclarationNameToString(current))
					links.resolvedType = c.errorType
					return links.resolvedType
				}
				c.symbolNodeLinks.Get(current).resolvedSymbol = next
				c.symbolNodeLinks.Get(current.Parent).resolvedSymbol = next
				currentNamespace = next
			}
			links.resolvedType = c.resolveImportSymbolType(node, currentNamespace, targetMeaning)
		} else {
			if c.getSymbolFlags(moduleSymbol)&targetMeaning != 0 {
				links.resolvedType = c.resolveImportSymbolType(node, moduleSymbol, targetMeaning)
			} else {
				message := core.IfElse(targetMeaning == ast.SymbolFlagsValue,
					diagnostics.Module_0_does_not_refer_to_a_value_but_is_used_as_a_value_here,
					diagnostics.Module_0_does_not_refer_to_a_type_but_is_used_as_a_type_here_Did_you_mean_typeof_import_0)
				c.error(node, message, n.Argument.AsLiteralTypeNode().Literal.Text())
				c.symbolNodeLinks.Get(node).resolvedSymbol = c.unknownSymbol
				links.resolvedType = c.errorType
			}
		}
	}
	return links.resolvedType
}

func (c *Checker) getIdentifierChain(node *ast.Node) []*ast.Node {
	if ast.IsIdentifier(node) {
		return []*ast.Node{node}
	}
	return append(c.getIdentifierChain(node.AsQualifiedName().Left), node.AsQualifiedName().Right)
}

func (c *Checker) resolveImportSymbolType(node *ast.Node, symbol *ast.Symbol, meaning ast.SymbolFlags) *Type {
	resolvedSymbol := c.resolveSymbol(symbol)
	c.symbolNodeLinks.Get(node).resolvedSymbol = resolvedSymbol
	if meaning == ast.SymbolFlagsValue {
		// intentionally doesn't use resolved symbol so type is cached as expected on the alias
		return c.getInstantiationExpressionType(c.getTypeOfSymbol(symbol), node)
	}
	// getTypeReferenceType doesn't handle aliases - it must get the resolved symbol
	return c.getTypeReferenceType(node, resolvedSymbol)
}

func (c *Checker) createTypeFromGenericGlobalType(genericGlobalType *Type, typeArguments []*Type) *Type {
	if genericGlobalType != c.emptyGenericType {
		return c.createTypeReference(genericGlobalType, typeArguments)
	}
	return c.emptyObjectType
}

func (c *Checker) getGlobalStrictFunctionType(name string) *Type {
	if c.strictBindCallApply {
		return c.getGlobalType(name, 0 /*arity*/, true /*reportErrors*/)
	}
	return c.globalFunctionType
}

func (c *Checker) getGlobalImportMetaExpressionType() *Type {
	if c.deferredGlobalImportMetaExpressionType == nil {
		// Create a synthetic type `ImportMetaExpression { meta: MetaProperty }`
		symbol := c.newSymbol(ast.SymbolFlagsNone, "ImportMetaExpression")
		importMetaType := c.getGlobalImportMetaType()
		metaPropertySymbol := c.newSymbolEx(ast.SymbolFlagsProperty, "meta", ast.CheckFlagsReadonly)
		metaPropertySymbol.Parent = symbol
		c.valueSymbolLinks.Get(metaPropertySymbol).resolvedType = importMetaType
		members := createSymbolTable([]*ast.Symbol{metaPropertySymbol})
		symbol.Members = members
		c.deferredGlobalImportMetaExpressionType = c.newAnonymousType(symbol, members, nil, nil, nil)
	}
	return c.deferredGlobalImportMetaExpressionType
}

func (c *Checker) createIterableType(iteratedType *Type) *Type {
	return c.createTypeFromGenericGlobalType(c.getGlobalIterableTypeChecked(), []*Type{iteratedType, c.voidType, c.undefinedType})
}

func (c *Checker) createArrayType(elementType *Type) *Type {
	return c.createArrayTypeEx(elementType, false /*readonly*/)
}

func (c *Checker) createArrayTypeEx(elementType *Type, readonly bool) *Type {
	return c.createTypeFromGenericGlobalType(core.IfElse(readonly, c.globalReadonlyArrayType, c.globalArrayType), []*Type{elementType})
}

func (c *Checker) getTupleElementFlags(node *ast.Node) ElementFlags {
	switch node.Kind {
	case ast.KindOptionalType:
		return ElementFlagsOptional
	case ast.KindRestType:
		return core.IfElse(c.getArrayElementTypeNode(node.Type()) != nil, ElementFlagsRest, ElementFlagsVariadic)
	case ast.KindNamedTupleMember:
		named := node.AsNamedTupleMember()
		switch {
		case named.QuestionToken != nil:
			return ElementFlagsOptional
		case named.DotDotDotToken != nil:
			return core.IfElse(c.getArrayElementTypeNode(named.Type) != nil, ElementFlagsRest, ElementFlagsVariadic)
		}
		return ElementFlagsRequired
	}
	return ElementFlagsRequired
}

func (c *Checker) getTupleElementInfo(node *ast.Node) TupleElementInfo {
	return TupleElementInfo{
		flags:              c.getTupleElementFlags(node),
		labeledDeclaration: core.IfElse(ast.IsNamedTupleMember(node) || ast.IsParameterDeclaration(node), node, nil),
	}
}

func (c *Checker) createTupleType(elementTypes []*Type) *Type {
	elementInfos := core.Map(elementTypes, func(_ *Type) TupleElementInfo { return TupleElementInfo{flags: ElementFlagsRequired} })
	return c.createTupleTypeEx(elementTypes, elementInfos, false /*readonly*/)
}

func (c *Checker) createTupleTypeEx(elementTypes []*Type, elementInfos []TupleElementInfo, readonly bool) *Type {
	tupleTarget := c.getTupleTargetType(elementInfos, readonly)
	switch {
	case tupleTarget == c.emptyGenericType:
		return c.emptyObjectType
	case len(elementTypes) != 0:
		return c.createNormalizedTypeReference(tupleTarget, elementTypes)
	}
	return tupleTarget
}

func (c *Checker) getTupleTargetType(elementInfos []TupleElementInfo, readonly bool) *Type {
	if len(elementInfos) == 1 && elementInfos[0].flags&ElementFlagsRest != 0 {
		// [...X[]] is equivalent to just X[]
		if readonly {
			return c.globalReadonlyArrayType
		}
		return c.globalArrayType
	}
	key := getTupleKey(elementInfos, readonly)
	t := c.tupleTypes[key]
	if t == nil {
		t = c.createTupleTargetType(elementInfos, readonly)
		c.tupleTypes[key] = t
	}
	return t
}

// We represent tuple types as type references to synthesized generic interface types created by
// this function. The types are of the form:
//
//	interface Tuple<T0, T1, T2, ...> extends Array<T0 | T1 | T2 | ...> { 0: T0, 1: T1, 2: T2, ... }
//
// Note that the generic type created by this function has no symbol associated with it. The same
// is true for each of the synthesized type parameters.
func (c *Checker) createTupleTargetType(elementInfos []TupleElementInfo, readonly bool) *Type {
	arity := len(elementInfos)
	minLength := core.CountWhere(elementInfos, func(e TupleElementInfo) bool {
		return e.flags&(ElementFlagsRequired|ElementFlagsVariadic) != 0
	})
	var typeParameters []*Type
	members := make(ast.SymbolTable)
	combinedFlags := ElementFlagsNone
	if arity != 0 {
		typeParameters = make([]*Type, 0, arity)
		for i := range arity {
			typeParameter := c.newTypeParameter(nil)
			typeParameters = append(typeParameters, typeParameter)
			flags := elementInfos[i].flags
			combinedFlags |= flags
			if combinedFlags&ElementFlagsVariable == 0 {
				property := c.newSymbolEx(ast.SymbolFlagsProperty|core.IfElse(flags&ElementFlagsOptional != 0, ast.SymbolFlagsOptional, 0), strconv.Itoa(i), core.IfElse(readonly, ast.CheckFlagsReadonly, 0))
				c.valueSymbolLinks.Get(property).resolvedType = typeParameter
				// c.valueSymbolLinks.get(property).tupleLabelDeclaration = elementInfos[i].labeledDeclaration
				members[property.Name] = property
			}
		}
	}
	fixedLength := len(members)
	lengthSymbol := c.newSymbolEx(ast.SymbolFlagsProperty, "length", core.IfElse(readonly, ast.CheckFlagsReadonly, 0))
	if combinedFlags&ElementFlagsVariable != 0 {
		c.valueSymbolLinks.Get(lengthSymbol).resolvedType = c.numberType
	} else {
		var literalTypes []*Type
		for i := minLength; i <= arity; i++ {
			literalTypes = append(literalTypes, c.getNumberLiteralType(jsnum.Number(i)))
		}
		c.valueSymbolLinks.Get(lengthSymbol).resolvedType = c.getUnionType(literalTypes)
	}
	members[lengthSymbol.Name] = lengthSymbol
	t := c.newObjectType(ObjectFlagsTuple|ObjectFlagsReference, nil)
	d := t.AsTupleType()
	d.thisType = c.newTypeParameter(nil)
	d.thisType.AsTypeParameter().isThisType = true
	d.thisType.AsTypeParameter().constraint = t
	d.allTypeParameters = append(typeParameters, d.thisType)
	d.instantiations = make(map[CacheHashKey]*Type)
	d.instantiations[getTypeListKey(d.TypeParameters())] = t
	d.target = t
	d.resolvedTypeArguments = d.TypeParameters()
	d.declaredMembersResolved = true
	d.declaredMembers = members
	d.elementInfos = elementInfos
	d.minLength = minLength
	d.fixedLength = fixedLength
	d.combinedFlags = combinedFlags
	d.readonly = readonly
	return t
}

func (c *Checker) getElementTypeOfSliceOfTupleType(t *Type, index int, endSkipCount int, writing bool, noReductions bool) *Type {
	length := c.getTypeReferenceArity(t) - endSkipCount
	elementInfos := t.TargetTupleType().elementInfos
	if index < length {
		typeArguments := c.getTypeArguments(t)
		var elementTypes []*Type
		for i := index; i < length; i++ {
			e := typeArguments[i]
			if elementInfos[i].flags&ElementFlagsVariadic != 0 {
				e = c.getIndexedAccessType(e, c.numberType)
			}
			elementTypes = append(elementTypes, e)
		}
		if writing {
			return c.getIntersectionType(elementTypes)
		}
		return c.getUnionTypeEx(elementTypes, core.IfElse(noReductions, UnionReductionNone, UnionReductionLiteral), nil, nil)
	}
	return nil
}

func (c *Checker) getRestTypeOfTupleType(t *Type) *Type {
	return c.getElementTypeOfSliceOfTupleType(t, t.TargetTupleType().fixedLength, 0, false, false)
}

func (c *Checker) getTupleElementTypeOutOfStartCount(t *Type, index jsnum.Number, undefinedLikeType *Type) *Type {
	return c.mapType(t, func(t *Type) *Type {
		restType := c.getRestTypeOfTupleType(t)
		if restType == nil {
			return c.undefinedType
		}
		if undefinedLikeType != nil && index >= jsnum.Number(getTotalFixedElementCount(t.TargetTupleType())) {
			return c.getUnionType([]*Type{restType, undefinedLikeType})
		}
		return restType
	})
}

func (c *Checker) isGenericType(t *Type) bool {
	return c.getGenericObjectFlags(t) != 0
}

func (c *Checker) isGenericObjectType(t *Type) bool {
	return c.getGenericObjectFlags(t)&ObjectFlagsIsGenericObjectType != 0
}

func (c *Checker) isGenericIndexType(t *Type) bool {
	return c.getGenericObjectFlags(t)&ObjectFlagsIsGenericIndexType != 0
}

func (c *Checker) getGenericObjectFlags(t *Type) ObjectFlags {
	var combinedFlags ObjectFlags
	if t.flags&(TypeFlagsUnionOrIntersection|TypeFlagsSubstitution) != 0 {
		if t.objectFlags&ObjectFlagsIsGenericTypeComputed == 0 {
			if t.flags&TypeFlagsUnionOrIntersection != 0 {
				for _, u := range t.Types() {
					combinedFlags |= c.getGenericObjectFlags(u)
				}
			} else {
				combinedFlags = c.getGenericObjectFlags(t.AsSubstitutionType().baseType) | c.getGenericObjectFlags(t.AsSubstitutionType().constraint)
			}
			t.objectFlags |= ObjectFlagsIsGenericTypeComputed | combinedFlags
		}
		return t.objectFlags & ObjectFlagsIsGenericType
	}
	if t.flags&TypeFlagsInstantiableNonPrimitive != 0 || c.isGenericMappedType(t) || c.isGenericTupleType(t) {
		combinedFlags |= ObjectFlagsIsGenericObjectType
	}
	if t.flags&(TypeFlagsInstantiableNonPrimitive|TypeFlagsIndex) != 0 || c.isGenericStringLikeType(t) {
		combinedFlags |= ObjectFlagsIsGenericIndexType
	}
	return combinedFlags
}

func (c *Checker) isGenericTupleType(t *Type) bool {
	return isTupleType(t) && t.TargetTupleType().combinedFlags&ElementFlagsVariadic != 0
}

func (c *Checker) isGenericMappedType(t *Type) bool {
	if t.objectFlags&ObjectFlagsMapped != 0 {
		constraint := c.getConstraintTypeFromMappedType(t)
		if c.isGenericIndexType(constraint) {
			return true
		}
		// A mapped type is generic if the 'as' clause references generic types other than the iteration type.
		// To determine this, we substitute the constraint type (that we now know isn't generic) for the iteration
		// type and check whether the resulting type is generic.
		nameType := c.getNameTypeFromMappedType(t)
		if nameType != nil && c.isGenericIndexType(c.instantiateType(nameType, newSimpleTypeMapper(c.getTypeParameterFromMappedType(t), constraint))) {
			return true
		}
	}
	return false
}

/**
 * A union type which is reducible upon instantiation (meaning some members are removed under certain instantiations)
 * must be kept generic, as that instantiation information needs to flow through the type system. By replacing all
 * type parameters in the union with a special never type that is treated as a literal in `getReducedType`, we can cause
 * the `getReducedType` logic to reduce the resulting type if possible (since only intersections with conflicting
 * literal-typed properties are reducible).
 */
func (c *Checker) isGenericReducibleType(t *Type) bool {
	return t.flags&TypeFlagsUnion != 0 && t.objectFlags&ObjectFlagsContainsIntersections != 0 && core.Some(t.Types(), c.isGenericReducibleType) ||
		t.flags&TypeFlagsIntersection != 0 && c.isReducibleIntersection(t)
}

func (c *Checker) isReducibleIntersection(t *Type) bool {
	d := t.AsIntersectionType()
	if d.uniqueLiteralFilledInstantiation == nil {
		d.uniqueLiteralFilledInstantiation = c.instantiateType(t, c.uniqueLiteralMapper)
	}
	return c.getReducedType(d.uniqueLiteralFilledInstantiation) != d.uniqueLiteralFilledInstantiation
}

func (c *Checker) getUniqueLiteralTypeForTypeParameter(t *Type) *Type {
	if t.flags&TypeFlagsTypeParameter != 0 {
		return c.uniqueLiteralType
	}
	return t
}

func (c *Checker) getConditionalFlowTypeOfType(t *Type, node *ast.Node) *Type {
	var constraints []*Type
	covariant := true
	for node != nil && !ast.IsStatement(node) && node.Kind != ast.KindJSDoc {
		parent := node.Parent
		// only consider variance flipped by parameter locations - `keyof` types would usually be considered variance inverting, but
		// often get used in indexed accesses where they behave sortof invariantly, but our checking is lax
		if ast.IsParameterDeclaration(parent) {
			covariant = !covariant
		}
		// Always substitute on type parameters, regardless of variance, since even
		// in contravariant positions, they may rely on substituted constraints to be valid
		if (covariant || t.flags&TypeFlagsTypeVariable != 0) && ast.IsConditionalTypeNode(parent) && node == parent.AsConditionalTypeNode().TrueType {
			constraint := c.getImpliedConstraint(t, parent.AsConditionalTypeNode().CheckType, parent.AsConditionalTypeNode().ExtendsType)
			if constraint != nil {
				constraints = append(constraints, constraint)
			}
		} else if t.flags&TypeFlagsTypeParameter != 0 && ast.IsMappedTypeNode(parent) && parent.AsMappedTypeNode().NameType == nil && node == parent.Type() {
			mappedType := c.getTypeFromTypeNode(parent)
			if c.getTypeParameterFromMappedType(mappedType) == c.getActualTypeVariable(t) {
				typeParameter := c.getHomomorphicTypeVariable(mappedType)
				if typeParameter != nil {
					constraint := c.getConstraintOfTypeParameter(typeParameter)
					if constraint != nil && everyType(constraint, c.isArrayOrTupleType) {
						constraints = append(constraints, c.getUnionType([]*Type{c.numberType, c.numericStringType}))
					}
				}
			}
		}
		node = parent
	}
	if len(constraints) != 0 {
		return c.getSubstitutionType(t, c.getIntersectionType(constraints))
	}
	return t
}

func (c *Checker) getImpliedConstraint(t *Type, checkNode *ast.Node, extendsNode *ast.Node) *Type {
	switch {
	case isUnaryTupleTypeNode(checkNode) && isUnaryTupleTypeNode(extendsNode):
		return c.getImpliedConstraint(t, checkNode.Elements()[0], extendsNode.Elements()[0])
	case c.getActualTypeVariable(c.getTypeFromTypeNode(checkNode)) == c.getActualTypeVariable(t):
		return c.getTypeFromTypeNode(extendsNode)
	}
	return nil
}

func isUnaryTupleTypeNode(node *ast.Node) bool {
	return ast.IsTupleTypeNode(node) && len(node.Elements()) == 1
}

func (c *Checker) newType(flags TypeFlags, objectFlags ObjectFlags, data TypeData) *Type {
	c.TypeCount++
	t := data.AsType()
	t.flags = flags
	t.objectFlags = objectFlags &^ (ObjectFlagsCouldContainTypeVariablesComputed | ObjectFlagsCouldContainTypeVariables | ObjectFlagsMembersResolved)
	t.id = TypeId(c.TypeCount)
	t.checker = c
	t.data = data
	if c.tracer != nil {
		c.tracer.RecordType(t)
	}
	return t
}

func (c *Checker) newIntrinsicType(flags TypeFlags, intrinsicName string) *Type {
	return c.newIntrinsicTypeEx(flags, intrinsicName, ObjectFlagsNone)
}

func (c *Checker) newIntrinsicTypeEx(flags TypeFlags, intrinsicName string, objectFlags ObjectFlags) *Type {
	data := &IntrinsicType{}
	data.intrinsicName = intrinsicName
	return c.newType(flags, objectFlags, data)
}

func (c *Checker) createWideningType(nonWideningType *Type) *Type {
	if c.strictNullChecks {
		return nonWideningType
	}
	t := c.newIntrinsicType(nonWideningType.flags, nonWideningType.AsIntrinsicType().intrinsicName)
	t.objectFlags |= ObjectFlagsContainsWideningType
	return t
}

func (c *Checker) createUnknownUnionType() *Type {
	if c.strictNullChecks {
		return c.getUnionType([]*Type{c.undefinedType, c.nullType, c.unknownEmptyObjectType})
	}
	return c.unknownType
}

func (c *Checker) newLiteralType(flags TypeFlags, value any, regularType *Type) *Type {
	data := &LiteralType{}
	data.value = value
	t := c.newType(flags, ObjectFlagsNone, data)
	if regularType != nil {
		data.regularType = regularType
	} else {
		data.regularType = t
	}
	return t
}

func (c *Checker) newUniqueESSymbolType(symbol *ast.Symbol, name string) *Type {
	data := &UniqueESSymbolType{}
	data.name = name
	t := c.newType(TypeFlagsUniqueESSymbol, ObjectFlagsNone, data)
	t.symbol = symbol
	return t
}

func (c *Checker) newObjectType(objectFlags ObjectFlags, symbol *ast.Symbol) *Type {
	var data TypeData
	switch {
	case objectFlags&ObjectFlagsClassOrInterface != 0:
		data = &InterfaceType{}
	case objectFlags&ObjectFlagsTuple != 0:
		data = &TupleType{}
	case objectFlags&ObjectFlagsReference != 0:
		data = &TypeReference{}
	case objectFlags&ObjectFlagsMapped != 0:
		data = &MappedType{}
	case objectFlags&ObjectFlagsReverseMapped != 0:
		data = &ReverseMappedType{}
	case objectFlags&ObjectFlagsEvolvingArray != 0:
		data = &EvolvingArrayType{}
	case objectFlags&ObjectFlagsInstantiationExpressionType != 0:
		data = &InstantiationExpressionType{}
	case objectFlags&ObjectFlagsAnonymous != 0:
		data = &ObjectType{}
	default:
		panic("Unhandled case in newObjectType")
	}
	t := c.newType(TypeFlagsObject, objectFlags, data)
	t.symbol = symbol
	return t
}

func (c *Checker) newAnonymousType(symbol *ast.Symbol, members ast.SymbolTable, callSignatures []*Signature, constructSignatures []*Signature, indexInfos []*IndexInfo) *Type {
	t := c.newObjectType(ObjectFlagsAnonymous, symbol)
	c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos)
	return t
}

func (c *Checker) tryCreateTypeReference(target *Type, typeArguments []*Type) *Type {
	if len(typeArguments) != 0 && target == c.emptyGenericType {
		return c.unknownType
	}
	return c.createTypeReference(target, typeArguments)
}

func (c *Checker) createTypeReference(target *Type, typeArguments []*Type) *Type {
	return c.createTypeReferenceEx(target, typeArguments, ObjectFlagsNone)
}

func (c *Checker) createTypeReferenceEx(target *Type, typeArguments []*Type, objectFlags ObjectFlags) *Type {
	id := getTypeListKey(typeArguments)
	intf := target.AsInterfaceType()
	if t, ok := intf.instantiations[id]; ok {
		return t
	}
	t := c.newObjectType(ObjectFlagsReference|objectFlags|c.getPropagatingFlagsOfTypes(typeArguments, TypeFlagsNone), target.symbol)
	d := t.AsTypeReference()
	d.target = target
	d.resolvedTypeArguments = typeArguments
	intf.instantiations[id] = t
	return t
}

func (c *Checker) createDeferredTypeReference(target *Type, node *ast.Node, mapper *TypeMapper, alias *TypeAlias) *Type {
	if alias == nil {
		alias = c.getAliasForTypeNode(node)
		if alias != nil && mapper != nil {
			alias.typeArguments = c.instantiateTypes(alias.typeArguments, mapper)
		}
	}
	t := c.newObjectType(ObjectFlagsReference, target.symbol)
	t.alias = alias
	d := t.AsTypeReference()
	d.target = target
	d.mapper = mapper
	d.node = node
	return t
}

func (c *Checker) cloneTypeReference(source *Type) *Type {
	t := c.newObjectType(ObjectFlagsReference, source.symbol)
	t.objectFlags = source.objectFlags &^ ObjectFlagsMembersResolved
	t.AsTypeReference().target = source.AsTypeReference().target
	t.AsTypeReference().resolvedTypeArguments = source.AsTypeReference().resolvedTypeArguments
	return t
}

func (c *Checker) setStructuredTypeMembers(t *Type, members ast.SymbolTable, callSignatures []*Signature, constructSignatures []*Signature, indexInfos []*IndexInfo) {
	t.objectFlags |= ObjectFlagsMembersResolved
	data := t.AsStructuredType()
	data.members = members
	data.properties = c.getNamedMembers(members, t.symbol)
	if len(callSignatures) != 0 {
		if len(constructSignatures) != 0 {
			data.signatures = core.Concatenate(callSignatures, constructSignatures)
		} else {
			data.signatures = slices.Clip(callSignatures)
		}
		data.callSignatureCount = len(callSignatures)
	} else {
		if len(constructSignatures) != 0 {
			data.signatures = slices.Clip(constructSignatures)
		} else {
			data.signatures = nil
		}
		data.callSignatureCount = 0
	}
	data.indexInfos = slices.Clip(indexInfos)
}

func (c *Checker) newTypeParameter(symbol *ast.Symbol) *Type {
	t := c.newType(TypeFlagsTypeParameter, ObjectFlagsNone, &TypeParameter{})
	t.symbol = symbol
	return t
}

// This function is used to propagate certain flags when creating new object type references and union types.
// It is only necessary to do so if a constituent type might be the undefined type, the null type, the type
// of an object literal or a non-inferrable type. This is because there are operations in the type checker
// that care about the presence of such types at arbitrary depth in a containing type.
func (c *Checker) getPropagatingFlagsOfTypes(types []*Type, excludeKinds TypeFlags) ObjectFlags {
	result := ObjectFlagsNone
	for _, t := range types {
		if t.flags&excludeKinds == 0 {
			result |= t.objectFlags
		}
	}
	return result & ObjectFlagsPropagatingFlags
}

func (c *Checker) newUnionType(objectFlags ObjectFlags, types []*Type) *Type {
	data := &UnionType{}
	data.types = types
	return c.newType(TypeFlagsUnion, objectFlags, data)
}

func (c *Checker) newIntersectionType(objectFlags ObjectFlags, types []*Type) *Type {
	data := &IntersectionType{}
	data.types = types
	return c.newType(TypeFlagsIntersection, objectFlags, data)
}

func (c *Checker) newIndexedAccessType(objectType *Type, indexType *Type, accessFlags AccessFlags) *Type {
	data := &IndexedAccessType{}
	data.objectType = objectType
	data.indexType = indexType
	data.accessFlags = accessFlags
	return c.newType(TypeFlagsIndexedAccess, ObjectFlagsNone, data)
}

func (c *Checker) newIndexType(target *Type, indexFlags IndexFlags) *Type {
	data := &IndexType{}
	data.target = target
	data.indexFlags = indexFlags
	return c.newType(TypeFlagsIndex, ObjectFlagsNone, data)
}

func (c *Checker) newTemplateLiteralType(texts []string, types []*Type) *Type {
	data := &TemplateLiteralType{}
	data.texts = texts
	data.types = types
	return c.newType(TypeFlagsTemplateLiteral, ObjectFlagsNone, data)
}

func (c *Checker) newStringMappingType(symbol *ast.Symbol, target *Type) *Type {
	data := &StringMappingType{}
	data.target = target
	t := c.newType(TypeFlagsStringMapping, ObjectFlagsNone, data)
	t.symbol = symbol
	return t
}

func (c *Checker) newConditionalType(root *ConditionalRoot, mapper *TypeMapper, combinedMapper *TypeMapper) *Type {
	data := &ConditionalType{}
	data.root = root
	data.checkType = c.instantiateType(root.checkType, mapper)
	data.extendsType = c.instantiateType(root.extendsType, mapper)
	data.mapper = mapper
	data.combinedMapper = combinedMapper
	return c.newType(TypeFlagsConditional, ObjectFlagsNone, data)
}

func (c *Checker) newSubstitutionType(baseType *Type, constraint *Type) *Type {
	data := &SubstitutionType{}
	data.baseType = baseType
	data.constraint = constraint
	return c.newType(TypeFlagsSubstitution, ObjectFlagsNone, data)
}

func (c *Checker) newSignature(flags SignatureFlags, declaration *ast.Node, typeParameters []*Type, thisParameter *ast.Symbol, parameters []*ast.Symbol, resolvedReturnType *Type, resolvedTypePredicate *TypePredicate, minArgumentCount int) *Signature {
	c.SignatureCount++
	sig := c.signatureArena.New()
	sig.id = SignatureId(c.SignatureCount)
	sig.flags = flags
	sig.declaration = declaration
	sig.typeParameters = typeParameters
	sig.parameters = parameters
	sig.thisParameter = thisParameter
	sig.resolvedReturnType = resolvedReturnType
	sig.resolvedTypePredicate = resolvedTypePredicate
	sig.minArgumentCount = int32(minArgumentCount)
	sig.resolvedMinArgumentCount = -1
	return sig
}

func (c *Checker) newIndexInfo(keyType *Type, valueType *Type, isReadonly bool, declaration *ast.Node, components []*ast.Node) *IndexInfo {
	info := c.indexInfoArena.New()
	info.keyType = keyType
	info.valueType = valueType
	info.isReadonly = isReadonly
	info.declaration = declaration
	info.components = components
	return info
}

func (c *Checker) getRegularTypeOfLiteralType(t *Type) *Type {
	if t.flags&TypeFlagsFreshable != 0 {
		return t.AsLiteralType().regularType
	}
	if t.flags&TypeFlagsUnion != 0 {
		u := t.AsUnionType()
		if u.regularType == nil {
			u.regularType = c.mapType(t, c.getRegularTypeOfLiteralType)
		}
		return u.regularType
	}
	return t
}

func (c *Checker) getFreshTypeOfLiteralType(t *Type) *Type {
	if t.flags&TypeFlagsFreshable != 0 {
		d := t.AsLiteralType()
		if d.freshType == nil {
			f := c.newLiteralType(t.flags, d.value, t)
			f.symbol = t.symbol
			f.AsLiteralType().freshType = f
			d.freshType = f
		}
		return d.freshType
	}
	return t
}

func isFreshLiteralType(t *Type) bool {
	return t.flags&TypeFlagsFreshable != 0 && t.AsLiteralType().freshType == t
}

func (c *Checker) getStringLiteralType(value string) *Type {
	t := c.stringLiteralTypes[value]
	if t == nil {
		t = c.newLiteralType(TypeFlagsStringLiteral, value, nil)
		c.stringLiteralTypes[value] = t
	}
	return t
}

func (c *Checker) getNumberLiteralType(value jsnum.Number) *Type {
	// NaN cannot be used as a Go map key because NaN != NaN in IEEE 754,
	// so Go map lookups for NaN always miss. Cache NaN type separately.
	if value.IsNaN() {
		if c.nanType == nil {
			c.nanType = c.newLiteralType(TypeFlagsNumberLiteral, value, nil)
		}
		return c.nanType
	}
	t := c.numberLiteralTypes[value]
	if t == nil {
		t = c.newLiteralType(TypeFlagsNumberLiteral, value, nil)
		c.numberLiteralTypes[value] = t
	}
	return t
}

func (c *Checker) getBigIntLiteralType(value jsnum.PseudoBigInt) *Type {
	t := c.bigintLiteralTypes[value]
	if t == nil {
		t = c.newLiteralType(TypeFlagsBigIntLiteral, value, nil)
		c.bigintLiteralTypes[value] = t
	}
	return t
}

// text is a valid bigint string excluding a trailing `n`, but including a possible prefix `-`.
// Use `isValidBigIntString(text, roundTripOnly)` before calling this function.
func (c *Checker) parseBigIntLiteralType(text string) *Type {
	return c.getBigIntLiteralType(jsnum.ParseValidBigInt(text))
}

func getStringLiteralValue(t *Type) string {
	return t.AsLiteralType().value.(string)
}

func getNumberLiteralValue(t *Type) jsnum.Number {
	return t.AsLiteralType().value.(jsnum.Number)
}

func getBigIntLiteralValue(t *Type) jsnum.PseudoBigInt {
	return t.AsLiteralType().value.(jsnum.PseudoBigInt)
}

func getBooleanLiteralValue(t *Type) bool {
	return t.AsLiteralType().value.(bool)
}

func (c *Checker) getEnumLiteralType(value any, enumSymbol *ast.Symbol, symbol *ast.Symbol) *Type {
	var flags TypeFlags
	switch v := value.(type) {
	case string:
		flags = TypeFlagsEnumLiteral | TypeFlagsStringLiteral
	case jsnum.Number:
		flags = TypeFlagsEnumLiteral | TypeFlagsNumberLiteral
		// NaN cannot be used as a Go map key because NaN != NaN in IEEE 754,
		// so Go map lookups for NaN always miss. Cache NaN enum types separately by enum symbol.
		if v.IsNaN() {
			t := c.enumNaNLiteralTypes[enumSymbol]
			if t == nil {
				t = c.newLiteralType(flags, value, nil)
				t.symbol = symbol
				c.enumNaNLiteralTypes[enumSymbol] = t
			}
			return t
		}
	default:
		panic("Unhandled case in getEnumLiteralType")
	}
	key := EnumLiteralKey{enumSymbol: enumSymbol, value: value}
	t := c.enumLiteralTypes[key]
	if t == nil {
		t = c.newLiteralType(flags, value, nil)
		t.symbol = symbol
		c.enumLiteralTypes[key] = t
	}
	return t
}

func isLiteralType(t *Type) bool {
	if t.flags&TypeFlagsBoolean != 0 {
		return true
	}
	if t.flags&TypeFlagsUnion != 0 {
		if t.flags&TypeFlagsEnumLiteral != 0 {
			return true
		}
		return core.Every(t.Types(), isUnitType)
	}
	return isUnitType(t)
}

func isNeitherUnitTypeNorNever(t *Type) bool {
	return t.flags&(TypeFlagsUnit|TypeFlagsNever) == 0
}

func isUnitType(t *Type) bool {
	return t.flags&TypeFlagsUnit != 0
}

func (c *Checker) isUnitLikeType(t *Type) bool {
	// Intersections that reduce to 'never' (e.g. 'T & null' where 'T extends {}') are not unit types.
	t = c.getBaseConstraintOrType(t)
	// Scan intersections such that tagged literal types are considered unit types.
	if t.flags&TypeFlagsIntersection != 0 {
		return core.Some(t.AsIntersectionType().types, isUnitType)
	}
	return isUnitType(t)
}

func (c *Checker) extractUnitType(t *Type) *Type {
	if t.flags&TypeFlagsIntersection != 0 {
		u := core.Find(t.AsIntersectionType().types, isUnitType)
		if u != nil {
			return u
		}
	}
	return t
}

func (c *Checker) getBaseTypeOfLiteralType(t *Type) *Type {
	switch {
	case t.flags&TypeFlagsEnumLike != 0:
		return c.getBaseTypeOfEnumLikeType(t)
	case t.flags&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0:
		return c.stringType
	case t.flags&TypeFlagsNumberLiteral != 0:
		return c.numberType
	case t.flags&TypeFlagsBigIntLiteral != 0:
		return c.bigintType
	case t.flags&TypeFlagsBooleanLiteral != 0:
		return c.booleanType
	case t.flags&TypeFlagsUnion != 0:
		return c.getBaseTypeOfLiteralTypeUnion(t)
	}
	return t
}

// This like getBaseTypeOfLiteralType, but instead treats enum literals as strings/numbers instead
// of returning their enum base type (which depends on the types of other literals in the enum).
func (c *Checker) getBaseTypeOfLiteralTypeForComparison(t *Type) *Type {
	switch {
	case t.flags&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0:
		return c.stringType
	case t.flags&(TypeFlagsNumberLiteral|TypeFlagsEnum) != 0:
		return c.numberType
	case t.flags&TypeFlagsBigIntLiteral != 0:
		return c.bigintType
	case t.flags&TypeFlagsBooleanLiteral != 0:
		return c.booleanType
	case t.flags&TypeFlagsUnion != 0:
		return c.mapType(t, c.getBaseTypeOfLiteralTypeForComparison)
	}
	return t
}

func (c *Checker) getBaseTypeOfEnumLikeType(t *Type) *Type {
	if t.flags&TypeFlagsEnumLike != 0 && t.symbol.Flags&ast.SymbolFlagsEnumMember != 0 {
		return c.getDeclaredTypeOfSymbol(c.getParentOfSymbol(t.symbol))
	}
	return t
}

func (c *Checker) getBaseTypeOfLiteralTypeUnion(t *Type) *Type {
	key := CachedTypeKey{kind: CachedTypeKindLiteralUnionBaseType, typeId: t.id}
	if cached, ok := c.cachedTypes[key]; ok {
		return cached
	}
	result := c.mapType(t, c.getBaseTypeOfLiteralType)
	c.cachedTypes[key] = result
	return result
}

func (c *Checker) getWidenedLiteralType(t *Type) *Type {
	switch {
	case t.flags&TypeFlagsEnumLike != 0 && isFreshLiteralType(t):
		return c.getBaseTypeOfEnumLikeType(t)
	case t.flags&TypeFlagsStringLiteral != 0 && isFreshLiteralType(t):
		return c.stringType
	case t.flags&TypeFlagsNumberLiteral != 0 && isFreshLiteralType(t):
		return c.numberType
	case t.flags&TypeFlagsBigIntLiteral != 0 && isFreshLiteralType(t):
		return c.bigintType
	case t.flags&TypeFlagsBooleanLiteral != 0 && isFreshLiteralType(t):
		return c.booleanType
	case t.flags&TypeFlagsUnion != 0:
		return c.mapType(t, c.getWidenedLiteralType)
	}
	return t
}

func (c *Checker) getWidenedUniqueESSymbolType(t *Type) *Type {
	switch {
	case t.flags&TypeFlagsUniqueESSymbol != 0:
		return c.esSymbolType
	case t.flags&TypeFlagsUnion != 0:
		return c.mapType(t, c.getWidenedUniqueESSymbolType)
	}
	return t
}

func (c *Checker) getWidenedLiteralLikeTypeForContextualType(t *Type, contextualType *Type) *Type {
	if !c.isLiteralOfContextualType(t, contextualType) {
		t = c.getWidenedUniqueESSymbolType(c.getWidenedLiteralType(t))
	}
	return c.getRegularTypeOfLiteralType(t)
}

func (c *Checker) isLiteralOfContextualType(candidateType *Type, contextualType *Type) bool {
	if contextualType != nil {
		if contextualType.flags&TypeFlagsUnionOrIntersection != 0 {
			return core.Some(contextualType.Types(), func(t *Type) bool {
				return c.isLiteralOfContextualType(candidateType, t)
			})
		}
		if contextualType.flags&TypeFlagsInstantiableNonPrimitive != 0 {
			// If the contextual type is a type variable constrained to a primitive type, consider
			// this a literal context for literals of that primitive type. For example, given a
			// type parameter 'T extends string', infer string literal types for T.
			constraint := c.getBaseConstraintOfType(contextualType)
			if constraint == nil {
				constraint = c.unknownType
			}
			return c.maybeTypeOfKind(constraint, TypeFlagsString) && c.maybeTypeOfKind(candidateType, TypeFlagsStringLiteral) ||
				c.maybeTypeOfKind(constraint, TypeFlagsNumber) && c.maybeTypeOfKind(candidateType, TypeFlagsNumberLiteral) ||
				c.maybeTypeOfKind(constraint, TypeFlagsBigInt) && c.maybeTypeOfKind(candidateType, TypeFlagsBigIntLiteral) ||
				c.maybeTypeOfKind(constraint, TypeFlagsESSymbol) && c.maybeTypeOfKind(candidateType, TypeFlagsUniqueESSymbol) ||
				c.isLiteralOfContextualType(candidateType, constraint)
		}
		// If the contextual type is a literal of a particular primitive type, we consider this a
		// literal context for all literals of that primitive type.
		return contextualType.flags&(TypeFlagsStringLiteral|TypeFlagsIndex|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 && c.maybeTypeOfKind(candidateType, TypeFlagsStringLiteral) ||
			contextualType.flags&TypeFlagsNumberLiteral != 0 && c.maybeTypeOfKind(candidateType, TypeFlagsNumberLiteral) ||
			contextualType.flags&TypeFlagsBigIntLiteral != 0 && c.maybeTypeOfKind(candidateType, TypeFlagsBigIntLiteral) ||
			contextualType.flags&TypeFlagsBooleanLiteral != 0 && c.maybeTypeOfKind(candidateType, TypeFlagsBooleanLiteral) ||
			contextualType.flags&TypeFlagsUniqueESSymbol != 0 && c.maybeTypeOfKind(candidateType, TypeFlagsUniqueESSymbol)
	}
	return false
}

func (c *Checker) mapTypeWithAlias(t *Type, f func(t *Type) *Type, alias *TypeAlias) *Type {
	if t.flags&TypeFlagsUnion != 0 && alias != nil {
		return c.getUnionTypeEx(core.Map(t.Types(), f), UnionReductionLiteral, alias, nil)
	}
	return c.mapType(t, f)
}

func (c *Checker) mapType(t *Type, f func(*Type) *Type) *Type {
	return c.mapTypeEx(t, f, false /*noReductions*/)
}

func (c *Checker) mapTypeEx(t *Type, f func(*Type) *Type, noReductions bool) *Type {
	if t.flags&TypeFlagsNever != 0 {
		return t
	}
	if t.flags&TypeFlagsUnion == 0 {
		return f(t)
	}
	u := t.AsUnionType()
	types := u.types
	if u.origin != nil && u.origin.flags&TypeFlagsUnion != 0 {
		types = u.origin.Types()
	}
	mappedTypes := make([]*Type, 0, 16)
	var changed bool
	for _, s := range types {
		var mapped *Type
		if s.flags&TypeFlagsUnion != 0 {
			mapped = c.mapTypeEx(s, f, noReductions)
		} else {
			mapped = f(s)
		}
		if mapped != s {
			changed = true
		}
		if mapped != nil {
			mappedTypes = append(mappedTypes, mapped)
		}
	}
	if changed {
		if len(mappedTypes) == 0 {
			return nil
		}
		return c.getUnionTypeEx(slices.Clone(mappedTypes), core.IfElse(noReductions, UnionReductionNone, UnionReductionLiteral), nil /*alias*/, nil /*origin*/)
	}
	return t
}

type UnionReduction int32

const (
	UnionReductionNone UnionReduction = iota
	UnionReductionLiteral
	UnionReductionSubtype
)

func (c *Checker) getUnionOrIntersectionType(types []*Type, isUnion bool, unionReduction UnionReduction) *Type {
	if isUnion {
		return c.getUnionTypeEx(types, unionReduction, nil, nil)
	}
	return c.getIntersectionType(types)
}

func (c *Checker) getUnionType(types []*Type) *Type {
	return c.getUnionTypeEx(types, UnionReductionLiteral, nil /*alias*/, nil /*origin*/)
}

// We sort and deduplicate the constituent types based on object identity. If the subtypeReduction
// flag is specified we also reduce the constituent type set to only include types that aren't subtypes
// of other types. Subtype reduction is expensive for large union types and is possible only when union
// types are known not to circularly reference themselves (as is the case with union types created by
// expression constructs such as array literals and the || and ?: operators). Named types can
// circularly reference themselves and therefore cannot be subtype reduced during their declaration.
// For example, "type Item = string | (() => Item" is a named type that circularly references itself.
func (c *Checker) getUnionTypeEx(types []*Type, unionReduction UnionReduction, alias *TypeAlias, origin *Type) *Type {
	if len(types) == 0 {
		return c.neverType
	}
	if len(types) == 1 {
		return types[0]
	}
	// We optimize for the common case of unioning a union type with some other type (such as `undefined`).
	if len(types) == 2 && origin == nil && (types[0].flags&TypeFlagsUnion != 0 || types[1].flags&TypeFlagsUnion != 0) {
		id1 := types[0].id
		id2 := types[1].id
		if id1 > id2 {
			id1, id2 = id2, id1
		}
		key := UnionOfUnionKey{id1: id1, id2: id2, r: unionReduction, a: getAliasKey(alias)}
		t := c.unionOfUnionTypes[key]
		if t == nil {
			t = c.getUnionTypeWorker(types, unionReduction, alias, nil /*origin*/)
			c.unionOfUnionTypes[key] = t
		}
		return t
	}
	return c.getUnionTypeWorker(types, unionReduction, alias, origin)
}

func (c *Checker) getUnionTypeWorker(types []*Type, unionReduction UnionReduction, alias *TypeAlias, origin *Type) *Type {
	typeSet, includes := c.addTypesToUnion(make([]*Type, 0, len(types)), 0, types)
	if unionReduction != UnionReductionNone {
		if includes&TypeFlagsAnyOrUnknown != 0 {
			if includes&TypeFlagsAny != 0 {
				switch {
				case includes&TypeFlagsIncludesWildcard != 0:
					return c.wildcardType
				case includes&TypeFlagsIncludesError != 0:
					return c.errorType
				}
				return c.anyType
			}
			return c.unknownType
		}
		if includes&TypeFlagsUndefined != 0 {
			// If type set contains both undefinedType and missingType, remove missingType
			if len(typeSet) >= 2 && typeSet[0] == c.undefinedType && typeSet[1] == c.missingType {
				typeSet = slices.Delete(typeSet, 1, 2)
			}
		}
		if includes&(TypeFlagsEnum|TypeFlagsLiteral|TypeFlagsUniqueESSymbol|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 ||
			includes&TypeFlagsVoid != 0 && includes&TypeFlagsUndefined != 0 {
			typeSet = c.removeRedundantLiteralTypes(typeSet, includes, unionReduction&UnionReductionSubtype != 0)
		}
		if includes&TypeFlagsStringLiteral != 0 && includes&(TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 {
			typeSet = c.removeStringLiteralsMatchedByTemplateLiterals(typeSet)
		}
		if includes&TypeFlagsIncludesConstrainedTypeVariable != 0 {
			typeSet = c.removeConstrainedTypeVariables(typeSet)
		}
		if unionReduction == UnionReductionSubtype {
			typeSet = c.removeSubtypes(typeSet, includes&TypeFlagsObject != 0)
			if typeSet == nil {
				return c.errorType
			}
		}
		if len(typeSet) == 0 {
			switch {
			case includes&TypeFlagsNull != 0:
				if includes&TypeFlagsIncludesNonWideningType != 0 {
					return c.nullType
				}
				return c.nullWideningType
			case includes&TypeFlagsUndefined != 0:
				if includes&TypeFlagsIncludesNonWideningType != 0 {
					return c.undefinedType
				}
				return c.undefinedWideningType
			}
			return c.neverType
		}
	}
	if origin == nil && includes&TypeFlagsUnion != 0 {
		namedUnions := c.addNamedUnions(nil, types)
		var reducedTypes []*Type
		for _, t := range typeSet {
			if !core.Some(namedUnions, func(u *Type) bool { return containsType(u.Types(), t) }) {
				reducedTypes = append(reducedTypes, t)
			}
		}
		if alias == nil && len(namedUnions) == 1 && len(reducedTypes) == 0 {
			return namedUnions[0]
		}
		// We create a denormalized origin type only when the union was created from one or more named unions
		// (unions with alias symbols or origins) and when there is no overlap between those named unions.
		namedTypesCount := 0
		for _, u := range namedUnions {
			namedTypesCount += len(u.Types())
		}
		if namedTypesCount+len(reducedTypes) == len(typeSet) {
			for _, t := range namedUnions {
				reducedTypes, _ = insertType(reducedTypes, t)
			}
			origin = c.newUnionType(ObjectFlagsNone, reducedTypes)
		}
	}
	objectFlags := core.IfElse(includes&TypeFlagsNotPrimitiveUnion != 0, ObjectFlagsNone, ObjectFlagsPrimitiveUnion) |
		core.IfElse(includes&TypeFlagsIntersection != 0, ObjectFlagsContainsIntersections, ObjectFlagsNone)
	return c.getUnionTypeFromSortedList(typeSet, objectFlags, alias, origin)
}

// This function assumes the constituent type list is sorted and deduplicated.
func (c *Checker) getUnionTypeFromSortedList(types []*Type, precomputedObjectFlags ObjectFlags, alias *TypeAlias, origin *Type) *Type {
	if len(types) == 0 {
		return c.neverType
	}
	if len(types) == 1 {
		return types[0]
	}
	key := getUnionKey(types, origin, alias)
	t := c.unionTypes[key]
	if t == nil {
		t = c.newUnionType(precomputedObjectFlags|c.getPropagatingFlagsOfTypes(types, TypeFlagsNullable), types)
		t.AsUnionType().origin = origin
		t.alias = alias
		if len(types) == 2 && types[0].flags&TypeFlagsBooleanLiteral != 0 && types[1].flags&TypeFlagsBooleanLiteral != 0 {
			t.flags |= TypeFlagsBoolean
		}
		c.unionTypes[key] = t
	}
	return t
}

func (c *Checker) UnionTypes() iter.Seq[*Type] {
	return maps.Values(c.unionTypes)
}

func (c *Checker) addTypesToUnion(typeSet []*Type, includes TypeFlags, types []*Type) ([]*Type, TypeFlags) {
	var lastType *Type
	for _, t := range types {
		if t != lastType {
			if t.flags&TypeFlagsUnion != 0 {
				u := t.AsUnionType()
				if t.alias != nil || u.origin != nil {
					includes |= TypeFlagsUnion
				}
				typeSet, includes = c.addTypesToUnion(typeSet, includes, u.types)
			} else {
				typeSet, includes = c.addTypeToUnion(typeSet, includes, t)
			}
			lastType = t
		}
	}
	return typeSet, includes
}

func (c *Checker) addTypeToUnion(typeSet []*Type, includes TypeFlags, t *Type) ([]*Type, TypeFlags) {
	flags := t.flags
	// We ignore 'never' types in unions
	if flags&TypeFlagsNever == 0 {
		includes |= flags & TypeFlagsIncludesMask
		if flags&TypeFlagsInstantiable != 0 {
			includes |= TypeFlagsIncludesInstantiable
		}
		if flags&TypeFlagsIntersection != 0 && t.objectFlags&ObjectFlagsIsConstrainedTypeVariable != 0 {
			includes |= TypeFlagsIncludesConstrainedTypeVariable
		}
		if t == c.wildcardType {
			includes |= TypeFlagsIncludesWildcard
		}
		if c.isErrorType(t) {
			includes |= TypeFlagsIncludesError
		}
		if !c.strictNullChecks && flags&TypeFlagsNullable != 0 {
			if t.objectFlags&ObjectFlagsContainsWideningType == 0 {
				includes |= TypeFlagsIncludesNonWideningType
			}
		} else {
			if index, ok := slices.BinarySearchFunc(typeSet, t, CompareTypes); !ok {
				typeSet = slices.Insert(typeSet, index, t)
			}
		}
	}
	return typeSet, includes
}

func (c *Checker) addNamedUnions(namedUnions []*Type, types []*Type) []*Type {
	for _, t := range types {
		if t.flags&TypeFlagsUnion != 0 {
			u := t.AsUnionType()
			if t.alias != nil || u.origin != nil && u.origin.flags&TypeFlagsUnion == 0 {
				namedUnions = core.AppendIfUnique(namedUnions, t)
			} else if u.origin != nil && u.origin.flags&TypeFlagsUnion != 0 {
				namedUnions = c.addNamedUnions(namedUnions, u.origin.Types())
			}
		}
	}
	return namedUnions
}

func (c *Checker) removeRedundantLiteralTypes(types []*Type, includes TypeFlags, reduceVoidUndefined bool) []*Type {
	i := len(types)
	for i > 0 {
		i--
		t := types[i]
		flags := t.flags
		remove := flags&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 && includes&TypeFlagsString != 0 ||
			flags&TypeFlagsNumberLiteral != 0 && includes&TypeFlagsNumber != 0 ||
			flags&TypeFlagsBigIntLiteral != 0 && includes&TypeFlagsBigInt != 0 ||
			flags&TypeFlagsUniqueESSymbol != 0 && includes&TypeFlagsESSymbol != 0 ||
			reduceVoidUndefined && flags&TypeFlagsUndefined != 0 && includes&TypeFlagsVoid != 0 ||
			isFreshLiteralType(t) && containsType(types, t.AsLiteralType().regularType)
		if remove {
			types = slices.Delete(types, i, i+1)
		}
	}
	return types
}

func (c *Checker) removeStringLiteralsMatchedByTemplateLiterals(types []*Type) []*Type {
	templates := core.Filter(types, c.isPatternLiteralType)
	if len(templates) != 0 {
		i := len(types)
		for i > 0 {
			i--
			t := types[i]
			if t.flags&TypeFlagsStringLiteral != 0 && core.Some(templates, func(template *Type) bool {
				return c.isTypeMatchedByTemplateLiteralOrStringMapping(t, template)
			}) {
				types = slices.Delete(types, i, i+1)
			}
		}
	}
	return types
}

func (c *Checker) isTypeMatchedByTemplateLiteralOrStringMapping(t *Type, template *Type) bool {
	if template.flags&TypeFlagsTemplateLiteral != 0 {
		return c.isTypeMatchedByTemplateLiteralType(t, template.AsTemplateLiteralType(), c.compareTypesAssignable)
	}
	return c.isMemberOfStringMapping(t, template)
}

func (c *Checker) removeConstrainedTypeVariables(types []*Type) []*Type {
	var typeVariables []*Type
	// First collect a list of the type variables occurring in constraining intersections.
	for _, t := range types {
		if t.flags&TypeFlagsIntersection != 0 && t.objectFlags&ObjectFlagsIsConstrainedTypeVariable != 0 {
			index := 0
			if t.AsIntersectionType().types[0].flags&TypeFlagsTypeVariable == 0 {
				index = 1
			}
			typeVariables = core.AppendIfUnique(typeVariables, t.AsIntersectionType().types[index])
		}
	}
	// For each type variable, check if the constraining intersections for that type variable fully
	// cover the constraint of the type variable; if so, remove the constraining intersections and
	// substitute the type variable.
	for _, typeVariable := range typeVariables {
		var primitives []*Type
		// First collect the primitive types from the constraining intersections.
		for _, t := range types {
			if t.flags&TypeFlagsIntersection != 0 && t.objectFlags&ObjectFlagsIsConstrainedTypeVariable != 0 {
				index := 0
				if t.AsIntersectionType().types[0].flags&TypeFlagsTypeVariable == 0 {
					index = 1
				}
				if t.AsIntersectionType().types[index] == typeVariable {
					primitives, _ = insertType(primitives, t.AsIntersectionType().types[1-index])
				}
			}
		}
		// If every constituent in the type variable's constraint is covered by an intersection of the type
		// variable and that constituent, remove those intersections and substitute the type variable.
		constraint := c.getBaseConstraintOfType(typeVariable)
		if everyType(constraint, func(t *Type) bool { return containsType(primitives, t) }) {
			i := len(types)
			for i > 0 {
				i--
				t := types[i]
				if t.flags&TypeFlagsIntersection != 0 && t.objectFlags&ObjectFlagsIsConstrainedTypeVariable != 0 {
					index := 0
					if t.AsIntersectionType().types[0].flags&TypeFlagsTypeVariable == 0 {
						index = 1
					}
					if t.AsIntersectionType().types[index] == typeVariable && containsType(primitives, t.AsIntersectionType().types[1-index]) {
						types = slices.Delete(types, i, i+1)
					}
				}
			}
			types, _ = insertType(types, typeVariable)
		}
	}
	return types
}

func (c *Checker) removeSubtypes(types []*Type, hasObjectTypes bool) []*Type {
	// [] and [T] immediately reduce to [] and [T] respectively
	if len(types) < 2 {
		return types
	}
	key := getTypeListKey(types)
	if cached := c.subtypeReductionCache[key]; cached != nil {
		return cached
	}
	// We assume that redundant primitive types have already been removed from the types array and that there
	// are no any and unknown types in the array. Thus, the only possible supertypes for primitive types are empty
	// object types, and if none of those are present we can exclude primitive types from the subtype check.
	hasEmptyObject := hasObjectTypes && core.Some(types, func(t *Type) bool {
		return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && c.isEmptyResolvedType(c.resolveStructuredTypeMembers(t))
	})
	length := len(types)
	i := length
	count := 0
	for i > 0 {
		i--
		source := types[i]
		if hasEmptyObject || source.flags&TypeFlagsStructuredOrInstantiable != 0 {
			// A type parameter with a union constraint may be a subtype of some union, but not a subtype of the
			// individual constituents of that union. For example, `T extends A | B` is a subtype of `A | B`, but not
			// a subtype of just `A` or just `B`. When we encounter such a type parameter, we therefore check if the
			// type parameter is a subtype of a union of all the other types.
			if source.flags&TypeFlagsTypeParameter != 0 && c.getBaseConstraintOrType(source).flags&TypeFlagsUnion != 0 {
				if c.isTypeRelatedTo(source, c.getUnionType(core.Map(types, func(t *Type) *Type {
					if t == source {
						return c.neverType
					}
					return t
				})), c.strictSubtypeRelation) {
					types = slices.Delete(types, i, i+1)
				}
				continue
			}
			// Find the first property with a unit type, if any. When constituents have a property by the same name
			// but of a different unit type, we can quickly disqualify them from subtype checks. This helps subtype
			// reduction of large discriminated union types.
			var keyProperty *ast.Symbol
			var keyPropertyType *Type
			if source.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsInstantiableNonPrimitive) != 0 {
				keyProperty = core.Find(c.getPropertiesOfType(source), func(p *ast.Symbol) bool {
					return isUnitType(c.getTypeOfSymbol(p))
				})
			}
			if keyProperty != nil {
				keyPropertyType = c.getRegularTypeOfLiteralType(c.getTypeOfSymbol(keyProperty))
			}
			for _, target := range types {
				if source != target {
					if count == 100000 {
						// After 100000 subtype checks we estimate the remaining amount of work by assuming the
						// same ratio of checks per element. If the estimated number of remaining type checks is
						// greater than 1M we deem the union type too complex to represent. This for example
						// caps union types at 1000 unique object types.
						estimatedCount := (count / (length - i)) * length
						if estimatedCount > 1000000 {
							if tr := c.tracer; tr != nil {
								tr.Instant(tracing.PhaseCheckTypes, "removeSubtypes_DepthLimit", map[string]any{"estimatedCount": estimatedCount})
							}
							c.error(c.currentNode, diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent)
							return nil
						}
					}
					count++
					if keyProperty != nil && target.flags&(TypeFlagsObject|TypeFlagsIntersection|TypeFlagsInstantiableNonPrimitive) != 0 {
						t := c.getTypeOfPropertyOfType(target, keyProperty.Name)
						if t != nil && isUnitType(t) && c.getRegularTypeOfLiteralType(t) != keyPropertyType {
							continue
						}
					}
					if (source == c.emptyObjectType || source == c.unknownEmptyObjectType) && target.symbol != nil && c.IsEmptyAnonymousObjectType(target) {
						continue
					}
					if c.isTypeRelatedTo(source, target, c.strictSubtypeRelation) && (c.getTargetType(source).objectFlags&ObjectFlagsClass == 0 ||
						c.getTargetType(target).objectFlags&ObjectFlagsClass == 0 ||
						c.isTypeDerivedFrom(source, target)) {
						types = slices.Delete(types, i, i+1)
						break
					}
				}
			}
		}
	}
	c.subtypeReductionCache[key] = types
	return types
}

func (c *Checker) intersectTypes(type1 *Type, type2 *Type) *Type {
	switch {
	case type1 == nil:
		return type2
	case type2 == nil:
		return type1
	}
	return c.getIntersectionType([]*Type{type1, type2})
}

type IntersectionFlags uint32

const (
	IntersectionFlagsNone                  IntersectionFlags = 0
	IntersectionFlagsNoSupertypeReduction  IntersectionFlags = 1 << 0
	IntersectionFlagsNoConstraintReduction IntersectionFlags = 1 << 1
)

// We normalize combinations of intersection and union types based on the distributive property of the '&'
// operator. Specifically, because X & (A | B) is equivalent to X & A | X & B, we can transform intersection
// types with union type constituents into equivalent union types with intersection type constituents and
// effectively ensure that union types are always at the top level in type representations.
//
// We do not perform structural deduplication on intersection types. Intersection types are created only by the &
// type operator and we can't reduce those because we want to support recursive intersection types. For example,
// a type alias of the form "type List<T> = T & { next: List<T> }" cannot be reduced during its declaration.
// Also, unlike union types, the order of the constituent types is preserved in order that overload resolution
// for intersections of types with signatures can be deterministic.
func (c *Checker) getIntersectionType(types []*Type) *Type {
	return c.getIntersectionTypeEx(types, IntersectionFlagsNone, nil /*alias*/)
}

func (c *Checker) getIntersectionTypeEx(types []*Type, flags IntersectionFlags, alias *TypeAlias) *Type {
	var orderedTypes orderedSet[*Type]
	orderedTypes.values = make([]*Type, 0, len(types))
	includes := c.addTypesToIntersection(&orderedTypes, 0, types)
	typeSet := orderedTypes.values
	objectFlags := ObjectFlagsNone
	// An intersection type is considered empty if it contains
	// the type never, or
	// more than one unit type or,
	// an object type and a nullable type (null or undefined), or
	// a string-like type and a type known to be non-string-like, or
	// a number-like type and a type known to be non-number-like, or
	// a symbol-like type and a type known to be non-symbol-like, or
	// a void-like type and a type known to be non-void-like, or
	// a non-primitive type and a type known to be primitive.
	if includes&TypeFlagsNever != 0 {
		if slices.Contains(typeSet, c.silentNeverType) {
			return c.silentNeverType
		}
		return c.neverType
	}
	if c.strictNullChecks && includes&TypeFlagsNullable != 0 && includes&(TypeFlagsObject|TypeFlagsNonPrimitive|TypeFlagsIncludesEmptyObject) != 0 ||
		includes&TypeFlagsNonPrimitive != 0 && includes&(TypeFlagsDisjointDomains&^TypeFlagsNonPrimitive) != 0 ||
		includes&TypeFlagsStringLike != 0 && includes&(TypeFlagsDisjointDomains&^TypeFlagsStringLike) != 0 ||
		includes&TypeFlagsNumberLike != 0 && includes&(TypeFlagsDisjointDomains&^TypeFlagsNumberLike) != 0 ||
		includes&TypeFlagsBigIntLike != 0 && includes&(TypeFlagsDisjointDomains&^TypeFlagsBigIntLike) != 0 ||
		includes&TypeFlagsESSymbolLike != 0 && includes&(TypeFlagsDisjointDomains&^TypeFlagsESSymbolLike) != 0 ||
		includes&TypeFlagsVoidLike != 0 && includes&(TypeFlagsDisjointDomains&^TypeFlagsVoidLike) != 0 {
		return c.neverType
	}
	if includes&(TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 && includes&TypeFlagsStringLiteral != 0 {
		var isEmptySet bool
		typeSet, isEmptySet = c.extractRedundantTemplateLiterals(typeSet)
		if isEmptySet {
			return c.neverType
		}
	}
	if includes&TypeFlagsAny != 0 {
		switch {
		case includes&TypeFlagsIncludesWildcard != 0:
			return c.wildcardType
		case includes&TypeFlagsIncludesError != 0:
			return c.errorType
		}
		return c.anyType
	}
	if !c.strictNullChecks && includes&TypeFlagsNullable != 0 {
		switch {
		case includes&TypeFlagsIncludesEmptyObject != 0:
			return c.neverType
		case includes&TypeFlagsUndefined != 0:
			return c.undefinedType
		}
		return c.nullType
	}
	if includes&TypeFlagsString != 0 && includes&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 ||
		includes&TypeFlagsNumber != 0 && includes&TypeFlagsNumberLiteral != 0 ||
		includes&TypeFlagsBigInt != 0 && includes&TypeFlagsBigIntLiteral != 0 ||
		includes&TypeFlagsESSymbol != 0 && includes&TypeFlagsUniqueESSymbol != 0 ||
		includes&TypeFlagsVoid != 0 && includes&TypeFlagsUndefined != 0 ||
		includes&TypeFlagsIncludesEmptyObject != 0 && includes&TypeFlagsDefinitelyNonNullable != 0 {
		if flags&IntersectionFlagsNoSupertypeReduction == 0 {
			typeSet = c.removeRedundantSupertypes(typeSet, includes)
		}
	}
	if includes&TypeFlagsIncludesMissingType != 0 {
		typeSet[slices.Index(typeSet, c.undefinedType)] = c.missingType
	}
	if len(typeSet) == 0 {
		return c.unknownType
	}
	if len(typeSet) == 1 {
		return typeSet[0]
	}
	if len(typeSet) == 2 && flags&IntersectionFlagsNoConstraintReduction == 0 {
		typeVarIndex := 0
		if typeSet[0].flags&TypeFlagsTypeVariable == 0 {
			typeVarIndex = 1
		}
		typeVariable := typeSet[typeVarIndex]
		primitiveType := typeSet[1-typeVarIndex]
		if typeVariable.flags&TypeFlagsTypeVariable != 0 && (primitiveType.flags&(TypeFlagsPrimitive|TypeFlagsNonPrimitive) != 0 && !c.isGenericStringLikeType(primitiveType) ||
			includes&TypeFlagsIncludesEmptyObject != 0) {
			// We have an intersection T & P or P & T, where T is a type variable and P is a primitive type, the object type, or {}.
			constraint := c.getBaseConstraintOfType(typeVariable)
			// Check that T's constraint is similarly composed of primitive types, the object type, or {}.
			if constraint != nil && everyType(constraint, c.isPrimitiveOrObjectOrEmptyType) {
				// If T's constraint is a subtype of P, simply return T. For example, given `T extends "a" | "b"`,
				// the intersection `T & string` reduces to just T.
				if c.isTypeStrictSubtypeOf(constraint, primitiveType) {
					return typeVariable
				}
				if !(constraint.flags&TypeFlagsUnion != 0 && someType(constraint, func(n *Type) bool {
					return c.isTypeStrictSubtypeOf(n, primitiveType)
				})) {
					// No constituent of T's constraint is a subtype of P. If P is also not a subtype of T's constraint,
					// then the constraint and P are unrelated, and the intersection reduces to never. For example, given
					// `T extends "a" | "b"`, the intersection `T & number` reduces to never.
					if !c.isTypeStrictSubtypeOf(primitiveType, constraint) {
						return c.neverType
					}
				}
				// Some constituent of T's constraint is a subtype of P, or P is a subtype of T's constraint. Thus,
				// the intersection further constrains the type variable. For example, given `T extends string | number`,
				// the intersection `T & "a"` is marked as a constrained type variable. Likewise, given `T extends "a" | 1`,
				// the intersection `T & number` is marked as a constrained type variable.
				objectFlags = ObjectFlagsIsConstrainedTypeVariable
			}
		}
	}
	key := getIntersectionKey(typeSet, flags, alias)
	result := c.intersectionTypes[key]
	if result == nil {
		if includes&TypeFlagsUnion != 0 {
			var reduced bool
			typeSet, reduced = c.intersectUnionsOfPrimitiveTypes(typeSet)
			switch {
			case reduced:
				// When the intersection creates a reduced set (which might mean that *all* union types have
				// disappeared), we restart the operation to get a new set of combined flags. Once we have
				// reduced we'll never reduce again, so this occurs at most once.
				result = c.getIntersectionTypeEx(typeSet, flags, alias)
			case core.Every(typeSet, isUnionWithUndefined):
				containedUndefinedType := c.undefinedType
				if core.Some(typeSet, c.containsMissingType) {
					containedUndefinedType = c.missingType
				}
				c.filterTypes(typeSet, isNotUndefinedType)
				result = c.getUnionTypeEx([]*Type{c.getIntersectionTypeEx(typeSet, flags, nil /*alias*/), containedUndefinedType}, UnionReductionLiteral, alias, nil /*origin*/)
			case core.Every(typeSet, isUnionWithNull):
				c.filterTypes(typeSet, isNotNullType)
				result = c.getUnionTypeEx([]*Type{c.getIntersectionTypeEx(typeSet, flags, nil /*alias*/), c.nullType}, UnionReductionLiteral, alias, nil /*origin*/)
			case len(typeSet) >= 3 && len(types) > 2:
				// When we have three or more constituents, more than two inputs (to head off infinite reexpansion), some of which are unions, we employ a "divide and conquer" strategy
				// where A & B & C & D is processed as (A & B) & (C & D). Since intersections of unions often produce far smaller
				// unions of intersections than the full cartesian product (due to some intersections becoming `never`), this can
				// dramatically reduce the overall work.
				middle := len(typeSet) / 2
				result = c.getIntersectionTypeEx([]*Type{
					c.getIntersectionTypeEx(typeSet[:middle], flags, nil /*alias*/),
					c.getIntersectionTypeEx(typeSet[middle:], flags, nil /*alias*/),
				},
					flags, alias)
			default:
				// We are attempting to construct a type of the form X & (A | B) & (C | D). Transform this into a type of
				// the form X & A & C | X & A & D | X & B & C | X & B & D. If the estimated size of the resulting union type
				// exceeds 100000 constituents, report an error.
				if !c.checkCrossProductUnion(typeSet) {
					return c.errorType
				}
				constituents := c.getCrossProductIntersections(typeSet, flags)
				// We attach a denormalized origin type when at least one constituent of the cross-product union is an
				// intersection (i.e. when the intersection didn't just reduce one or more unions to smaller unions) and
				// the denormalized origin has fewer constituents than the union itself.
				var origin *Type
				if core.Some(constituents, isIntersectionType) && getConstituentCountOfTypes(constituents) > getConstituentCountOfTypes(typeSet) {
					origin = c.newIntersectionType(ObjectFlagsNone, typeSet)
				}
				result = c.getUnionTypeEx(constituents, UnionReductionLiteral, alias, origin)
			}
		} else {
			result = c.newIntersectionType(objectFlags|c.getPropagatingFlagsOfTypes(types /*excludeKinds*/, TypeFlagsNullable), typeSet)
			result.alias = alias
		}
		c.intersectionTypes[key] = result
	}
	return result
}

func isUnionWithUndefined(t *Type) bool {
	return t.flags&TypeFlagsUnion != 0 && t.Types()[0].flags&TypeFlagsUndefined != 0
}

func isUnionWithNull(t *Type) bool {
	return t.flags&TypeFlagsUnion != 0 && (t.Types()[0].flags&TypeFlagsNull != 0 || t.Types()[1].flags&TypeFlagsNull != 0)
}

func isIntersectionType(t *Type) bool {
	return t.flags&TypeFlagsIntersection != 0
}

func isPrimitiveUnion(t *Type) bool {
	return t.objectFlags&ObjectFlagsPrimitiveUnion != 0
}

func isNotUndefinedType(t *Type) bool {
	return t.flags&TypeFlagsUndefined == 0
}

func isNotNullType(t *Type) bool {
	return t.flags&TypeFlagsNull == 0
}

// Add the given types to the given type set. Order is preserved, freshness is removed from literal
// types, duplicates are removed, and nested types of the given kind are flattened into the set.
func (c *Checker) addTypesToIntersection(typeSet *orderedSet[*Type], includes TypeFlags, types []*Type) TypeFlags {
	for _, t := range types {
		includes = c.addTypeToIntersection(typeSet, includes, c.getRegularTypeOfLiteralType(t))
	}
	return includes
}

func (c *Checker) addTypeToIntersection(typeSet *orderedSet[*Type], includes TypeFlags, t *Type) TypeFlags {
	flags := t.flags
	if flags&TypeFlagsIntersection != 0 {
		return c.addTypesToIntersection(typeSet, includes, t.Types())
	}
	if c.IsEmptyAnonymousObjectType(t) {
		if includes&TypeFlagsIncludesEmptyObject == 0 {
			includes |= TypeFlagsIncludesEmptyObject
			typeSet.add(t)
		}
	} else {
		if flags&TypeFlagsAnyOrUnknown != 0 {
			if t == c.wildcardType {
				includes |= TypeFlagsIncludesWildcard
			}
			if c.isErrorType(t) {
				includes |= TypeFlagsIncludesError
			}
		} else if c.strictNullChecks || flags&TypeFlagsNullable == 0 {
			if t == c.missingType {
				includes |= TypeFlagsIncludesMissingType
				t = c.undefinedType
			}
			if !typeSet.contains(t) {
				if t.flags&TypeFlagsUnit != 0 && includes&TypeFlagsUnit != 0 {
					// We have seen two distinct unit types which means we should reduce to an
					// empty intersection. Adding TypeFlags.NonPrimitive causes that to happen.
					includes |= TypeFlagsNonPrimitive
				}
				typeSet.add(t)
			}
		}
		includes |= flags & TypeFlagsIncludesMask
	}
	return includes
}

func (c *Checker) removeRedundantSupertypes(types []*Type, includes TypeFlags) []*Type {
	i := len(types)
	for i > 0 {
		i--
		t := types[i]
		remove := t.flags&TypeFlagsString != 0 && includes&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 ||
			t.flags&TypeFlagsNumber != 0 && includes&TypeFlagsNumberLiteral != 0 ||
			t.flags&TypeFlagsBigInt != 0 && includes&TypeFlagsBigIntLiteral != 0 ||
			t.flags&TypeFlagsESSymbol != 0 && includes&TypeFlagsUniqueESSymbol != 0 ||
			t.flags&TypeFlagsVoid != 0 && includes&TypeFlagsUndefined != 0 ||
			c.IsEmptyAnonymousObjectType(t) && includes&TypeFlagsDefinitelyNonNullable != 0
		if remove {
			types = slices.Delete(types, i, i+1)
		}
	}
	return types
}

/**
 * Returns true if the intersection of the template literals and string literals is the empty set,
 * for example `get${string}` & "setX", and should reduce to never.
 */
func (c *Checker) extractRedundantTemplateLiterals(types []*Type) ([]*Type, bool) {
	literals := core.Filter(types, func(t *Type) bool { return t.flags&TypeFlagsStringLiteral != 0 })
	i := len(types)
	for i > 0 {
		i--
		t := types[i]
		if t.flags&(TypeFlagsTemplateLiteral|TypeFlagsStringMapping) == 0 {
			continue
		}
		for _, t2 := range literals {
			if c.isTypeSubtypeOf(t2, t) {
				// For example, `get${T}` & "getX" is just "getX", and Lowercase<string> & "foo" is just "foo"
				types = slices.Delete(types, i, i+1)
				break
			}
			if c.isPatternLiteralType(t) {
				return types, true
			}
		}
	}
	return types, false
}

// If the given list of types contains more than one union of primitive types, replace the
// first with a union containing an intersection of those primitive types, then remove the
// other unions and return true. Otherwise, do nothing and return false.
func (c *Checker) intersectUnionsOfPrimitiveTypes(types []*Type) ([]*Type, bool) {
	index := slices.IndexFunc(types, isPrimitiveUnion)
	if index < 0 {
		return types, false
	}
	// Remove all but the first union of primitive types and collect them in
	// the unionTypes array.
	i := index + 1
	unionTypes := types[index:i:i]
	for i < len(types) {
		t := types[i]
		if t.objectFlags&ObjectFlagsPrimitiveUnion != 0 {
			unionTypes = append(unionTypes, t)
			types = slices.Delete(types, i, i+1)
		} else {
			i++
		}
	}
	// Return false if there was only one union of primitive types
	if len(unionTypes) == 1 {
		return types, false
	}
	// We have more than one union of primitive types, now intersect them. For each
	// type in each union we check if the type is matched in every union and if so
	// we include it in the result.
	var checked []*Type
	var result []*Type
	for _, u := range unionTypes {
		for _, t := range u.Types() {
			var inserted bool
			if checked, inserted = insertType(checked, t); inserted {
				if c.eachUnionContains(unionTypes, t) {
					// undefinedType/missingType are always sorted first so we leverage that here
					if t == c.undefinedType && len(result) != 0 && result[0] == c.missingType {
						continue
					}
					if t == c.missingType && len(result) != 0 && result[0] == c.undefinedType {
						result[0] = c.missingType
						continue
					}
					result, _ = insertType(result, t)
				}
			}
		}
	}
	// Finally replace the first union with the result
	types[index] = c.getUnionTypeFromSortedList(result, ObjectFlagsPrimitiveUnion, nil /*alias*/, nil /*origin*/)
	return types, true
}

// Check that the given type has a match in every union. A given type is matched by
// an identical type, and a literal type is additionally matched by its corresponding
// primitive type, and missingType is matched by undefinedType (and vice versa).
func (c *Checker) eachUnionContains(unionTypes []*Type, t *Type) bool {
	for _, u := range unionTypes {
		types := u.Types()
		if !containsType(types, t) {
			if t == c.missingType {
				return containsType(types, c.undefinedType)
			}
			if t == c.undefinedType {
				return containsType(types, c.missingType)
			}
			var primitive *Type
			switch {
			case t.flags&TypeFlagsStringLiteral != 0:
				primitive = c.stringType
			case t.flags&(TypeFlagsEnum|TypeFlagsNumberLiteral) != 0:
				primitive = c.numberType
			case t.flags&TypeFlagsBigIntLiteral != 0:
				primitive = c.bigintType
			case t.flags&TypeFlagsUniqueESSymbol != 0:
				primitive = c.esSymbolType
			}
			if primitive == nil || !containsType(types, primitive) {
				return false
			}
		}
	}
	return true
}

func (c *Checker) getCrossProductIntersections(types []*Type, flags IntersectionFlags) []*Type {
	count := c.getCrossProductUnionSize(types)
	var intersections []*Type
	for i := range count {
		constituents := slices.Clone(types)
		n := i
		for j := len(types) - 1; j >= 0; j-- {
			if types[j].flags&TypeFlagsUnion != 0 {
				sourceTypes := types[j].Types()
				length := len(sourceTypes)
				constituents[j] = sourceTypes[n%length]
				n = n / length
			}
		}
		t := c.getIntersectionTypeEx(constituents, flags, nil /*alias*/)
		if t.flags&TypeFlagsNever == 0 {
			intersections = append(intersections, t)
		}
	}
	return intersections
}

func getConstituentCount(t *Type) int {
	switch {
	case t.flags&TypeFlagsUnionOrIntersection == 0 || t.alias != nil:
		return 1
	case t.flags&TypeFlagsUnion != 0 && t.AsUnionType().origin != nil:
		return getConstituentCount(t.AsUnionType().origin)
	}
	return getConstituentCountOfTypes(t.Types())
}

func getConstituentCountOfTypes(types []*Type) int {
	n := 0
	for _, t := range types {
		n += getConstituentCount(t)
	}
	return n
}

func (c *Checker) filterTypes(types []*Type, predicate func(*Type) bool) {
	for i, t := range types {
		types[i] = c.filterType(t, predicate)
	}
}

func (c *Checker) IsEmptyAnonymousObjectType(t *Type) bool {
	return t.objectFlags&ObjectFlagsAnonymous != 0 && (t.objectFlags&ObjectFlagsMembersResolved != 0 && c.isEmptyResolvedType(t.AsStructuredType()) ||
		t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsTypeLiteral != 0 && len(c.getMembersOfSymbol(t.symbol)) == 0)
}

func (c *Checker) isEmptyResolvedType(t *StructuredType) bool {
	return t.AsType() != c.anyFunctionType && len(t.properties) == 0 && len(t.signatures) == 0 && len(t.indexInfos) == 0
}

func (c *Checker) isEmptyObjectType(t *Type) bool {
	switch {
	case t.flags&TypeFlagsObject != 0:
		return !c.isGenericMappedType(t) && c.isEmptyResolvedType(c.resolveStructuredTypeMembers(t))
	case t.flags&TypeFlagsNonPrimitive != 0:
		return true
	case t.flags&TypeFlagsUnion != 0:
		return core.Some(t.Types(), c.isEmptyObjectType)
	case t.flags&TypeFlagsIntersection != 0:
		return core.Every(t.Types(), c.isEmptyObjectType)
	}
	return false
}

func (c *Checker) isPatternLiteralPlaceholderType(t *Type) bool {
	if t.flags&TypeFlagsIntersection != 0 {
		// Return true if the intersection consists of one or more placeholders and zero or
		// more object type tags.
		seenPlaceholder := false
		for _, s := range t.Types() {
			if s.flags&(TypeFlagsLiteral|TypeFlagsNullable) != 0 || c.isPatternLiteralPlaceholderType(s) {
				seenPlaceholder = true
			} else if s.flags&TypeFlagsObject == 0 {
				return false
			}
		}
		return seenPlaceholder
	}
	return t.flags&(TypeFlagsAny|TypeFlagsString|TypeFlagsNumber|TypeFlagsBigInt) != 0 || c.isPatternLiteralType(t)
}

func (c *Checker) isPatternLiteralType(t *Type) bool {
	// A pattern literal type is a template literal or a string mapping type that contains only
	// non-generic pattern literal placeholders.
	return t.flags&TypeFlagsTemplateLiteral != 0 && core.Every(t.AsTemplateLiteralType().types, c.isPatternLiteralPlaceholderType) ||
		t.flags&TypeFlagsStringMapping != 0 && c.isPatternLiteralPlaceholderType(t.Target())
}

func (c *Checker) isGenericStringLikeType(t *Type) bool {
	return t.flags&(TypeFlagsTemplateLiteral|TypeFlagsStringMapping) != 0 && !c.isPatternLiteralType(t)
}

func forEachType(t *Type, f func(t *Type)) {
	if t.flags&TypeFlagsUnion != 0 {
		for _, u := range t.Types() {
			f(u)
		}
	} else {
		f(t)
	}
}

func someType(t *Type, f func(*Type) bool) bool {
	if t.flags&TypeFlagsUnion != 0 {
		return core.Some(t.Types(), f)
	}
	return f(t)
}

func everyType(t *Type, f func(*Type) bool) bool {
	if t.flags&TypeFlagsUnion != 0 {
		return core.Every(t.Types(), f)
	}
	return f(t)
}

func everyContainedType(t *Type, f func(*Type) bool) bool {
	if t.flags&TypeFlagsUnionOrIntersection != 0 {
		return core.Every(t.Types(), f)
	}
	return f(t)
}

func (c *Checker) filterType(t *Type, f func(*Type) bool) *Type {
	if t.flags&TypeFlagsUnion != 0 {
		types := t.Types()
		filtered := core.Filter(types, f)
		if core.Same(types, filtered) {
			return t
		}
		origin := t.AsUnionType().origin
		var newOrigin *Type
		if origin != nil && origin.flags&TypeFlagsUnion != 0 {
			// If the origin type is a (denormalized) union type, filter its non-union constituents. If that ends
			// up removing a smaller number of types than in the normalized constituent set (meaning some of the
			// filtered types are within nested unions in the origin), then we can't construct a new origin type.
			// Otherwise, if we have exactly one type left in the origin set, return that as the filtered type.
			// Otherwise, construct a new filtered origin type.
			originTypes := origin.Types()
			originFiltered := core.Filter(originTypes, func(u *Type) bool {
				return u.flags&TypeFlagsUnion != 0 || f(u)
			})
			if len(originTypes)-len(originFiltered) == len(types)-len(filtered) {
				if len(originFiltered) == 1 {
					return originFiltered[0]
				}
				newOrigin = c.newUnionType(ObjectFlagsNone, originFiltered)
			}
		}
		// filtering could remove intersections so `ContainsIntersections` might be forwarded "incorrectly"
		// it is purely an optimization hint so there is no harm in accidentally forwarding it
		return c.getUnionTypeFromSortedList(filtered, t.AsUnionType().objectFlags&(ObjectFlagsPrimitiveUnion|ObjectFlagsContainsIntersections), nil /*alias*/, newOrigin)
	}
	if t.flags&TypeFlagsNever != 0 || f(t) {
		return t
	}
	return c.neverType
}

func (c *Checker) removeType(t *Type, targetType *Type) *Type {
	return c.filterType(t, func(t *Type) bool { return t != targetType })
}

func containsType(types []*Type, t *Type) bool {
	_, ok := slices.BinarySearchFunc(types, t, CompareTypes)
	return ok
}

func insertType(types []*Type, t *Type) ([]*Type, bool) {
	if i, ok := slices.BinarySearchFunc(types, t, CompareTypes); !ok {
		return slices.Insert(types, i, t), true
	}
	return types, false
}

func countTypes(t *Type) int {
	switch {
	case t.flags&TypeFlagsUnion != 0:
		return len(t.Types())
	case t.flags&TypeFlagsNever != 0:
		return 0
	}
	return 1
}

func (c *Checker) isErrorType(t *Type) bool {
	// The only 'any' types that have alias symbols are those manufactured by getTypeFromTypeAliasReference for
	// a reference to an unresolved symbol. We want those to behave like the errorType.
	return t == c.errorType || t.flags&TypeFlagsAny != 0 && t.alias != nil
}

func compareTypeIds(t1, t2 *Type) int {
	return int(t1.id) - int(t2.id)
}

func (c *Checker) checkCrossProductUnion(types []*Type) bool {
	size := c.getCrossProductUnionSize(types)
	if size >= 100_000 {
		if tr := c.tracer; tr != nil {
			tr.Instant(tracing.PhaseCheckTypes, "checkCrossProductUnion_DepthLimit", map[string]any{"size": size})
		}
		c.error(c.currentNode, diagnostics.Expression_produces_a_union_type_that_is_too_complex_to_represent)
		return false
	}
	return true
}

func (c *Checker) getCrossProductUnionSize(types []*Type) int {
	size := 1
	for _, t := range types {
		switch {
		case t.flags&TypeFlagsUnion != 0:
			n := len(t.Types())
			// Cap the result to avoid integer overflow when computing the cross product of many large unions.
			// In TypeScript, number overflow produces Infinity which naturally exceeds the limit check;
			// in Go, we must guard against int wrapping to zero or negative.
			if n > 0 && size > math.MaxInt/n {
				return math.MaxInt
			}
			size *= n
		case t.flags&TypeFlagsNever != 0:
			return 0
		}
	}
	return size
}

func (c *Checker) getIndexType(t *Type) *Type {
	return c.getIndexTypeEx(t, IndexFlagsNone)
}

func (c *Checker) getIndexTypeEx(t *Type, indexFlags IndexFlags) *Type {
	t = c.getReducedType(t)
	switch {
	case c.isNoInferType(t):
		return c.getNoInferType(c.getIndexTypeEx(t.AsSubstitutionType().baseType, indexFlags))
	case c.shouldDeferIndexType(t, indexFlags):
		return c.getIndexTypeForGenericType(t, indexFlags)
	case t.flags&TypeFlagsUnion != 0:
		return c.getIntersectionType(core.Map(t.Types(), func(t *Type) *Type { return c.getIndexTypeEx(t, indexFlags) }))
	case t.flags&TypeFlagsIntersection != 0:
		return c.getUnionType(core.Map(t.Types(), func(t *Type) *Type { return c.getIndexTypeEx(t, indexFlags) }))
	case t.objectFlags&ObjectFlagsMapped != 0:
		return c.getIndexTypeForMappedType(t, indexFlags)
	case t == c.wildcardType:
		return c.wildcardType
	case t.flags&TypeFlagsUnknown != 0:
		return c.neverType
	case t.flags&(TypeFlagsAny|TypeFlagsNever) != 0:
		return c.stringNumberSymbolType
	}
	include := core.IfElse(indexFlags&IndexFlagsNoIndexSignatures != 0, TypeFlagsStringLiteral, TypeFlagsStringLike) |
		core.IfElse(indexFlags&IndexFlagsStringsOnly != 0, TypeFlagsNone, TypeFlagsNumberLike|TypeFlagsESSymbolLike)
	return c.getLiteralTypeFromProperties(t, include, indexFlags == IndexFlagsNone)
}

func (c *Checker) getExtractStringType(t *Type) *Type {
	extractTypeAlias := c.getGlobalExtractSymbol()
	if extractTypeAlias != nil {
		return c.getTypeAliasInstantiation(extractTypeAlias, []*Type{t, c.stringType}, nil)
	}
	return c.stringType
}

func (c *Checker) getLiteralTypeFromProperties(t *Type, include TypeFlags, includeOrigin bool) *Type {
	key := PropertiesTypesKey{typeId: t.id, include: include, includeOrigin: includeOrigin, unresolvedMembers: t.objectFlags&ObjectFlagsUnresolvedMembers != 0}
	if cached, ok := c.propertiesTypes[key]; ok {
		return cached
	}
	var origin *Type
	if includeOrigin && t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 || t.alias != nil {
		origin = c.newIndexType(t, IndexFlagsNone)
	}
	props := c.getPropertiesOfType(t)
	indexInfos := c.getIndexInfosOfType(t)
	types := make([]*Type, 0, len(props)+len(indexInfos))
	for _, prop := range props {
		types = append(types, c.getLiteralTypeFromProperty(prop, include, false))
	}
	for _, info := range indexInfos {
		if info != c.enumNumberIndexInfo && c.isKeyTypeIncluded(info.keyType, include) {
			if info.keyType == c.stringType && include&TypeFlagsNumber != 0 {
				types = append(types, c.stringOrNumberType)
			} else {
				types = append(types, info.keyType)
			}
		}
	}
	result := c.getUnionTypeEx(types, UnionReductionLiteral, nil, origin)
	c.propertiesTypes[key] = result
	return result
}

func (c *Checker) getLiteralTypeFromProperty(prop *ast.Symbol, include TypeFlags, includeNonPublic bool) *Type {
	if includeNonPublic || getDeclarationModifierFlagsFromSymbol(prop)&ast.ModifierFlagsNonPublicAccessibilityModifier == 0 {
		t := c.valueSymbolLinks.Get(c.getLateBoundSymbol(prop)).nameType
		if t == nil {
			if prop.Name == ast.InternalSymbolNameDefault {
				t = c.getStringLiteralType("default")
			} else {
				name := ast.GetNameOfDeclaration(prop.ValueDeclaration)
				if name != nil {
					t = c.getLiteralTypeFromPropertyName(name)
				}
				if t == nil && !IsKnownSymbol(prop) {
					t = c.getStringLiteralType(ast.SymbolName(prop))
				}
			}
		}
		if t != nil && t.flags&include != 0 {
			return t
		}
	}
	return c.neverType
}

func (c *Checker) getLiteralTypeFromPropertyName(name *ast.Node) *Type {
	if ast.IsPrivateIdentifier(name) {
		return c.neverType
	}
	if ast.IsNumericLiteral(name) {
		return c.getRegularTypeOfLiteralType(c.checkExpression(name))
	}
	if ast.IsComputedPropertyName(name) {
		return c.getRegularTypeOfLiteralType(c.checkComputedPropertyName(name))
	}
	propertyName := ast.GetPropertyNameForPropertyNameNode(name)
	if propertyName != ast.InternalSymbolNameMissing {
		return c.getStringLiteralType(propertyName)
	}
	if ast.IsExpression(name) {
		return c.getRegularTypeOfLiteralType(c.checkExpression(name))
	}
	return c.neverType
}

func (c *Checker) isKeyTypeIncluded(keyType *Type, include TypeFlags) bool {
	return keyType.flags&include != 0 ||
		keyType.flags&TypeFlagsIntersection != 0 && core.Some(keyType.Types(), func(t *Type) bool {
			return c.isKeyTypeIncluded(t, include)
		})
}

func (c *Checker) checkComputedPropertyName(node *ast.Node) *Type {
	links := c.typeNodeLinks.Get(node.Expression())
	if links.resolvedType == nil {
		if (ast.IsTypeLiteralNode(node.Parent.Parent) || ast.IsClassLike(node.Parent.Parent) || ast.IsInterfaceDeclaration(node.Parent.Parent)) &&
			ast.IsBinaryExpression(node.Expression()) && node.Expression().AsBinaryExpression().OperatorToken.Kind == ast.KindInKeyword &&
			!ast.IsAccessor(node.Parent) {
			links.resolvedType = c.errorType
			return links.resolvedType
		}
		links.resolvedType = c.checkExpression(node.Expression())
		// This will allow types number, string, symbol or any. It will also allow enums, the unknown
		// type, and any union of these types (like string | number).
		if links.resolvedType.flags&TypeFlagsNullable != 0 ||
			!c.isTypeAssignableToKind(links.resolvedType, TypeFlagsStringLike|TypeFlagsNumberLike|TypeFlagsESSymbolLike) &&
				!c.isTypeAssignableTo(links.resolvedType, c.stringNumberSymbolType) {
			c.error(node, diagnostics.A_computed_property_name_must_be_of_type_string_number_symbol_or_any)
		}
	}
	return links.resolvedType
}

func (c *Checker) isNoInferType(t *Type) bool {
	// A NoInfer<T> type is represented as a substitution type with a TypeFlags.Unknown constraint.
	return t.flags&TypeFlagsSubstitution != 0 && t.AsSubstitutionType().constraint.flags&TypeFlagsUnknown != 0
}

func (c *Checker) getSubstitutionIntersection(t *Type) *Type {
	if c.isNoInferType(t) {
		return t.AsSubstitutionType().baseType
	}
	return c.getIntersectionType([]*Type{t.AsSubstitutionType().constraint, t.AsSubstitutionType().baseType})
}

func (c *Checker) shouldDeferIndexType(t *Type, indexFlags IndexFlags) bool {
	return t.flags&TypeFlagsInstantiableNonPrimitive != 0 ||
		c.isGenericTupleType(t) ||
		c.isGenericMappedType(t) && c.getNameTypeFromMappedType(t) != nil ||
		t.flags&TypeFlagsUnion != 0 && indexFlags&IndexFlagsNoReducibleCheck == 0 && c.isGenericReducibleType(t) ||
		t.flags&TypeFlagsIntersection != 0 && c.maybeTypeOfKind(t, TypeFlagsInstantiable) && core.Some(t.Types(), c.IsEmptyAnonymousObjectType)
}

func (c *Checker) getMappedTypeNameTypeKind(t *Type) MappedTypeNameTypeKind {
	nameType := c.getNameTypeFromMappedType(t)
	if nameType == nil {
		return MappedTypeNameTypeKindNone
	}
	if c.isTypeAssignableTo(nameType, c.getTypeParameterFromMappedType(t)) {
		return MappedTypeNameTypeKindFiltering
	}
	return MappedTypeNameTypeKindRemapping
}

func (c *Checker) getIndexTypeForGenericType(t *Type, indexFlags IndexFlags) *Type {
	key := CachedTypeKey{
		kind:   core.IfElse(indexFlags&IndexFlagsStringsOnly != 0, CachedTypeKindStringIndexType, CachedTypeKindIndexType),
		typeId: t.id,
	}
	if indexType := c.cachedTypes[key]; indexType != nil {
		return indexType
	}
	indexType := c.newIndexType(t, indexFlags&IndexFlagsStringsOnly)
	c.cachedTypes[key] = indexType
	return indexType
}

// This roughly mirrors `resolveMappedTypeMembers` in the nongeneric case, except only reports a union of the keys calculated,
// rather than manufacturing the properties. We can't just fetch the `constraintType` since that would ignore mappings
// and mapping the `constraintType` directly ignores how mapped types map _properties_ and not keys (thus ignoring subtype
// reduction in the constraintType) when possible.
// @param noIndexSignatures Indicates if _string_ index signatures should be elided. (other index signatures are always reported)
func (c *Checker) getIndexTypeForMappedType(t *Type, indexFlags IndexFlags) *Type {
	typeParameter := c.getTypeParameterFromMappedType(t)
	constraintType := c.getConstraintTypeFromMappedType(t)
	nameType := c.getNameTypeFromMappedType(core.OrElse(t.AsMappedType().target, t))
	if nameType == nil && indexFlags&IndexFlagsNoIndexSignatures == 0 {
		// no mapping and no filtering required, just quickly bail to returning the constraint in the common case
		return constraintType
	}
	var keyTypes []*Type
	addMemberForKeyType := func(keyType *Type) {
		propNameType := keyType
		if nameType != nil {
			propNameType = c.instantiateType(nameType, appendTypeMapping(t.AsMappedType().mapper, typeParameter, keyType))
		}
		// `keyof` currently always returns `string | number` for concrete `string` index signatures - the below ternary keeps that behavior for mapped types
		// See `getLiteralTypeFromProperties` where there's a similar ternary to cause the same behavior.
		keyTypes = append(keyTypes, core.IfElse(propNameType == c.stringType, c.stringOrNumberType, propNameType))
	}
	// Calling getApparentType on the `T` of a `keyof T` in the constraint type of a generic mapped type can
	// trigger a circularity. For example, `T extends { [P in keyof T & string as Captitalize<P>]: any }` is
	// a circular definition. For this reason, we only eagerly manifest the keys if the constraint is non-generic.
	if c.isGenericIndexType(constraintType) {
		if c.isMappedTypeWithKeyofConstraintDeclaration(t) {
			// We have a generic index and a homomorphic mapping and a key remapping - we need to defer
			// the whole `keyof whatever` for later since it's not safe to resolve the shape of modifier type.
			return c.getIndexTypeForGenericType(t, indexFlags)
		}
		// Include the generic component in the resulting type.
		forEachType(constraintType, addMemberForKeyType)
	} else if c.isMappedTypeWithKeyofConstraintDeclaration(t) {
		modifiersType := c.getApparentType(c.getModifiersTypeFromMappedType(t))
		// The 'T' in 'keyof T'
		c.forEachMappedTypePropertyKeyTypeAndIndexSignatureKeyType(modifiersType, TypeFlagsStringOrNumberLiteralOrUnique, indexFlags&IndexFlagsStringsOnly != 0, addMemberForKeyType)
	} else {
		forEachType(c.getLowerBoundOfKeyType(constraintType), addMemberForKeyType)
	}
	// We had to pick apart the constraintType to potentially map/filter it - compare the final resulting list with the
	// original constraintType, so we can return the union that preserves aliases/origin data if possible.
	var result *Type
	if indexFlags&IndexFlagsNoIndexSignatures != 0 {
		result = c.filterType(c.getUnionType(keyTypes), func(t *Type) bool {
			return t.flags&(TypeFlagsAny|TypeFlagsString) == 0
		})
	} else {
		result = c.getUnionType(keyTypes)
	}
	if result.flags&TypeFlagsUnion != 0 && constraintType.flags&TypeFlagsUnion != 0 && getTypeListKey(result.Types()) == getTypeListKey(constraintType.Types()) {
		return constraintType
	}
	return result
}

func (c *Checker) getIndexedAccessType(objectType *Type, indexType *Type) *Type {
	return c.getIndexedAccessTypeEx(objectType, indexType, AccessFlagsNone, nil, nil)
}

func (c *Checker) getIndexedAccessTypeEx(objectType *Type, indexType *Type, accessFlags AccessFlags, accessNode *ast.Node, alias *TypeAlias) *Type {
	result := c.getIndexedAccessTypeOrUndefined(objectType, indexType, accessFlags, accessNode, alias)
	if result == nil {
		result = core.IfElse(accessNode != nil, c.errorType, c.unknownType)
	}
	return result
}

func (c *Checker) getIndexedAccessTypeOrUndefined(objectType *Type, indexType *Type, accessFlags AccessFlags, accessNode *ast.Node, alias *TypeAlias) *Type {
	if objectType == c.wildcardType || indexType == c.wildcardType {
		return c.wildcardType
	}
	objectType = c.getReducedType(objectType)
	// If the object type has a string index signature and no other members we know that the result will
	// always be the type of that index signature and we can simplify accordingly.
	if c.isStringIndexSignatureOnlyType(objectType) && indexType.flags&TypeFlagsNullable == 0 && c.isTypeAssignableToKind(indexType, TypeFlagsString|TypeFlagsNumber) {
		indexType = c.stringType
	}
	// In noUncheckedIndexedAccess mode, indexed access operations that occur in an expression in a read position and resolve to
	// an index signature have 'undefined' included in their type.
	if c.compilerOptions.NoUncheckedIndexedAccess == core.TSTrue && accessFlags&AccessFlagsExpressionPosition != 0 {
		accessFlags |= AccessFlagsIncludeUndefined
	}
	// If the index type is generic, or if the object type is generic and doesn't originate in an expression and
	// the operation isn't exclusively indexing the fixed (non-variadic) portion of a tuple type, we are performing
	// a higher-order index access where we cannot meaningfully access the properties of the object type. Note that
	// for a generic T and a non-generic K, we eagerly resolve T[K] if it originates in an expression. This is to
	// preserve backwards compatibility. For example, an element access 'this["foo"]' has always been resolved
	// eagerly using the constraint type of 'this' at the given location.
	if c.shouldDeferIndexedAccessType(objectType, indexType, accessNode) {
		if objectType.flags&TypeFlagsAnyOrUnknown != 0 {
			return objectType
		}
		// Defer the operation by creating an indexed access type.
		persistentAccessFlags := accessFlags & AccessFlagsPersistent
		key := getIndexedAccessKey(objectType, indexType, accessFlags, alias)
		t := c.indexedAccessTypes[key]
		if t == nil {
			t = c.newIndexedAccessType(objectType, indexType, persistentAccessFlags)
			t.alias = alias
			c.indexedAccessTypes[key] = t
		}
		return t
	}
	// In the following we resolve T[K] to the type of the property in T selected by K.
	// We treat boolean as different from other unions to improve errors;
	// skipping straight to getPropertyTypeForIndexType gives errors with 'boolean' instead of 'true'.
	apparentObjectType := c.getReducedApparentType(objectType)
	if indexType.flags&TypeFlagsUnion != 0 && indexType.flags&TypeFlagsBoolean == 0 {
		var propTypes []*Type
		wasMissingProp := false
		for _, t := range indexType.Types() {
			propType := c.getPropertyTypeForIndexType(objectType, apparentObjectType, t, indexType, accessNode, accessFlags|core.IfElse(wasMissingProp, AccessFlagsSuppressNoImplicitAnyError, 0))
			if propType != nil {
				propTypes = append(propTypes, propType)
			} else if accessNode == nil {
				// If there's no error node, we can immediately stop, since error reporting is off
				return nil
			} else {
				// Otherwise we set a flag and return at the end of the loop so we still mark all errors
				wasMissingProp = true
			}
		}
		if wasMissingProp {
			return nil
		}
		if accessFlags&AccessFlagsWriting != 0 {
			return c.getIntersectionTypeEx(propTypes, IntersectionFlagsNone, alias)
		}
		return c.getUnionTypeEx(propTypes, UnionReductionLiteral, alias, nil)
	}
	return c.getPropertyTypeForIndexType(objectType, apparentObjectType, indexType, indexType, accessNode, accessFlags|AccessFlagsCacheSymbol|AccessFlagsReportDeprecated)
}

func (c *Checker) getPropertyTypeForIndexType(originalObjectType *Type, objectType *Type, indexType *Type, fullIndexType *Type, accessNode *ast.Node, accessFlags AccessFlags) *Type {
	var accessExpression *ast.Node
	if accessNode != nil && ast.IsElementAccessExpression(accessNode) {
		accessExpression = accessNode
	}
	var propName string
	var hasPropName bool
	if !(accessNode != nil && ast.IsPrivateIdentifier(accessNode)) {
		propName = c.getPropertyNameFromIndex(indexType, accessNode)
		hasPropName = propName != ast.InternalSymbolNameMissing
	}
	if hasPropName {
		if accessFlags&AccessFlagsContextual != 0 {
			t := c.getTypeOfPropertyOfContextualType(objectType, propName)
			if t == nil {
				t = c.anyType
			}
			return t
		}
		prop := c.getPropertyOfType(objectType, propName)
		if prop != nil {
			if accessFlags&AccessFlagsReportDeprecated != 0 && accessNode != nil && len(prop.Declarations) != 0 && c.isDeprecatedSymbol(prop) && c.isUncalledFunctionReference(accessNode, prop) {
				var deprecatedNode *ast.Node
				if accessExpression != nil {
					deprecatedNode = accessExpression.AsElementAccessExpression().ArgumentExpression
				} else if ast.IsIndexedAccessTypeNode(accessNode) {
					deprecatedNode = accessNode.AsIndexedAccessTypeNode().IndexType
				} else {
					deprecatedNode = accessNode
				}
				c.addDeprecatedSuggestion(deprecatedNode, prop.Declarations, propName)
			}
			if accessExpression != nil {
				c.markPropertyAsReferenced(prop, accessExpression, c.isSelfTypeAccess(accessExpression.Expression(), objectType.symbol))
				if c.isAssignmentToReadonlyEntity(accessExpression, prop, getAssignmentTargetKind(accessExpression)) {
					c.error(accessExpression.AsElementAccessExpression().ArgumentExpression, diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, c.symbolToString(prop))
					return nil
				}
				if accessFlags&AccessFlagsCacheSymbol != 0 {
					c.symbolNodeLinks.Get(accessNode).resolvedSymbol = prop
				}
				if c.isThisPropertyAccessInConstructor(accessExpression, prop) {
					return c.autoType
				}
			}
			var propType *Type
			if accessFlags&AccessFlagsWriting != 0 {
				propType = c.getWriteTypeOfSymbol(prop)
			} else {
				propType = c.getTypeOfSymbol(prop)
			}
			switch {
			case accessExpression != nil && getAssignmentTargetKind(accessExpression) != AssignmentKindDefinite:
				return c.getFlowTypeOfReference(accessExpression, propType)
			case accessNode != nil && ast.IsIndexedAccessTypeNode(accessNode) && c.containsMissingType(propType):
				return c.getUnionType([]*Type{propType, c.undefinedType})
			default:
				return propType
			}
		}
		if everyType(objectType, isTupleType) && isNumericLiteralName(propName) {
			index := jsnum.FromString(propName)
			if accessNode != nil && everyType(objectType, func(t *Type) bool {
				return t.TargetTupleType().combinedFlags&ElementFlagsVariable == 0
			}) && accessFlags&AccessFlagsAllowMissing == 0 {
				indexNode := getIndexNodeForAccessExpression(accessNode)
				if isTupleType(objectType) {
					if index < 0 {
						c.error(indexNode, diagnostics.A_tuple_type_cannot_be_indexed_with_a_negative_value)
						return c.undefinedType
					}
					c.error(indexNode, diagnostics.Tuple_type_0_of_length_1_has_no_element_at_index_2, c.TypeToString(objectType), c.getTypeReferenceArity(objectType), propName)
				} else {
					c.error(indexNode, diagnostics.Property_0_does_not_exist_on_type_1, propName, c.TypeToString(objectType))
				}
			}
			if index >= 0 {
				c.errorIfWritingToReadonlyIndex(c.getIndexInfoOfType(objectType, c.numberType), objectType, accessExpression)
				return c.getTupleElementTypeOutOfStartCount(objectType, index, core.IfElse(accessFlags&AccessFlagsIncludeUndefined != 0, c.missingType, nil))
			}
		}
	}
	if indexType.flags&TypeFlagsNullable == 0 && c.isTypeAssignableToKind(indexType, TypeFlagsStringLike|TypeFlagsNumberLike|TypeFlagsESSymbolLike) {
		if objectType.flags&(TypeFlagsAny|TypeFlagsNever) != 0 {
			return objectType
		}
		// If no index signature is applicable, we default to the string index signature. In effect, this means the string
		// index signature applies even when accessing with a symbol-like type.
		indexInfo := c.getApplicableIndexInfo(objectType, indexType)
		if indexInfo == nil {
			indexInfo = c.getIndexInfoOfType(objectType, c.stringType)
		}
		if indexInfo != nil {
			if accessFlags&AccessFlagsNoIndexSignatures != 0 && indexInfo.keyType != c.numberType {
				if accessExpression != nil {
					if accessFlags&AccessFlagsWriting != 0 {
						c.error(accessExpression, diagnostics.Type_0_is_generic_and_can_only_be_indexed_for_reading, c.TypeToString(originalObjectType))
					} else {
						c.error(accessExpression, diagnostics.Type_0_cannot_be_used_to_index_type_1, c.TypeToString(indexType), c.TypeToString(originalObjectType))
					}
				}
				return nil
			}
			if accessNode != nil && indexInfo.keyType == c.stringType && !c.isTypeAssignableToKind(indexType, TypeFlagsString|TypeFlagsNumber) {
				indexNode := getIndexNodeForAccessExpression(accessNode)
				c.error(indexNode, diagnostics.Type_0_cannot_be_used_as_an_index_type, c.TypeToString(indexType))
				if accessFlags&AccessFlagsIncludeUndefined != 0 {
					return c.getUnionType([]*Type{indexInfo.valueType, c.missingType})
				} else {
					return indexInfo.valueType
				}
			}
			c.errorIfWritingToReadonlyIndex(indexInfo, objectType, accessExpression)
			// When accessing an enum object with its own type,
			// e.g. E[E.A] for enum E { A }, undefined shouldn't
			// be included in the result type
			if accessFlags&AccessFlagsIncludeUndefined != 0 &&
				!(objectType.symbol != nil &&
					objectType.symbol.Flags&(ast.SymbolFlagsRegularEnum|ast.SymbolFlagsConstEnum) != 0 &&
					(indexType.symbol != nil &&
						indexType.flags&TypeFlagsEnumLiteral != 0 &&
						c.getParentOfSymbol(indexType.symbol) == objectType.symbol)) {
				return c.getUnionType([]*Type{indexInfo.valueType, c.missingType})
			}
			return indexInfo.valueType
		}
		if indexType.flags&TypeFlagsNever != 0 {
			return c.neverType
		}
		if c.isJSLiteralType(objectType) {
			return c.anyType
		}
		if accessExpression != nil && !isConstEnumObjectType(objectType) {
			if isObjectLiteralType(objectType) {
				if c.noImplicitAny && indexType.flags&(TypeFlagsStringLiteral|TypeFlagsNumberLiteral) != 0 {
					c.addDiagnostic(createDiagnosticForNode(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, indexType.AsLiteralType().value, c.TypeToString(objectType)))
					return c.undefinedType
				} else if indexType.flags&(TypeFlagsNumber|TypeFlagsString) != 0 {
					types := core.Map(objectType.AsStructuredType().properties, func(prop *ast.Symbol) *Type {
						return c.getTypeOfSymbol(prop)
					})
					return c.getUnionType(append(types, c.undefinedType))
				}
			}
			if objectType.symbol == c.globalThisSymbol && hasPropName && c.globalThisSymbol.Exports[propName] != nil && c.globalThisSymbol.Exports[propName].Flags&ast.SymbolFlagsBlockScoped != 0 {
				c.error(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, propName, c.TypeToString(objectType))
			} else if c.noImplicitAny && accessFlags&AccessFlagsSuppressNoImplicitAnyError == 0 {
				if hasPropName && c.typeHasStaticProperty(propName, objectType) {
					typeName := c.TypeToString(objectType)
					c.error(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_to_access_the_static_member_2_instead, propName /* as string */, typeName, typeName+"["+scanner.GetTextOfNode(accessExpression.AsElementAccessExpression().ArgumentExpression)+"]")
				} else if c.getIndexTypeOfType(objectType, c.numberType) != nil {
					c.error(accessExpression.AsElementAccessExpression().ArgumentExpression, diagnostics.Element_implicitly_has_an_any_type_because_index_expression_is_not_of_type_number)
				} else {
					var suggestion string
					if hasPropName {
						suggestion = c.getSuggestionForNonexistentProperty(propName, objectType)
					}
					if suggestion != "" {
						c.error(accessExpression.AsElementAccessExpression().ArgumentExpression, diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, propName /* as string */, c.TypeToString(objectType), suggestion)
					} else {
						suggestion = c.getSuggestionForNonexistentIndexSignature(objectType, accessExpression, indexType)
						if suggestion != "" {
							c.error(accessExpression, diagnostics.Element_implicitly_has_an_any_type_because_type_0_has_no_index_signature_Did_you_mean_to_call_1, c.TypeToString(objectType), suggestion)
						} else {
							var diagnostic *ast.Diagnostic
							switch {
							case indexType.flags&TypeFlagsEnumLiteral != 0:
								diagnostic = NewDiagnosticForNode(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, "["+c.TypeToString(indexType)+"]", c.TypeToString(objectType))
							case indexType.flags&TypeFlagsUniqueESSymbol != 0:
								symbolName := c.getFullyQualifiedName(indexType.symbol, accessExpression)
								diagnostic = NewDiagnosticForNode(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, "["+symbolName+"]", c.TypeToString(objectType))
							case indexType.flags&TypeFlagsStringLiteral != 0:
								diagnostic = NewDiagnosticForNode(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, indexType.AsLiteralType().value, c.TypeToString(objectType))
							case indexType.flags&TypeFlagsNumberLiteral != 0:
								diagnostic = NewDiagnosticForNode(accessExpression, diagnostics.Property_0_does_not_exist_on_type_1, indexType.AsLiteralType().value, c.TypeToString(objectType))
							case indexType.flags&(TypeFlagsNumber|TypeFlagsString) != 0:
								diagnostic = NewDiagnosticForNode(accessExpression, diagnostics.No_index_signature_with_a_parameter_of_type_0_was_found_on_type_1, c.TypeToString(indexType), c.TypeToString(objectType))
							}
							c.addDiagnostic(NewDiagnosticChainForNode(diagnostic, accessExpression, diagnostics.Element_implicitly_has_an_any_type_because_expression_of_type_0_can_t_be_used_to_index_type_1, c.TypeToString(fullIndexType), c.TypeToString(objectType)))
						}
					}
				}
			}
			return nil
		}
	}
	if accessFlags&AccessFlagsAllowMissing != 0 && isObjectLiteralType(objectType) {
		return c.undefinedType
	}
	if c.isJSLiteralType(objectType) {
		return c.anyType
	}
	if accessNode != nil {
		indexNode := getIndexNodeForAccessExpression(accessNode)
		if indexNode.Kind != ast.KindBigIntLiteral && indexType.flags&(TypeFlagsStringLiteral|TypeFlagsNumberLiteral) != 0 {
			c.error(indexNode, diagnostics.Property_0_does_not_exist_on_type_1, indexType.AsLiteralType().value, c.TypeToString(objectType))
		} else if indexType.flags&(TypeFlagsString|TypeFlagsNumber) != 0 {
			c.error(indexNode, diagnostics.Type_0_has_no_matching_index_signature_for_type_1, c.TypeToString(objectType), c.TypeToString(indexType))
		} else {
			var typeString string
			if indexNode.Kind == ast.KindBigIntLiteral {
				typeString = "bigint"
			} else {
				typeString = c.TypeToString(indexType)
			}
			c.error(indexNode, diagnostics.Type_0_cannot_be_used_as_an_index_type, typeString)
		}
	}
	if IsTypeAny(indexType) {
		return indexType
	}
	return nil
}

func (c *Checker) typeHasStaticProperty(propName string, containingType *Type) bool {
	if containingType.symbol != nil {
		prop := c.getPropertyOfType(c.getTypeOfSymbol(containingType.symbol), propName)
		return prop != nil && prop.ValueDeclaration != nil && ast.IsStatic(prop.ValueDeclaration)
	}
	return false
}

func (c *Checker) getSuggestionForNonexistentProperty(name string, containingType *Type) string {
	symbol := c.getSpellingSuggestionForName(name, slices.Values(c.getPropertiesOfType(containingType)), ast.SymbolFlagsValue)
	if symbol != nil {
		return symbol.Name
	}
	return ""
}

func (c *Checker) getSuggestionForNonexistentIndexSignature(objectType *Type, expr *ast.Node, keyedType *Type) string {
	// check if object type has setter or getter
	hasProp := func(name string) bool {
		prop := c.getPropertyOfObjectType(objectType, name)
		if prop != nil {
			s := c.getSingleCallSignature(c.getTypeOfSymbol(prop))
			return s != nil && c.getMinArgumentCount(s) >= 1 && c.isTypeAssignableTo(keyedType, c.getTypeAtPosition(s, 0))
		}
		return false
	}
	suggestedMethod := core.IfElse(ast.IsAssignmentTarget(expr), "set", "get")
	if !hasProp(suggestedMethod) {
		return ""
	}
	suggestion := tryGetPropertyAccessOrIdentifierToString(expr.Expression())
	if suggestion == "" {
		return suggestedMethod
	}
	return suggestion + "." + suggestedMethod
}

func (c *Checker) getSuggestedTypeForNonexistentStringLiteralType(source *Type, target *Type) *Type {
	candidates := core.FilterSeq(target.Types(), func(t *Type) bool { return t.flags&TypeFlagsStringLiteral != 0 })
	return core.GetSpellingSuggestion(getStringLiteralValue(source), candidates, getStringLiteralValue, CompareTypes)
}

func getIndexNodeForAccessExpression(accessNode *ast.Node) *ast.Node {
	switch accessNode.Kind {
	case ast.KindElementAccessExpression:
		return accessNode.AsElementAccessExpression().ArgumentExpression
	case ast.KindIndexedAccessType:
		return accessNode.AsIndexedAccessTypeNode().IndexType
	case ast.KindComputedPropertyName:
		return accessNode.Expression()
	}
	return accessNode
}

func (c *Checker) errorIfWritingToReadonlyIndex(indexInfo *IndexInfo, objectType *Type, accessExpression *ast.Node) {
	if indexInfo != nil && indexInfo.isReadonly && accessExpression != nil && (ast.IsAssignmentTarget(accessExpression) || isDeleteTarget(accessExpression)) {
		c.error(accessExpression, diagnostics.Index_signature_in_type_0_only_permits_reading, c.TypeToString(objectType))
	}
}

func (c *Checker) isSelfTypeAccess(name *ast.Node, parent *ast.Symbol) bool {
	return name.Kind == ast.KindThisKeyword || parent != nil && ast.IsEntityNameExpression(name) && parent == c.getResolvedSymbol(ast.GetFirstIdentifier(name))
}

func (c *Checker) isAssignmentToReadonlyEntity(expr *ast.Node, symbol *ast.Symbol, assignmentKind AssignmentKind) bool {
	if assignmentKind == AssignmentKindNone {
		// no assignment means it doesn't matter whether the entity is readonly
		return false
	}
	if ast.IsAccessExpression(expr) {
		node := ast.SkipParentheses(expr.Expression())
		if ast.IsIdentifier(node) {
			expressionSymbol := c.getResolvedSymbol(node)
			// CommonJS module.exports is never readonly
			if expressionSymbol.Flags&ast.SymbolFlagsModuleExports != 0 {
				return false
			}
		}
	}
	if c.isReadonlySymbol(symbol) {
		// Allow assignments to readonly properties within constructors of the same class declaration.
		if symbol.Flags&ast.SymbolFlagsProperty != 0 && ast.IsAccessExpression(expr) && expr.Expression().Kind == ast.KindThisKeyword {
			// Look for if this is the constructor for the class that `symbol` is a property of.
			ctor := c.getControlFlowContainer(expr)
			if ctor == nil || !ast.IsConstructorDeclaration(ctor) {
				return true
			}
			if symbol.ValueDeclaration != nil {
				isAssignmentDeclaration := ast.IsBinaryExpression(symbol.ValueDeclaration)
				isLocalPropertyDeclaration := ctor.Parent == symbol.ValueDeclaration.Parent
				isLocalParameterProperty := ctor == symbol.ValueDeclaration.Parent
				isLocalThisPropertyAssignment := isAssignmentDeclaration && symbol.Parent.ValueDeclaration == ctor.Parent
				isLocalThisPropertyAssignmentConstructorFunction := isAssignmentDeclaration && symbol.Parent.ValueDeclaration == ctor
				isWriteableSymbol := isLocalPropertyDeclaration || isLocalParameterProperty || isLocalThisPropertyAssignment || isLocalThisPropertyAssignmentConstructorFunction
				return !isWriteableSymbol
			}
		}
		return true
	}
	if ast.IsAccessExpression(expr) {
		// references through namespace import should be readonly
		node := ast.SkipParentheses(expr.Expression())
		if ast.IsIdentifier(node) {
			expressionSymbol := c.getResolvedSymbol(node)
			if expressionSymbol.Flags&ast.SymbolFlagsAlias != 0 {
				declaration := c.getDeclarationOfAliasSymbol(expressionSymbol)
				return declaration != nil && ast.IsNamespaceImport(declaration)
			}
		}
	}
	return false
}

func (c *Checker) isThisPropertyAccessInConstructor(node *ast.Node, prop *ast.Symbol) bool {
	var constructor *ast.Node
	if kind, location := c.isConstructorDeclaredThisProperty(prop); kind == thisAssignmentDeclarationConstructor {
		constructor = location
	} else if isThisProperty(node) && c.isAutoTypedProperty(prop) {
		constructor = c.getDeclaringConstructor(prop)
	}
	return ast.GetThisContainer(node, true /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/) == constructor
}

func (c *Checker) isAutoTypedProperty(symbol *ast.Symbol) bool {
	// A property is auto-typed when its declaration has no type annotation or initializer and we're in
	// noImplicitAny mode or a .js file.
	declaration := symbol.ValueDeclaration
	return declaration != nil && ast.IsPropertyDeclaration(declaration) && declaration.Type() == nil && declaration.Initializer() == nil && c.noImplicitAny
}

func (c *Checker) getDeclaringConstructor(symbol *ast.Symbol) *ast.Node {
	for _, declaration := range symbol.Declarations {
		container := ast.GetThisContainer(declaration, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/)
		if container != nil && ast.IsConstructorDeclaration(container) {
			return container
		}
	}
	return nil
}

func (c *Checker) getPropertyNameFromIndex(indexType *Type, accessNode *ast.Node) string {
	if isTypeUsableAsPropertyName(indexType) {
		return getPropertyNameFromType(indexType)
	}
	if accessNode != nil && ast.IsPropertyName(accessNode) {
		return ast.GetPropertyNameForPropertyNameNode(accessNode)
	}
	return ast.InternalSymbolNameMissing
}

func (c *Checker) isStringIndexSignatureOnlyTypeWorker(t *Type) bool {
	return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && len(c.getPropertiesOfType(t)) == 0 && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil ||
		t.flags&TypeFlagsUnionOrIntersection != 0 && core.Every(t.Types(), c.isStringIndexSignatureOnlyType)
}

func (c *Checker) shouldDeferIndexedAccessType(objectType *Type, indexType *Type, accessNode *ast.Node) bool {
	if c.isGenericIndexType(indexType) {
		return true
	}
	if accessNode != nil && !ast.IsIndexedAccessTypeNode(accessNode) {
		return c.isGenericTupleType(objectType) && !indexTypeLessThan(indexType, getTotalFixedElementCount(objectType.TargetTupleType()))
	}
	return c.isGenericObjectType(objectType) && !(isTupleType(objectType) && indexTypeLessThan(indexType, getTotalFixedElementCount(objectType.TargetTupleType()))) ||
		c.isGenericReducibleType(objectType)
}

func indexTypeLessThan(indexType *Type, limit int) bool {
	return everyType(indexType, func(t *Type) bool {
		if t.flags&TypeFlagsStringOrNumberLiteral != 0 {
			propName := getPropertyNameFromType(t)
			if isNumericLiteralName(propName) {
				index := jsnum.FromString(propName)
				return index >= 0 && index < jsnum.Number(limit)
			}
		}
		return false
	})
}

func (c *Checker) getNoInferType(t *Type) *Type {
	if c.isNoInferTargetType(t) {
		return c.getOrCreateSubstitutionType(t, c.unknownType)
	}
	return t
}

func (c *Checker) isNoInferTargetType(t *Type) bool {
	// This is effectively a more conservative and predictable form of couldContainTypeVariables. We want to
	// preserve NoInfer<T> only for types that could contain type variables, but we don't want to exhaustively
	// examine all object type members.
	return t.flags&TypeFlagsUnionOrIntersection != 0 && core.Some(t.AsUnionOrIntersectionType().types, c.isNoInferTargetType) ||
		t.flags&TypeFlagsSubstitution != 0 && !c.isNoInferType(t) && c.isNoInferTargetType(t.AsSubstitutionType().baseType) ||
		t.flags&TypeFlagsObject != 0 && !c.IsEmptyAnonymousObjectType(t) ||
		t.flags&(TypeFlagsInstantiable & ^TypeFlagsSubstitution) != 0 && !c.isPatternLiteralType(t)
}

func (c *Checker) getSubstitutionType(baseType *Type, constraint *Type) *Type {
	if constraint.flags&TypeFlagsAnyOrUnknown != 0 || constraint == baseType || baseType.flags&TypeFlagsAny != 0 {
		return baseType
	}
	return c.getOrCreateSubstitutionType(baseType, constraint)
}

func (c *Checker) getOrCreateSubstitutionType(baseType *Type, constraint *Type) *Type {
	key := SubstitutionTypeKey{baseId: baseType.id, constraintId: constraint.id}
	if cached := c.substitutionTypes[key]; cached != nil {
		return cached
	}
	result := c.newSubstitutionType(baseType, constraint)
	c.substitutionTypes[key] = result
	return result
}

func (c *Checker) getBaseConstraintOrType(t *Type) *Type {
	constraint := c.getBaseConstraintOfType(t)
	if constraint != nil {
		return constraint
	}
	return t
}

func (c *Checker) getBaseConstraintOfType(t *Type) *Type {
	if t.flags&(TypeFlagsInstantiableNonPrimitive|TypeFlagsUnionOrIntersection|TypeFlagsTemplateLiteral|TypeFlagsStringMapping|TypeFlagsIndex) != 0 || c.isGenericTupleType(t) {
		constraint := c.getResolvedBaseConstraint(t, nil)
		if constraint != c.noConstraintType && constraint != c.circularConstraintType {
			return constraint
		}
		return nil
	}
	return nil
}

func (c *Checker) getResolvedBaseConstraint(t *Type, stack []RecursionId) *Type {
	constrained := t.AsConstrainedType()
	if constrained == nil {
		return t
	}
	if constrained.resolvedBaseConstraint != nil {
		return constrained.resolvedBaseConstraint
	}
	if !c.pushTypeResolution(t, TypeSystemPropertyNameResolvedBaseConstraint) {
		return c.circularConstraintType
	}
	var constraint *Type
	// We always explore at least 10 levels of nested constraints. Thereafter, we continue to explore
	// up to 50 levels of nested constraints provided there are no "deeply nested" types on the stack
	// (i.e. no types for which five instantiations have been recorded on the stack). If we reach 50
	// levels of nesting, we are presumably exploring a repeating pattern with a long cycle that hasn't
	// yet triggered the deeply nested limiter. We have no test cases that actually get to 50 levels of
	// nesting, so it is effectively just a safety stop.
	identity := getRecursionIdentity(t)
	if len(stack) < 10 || len(stack) < 50 && !slices.Contains(stack, identity) {
		constraint = c.computeBaseConstraint(c.getSimplifiedType(t /*writing*/, false), append(stack, identity))
	}
	if !c.popTypeResolution() {
		if t.flags&TypeFlagsTypeParameter != 0 {
			errorNode := c.getConstraintDeclaration(t)
			if errorNode != nil {
				diagnostic := c.error(errorNode, diagnostics.Type_parameter_0_has_a_circular_constraint, c.TypeToString(t))
				if c.currentNode != nil && !isNodeDescendantOf(errorNode, c.currentNode) && !isNodeDescendantOf(c.currentNode, errorNode) {
					diagnostic.AddRelatedInfo(NewDiagnosticForNode(c.currentNode, diagnostics.Circularity_originates_in_type_at_this_location))
				}
			}
		}
		constraint = c.circularConstraintType
	}
	if constraint == nil {
		constraint = c.noConstraintType
	}
	if constrained.resolvedBaseConstraint == nil {
		constrained.resolvedBaseConstraint = constraint
	}
	return constraint
}

func (c *Checker) computeBaseConstraint(t *Type, stack []RecursionId) *Type {
	switch {
	case t.flags&TypeFlagsTypeParameter != 0:
		constraint := c.getConstraintFromTypeParameter(t)
		if t.AsTypeParameter().isThisType {
			return constraint
		}
		return c.getNextBaseConstraint(constraint, stack)
	case t.flags&TypeFlagsUnionOrIntersection != 0:
		types := t.Types()
		constraints := make([]*Type, 0, len(types))
		different := false
		for _, s := range types {
			constraint := c.getNextBaseConstraint(s, stack)
			if constraint != nil {
				if constraint != s {
					different = true
				}
				constraints = append(constraints, constraint)
			} else {
				different = true
			}
		}
		if !different {
			return t
		}
		switch {
		case t.flags&TypeFlagsUnion != 0 && len(constraints) == len(types):
			return c.getUnionType(constraints)
		case t.flags&TypeFlagsIntersection != 0 && len(constraints) != 0:
			return c.getIntersectionType(constraints)
		}
		return nil
	case t.flags&TypeFlagsIndex != 0:
		if c.isGenericMappedType(t.AsIndexType().target) {
			mappedType := t.AsIndexType().target
			if c.getNameTypeFromMappedType(mappedType) != nil && !c.isMappedTypeWithKeyofConstraintDeclaration(mappedType) {
				return c.getNextBaseConstraint(c.getIndexTypeForMappedType(mappedType, IndexFlagsNone), stack)
			}
		}
		return c.stringNumberSymbolType
	case t.flags&TypeFlagsTemplateLiteral != 0:
		types := t.Types()
		constraints := make([]*Type, 0, len(types))
		for _, s := range types {
			constraint := c.getNextBaseConstraint(s, stack)
			if constraint != nil {
				constraints = append(constraints, constraint)
			}
		}
		if len(constraints) == len(types) {
			return c.getTemplateLiteralType(t.AsTemplateLiteralType().texts, constraints)
		}
		return c.stringType
	case t.flags&TypeFlagsStringMapping != 0:
		constraint := c.getNextBaseConstraint(t.Target(), stack)
		if constraint != nil && constraint != t.Target() {
			return c.getStringMappingType(t.symbol, constraint)
		}
		return c.stringType
	case t.flags&TypeFlagsIndexedAccess != 0:
		if c.isMappedTypeGenericIndexedAccess(t) {
			// For indexed access types of the form { [P in K]: E }[X], where K is non-generic and X is generic,
			// we substitute an instantiation of E where P is replaced with X.
			return c.getNextBaseConstraint(c.substituteIndexedMappedType(t.AsIndexedAccessType().objectType, t.AsIndexedAccessType().indexType), stack)
		}
		baseObjectType := c.getNextBaseConstraint(t.AsIndexedAccessType().objectType, stack)
		baseIndexType := c.getNextBaseConstraint(t.AsIndexedAccessType().indexType, stack)
		if baseObjectType == nil || baseIndexType == nil {
			return nil
		}
		return c.getNextBaseConstraint(c.getIndexedAccessTypeOrUndefined(baseObjectType, baseIndexType, t.AsIndexedAccessType().accessFlags, nil, nil), stack)
	case t.flags&TypeFlagsConditional != 0:
		if c.conditionalConstraintDepth >= 100 {
			return nil
		}
		c.conditionalConstraintDepth++
		constraint := c.getConstraintFromConditionalType(t)
		c.conditionalConstraintDepth--
		return c.getNextBaseConstraint(constraint, stack)
	case t.flags&TypeFlagsSubstitution != 0:
		return c.getNextBaseConstraint(c.getSubstitutionIntersection(t), stack)
	case c.isGenericTupleType(t):
		// We substitute constraints for variadic elements only when the constraints are array types or
		// non-variadic tuple types as we want to avoid further (possibly unbounded) recursion.
		elementTypes := c.getElementTypes(t)
		elementInfos := t.TargetTupleType().elementInfos
		newElements := make([]*Type, 0, len(elementTypes))
		for i, v := range elementTypes {
			newElement := v
			if v.flags&TypeFlagsTypeParameter != 0 && elementInfos[i].flags&ElementFlagsVariadic != 0 {
				constraint := c.getNextBaseConstraint(v, stack)
				if constraint != nil && constraint != v && everyType(constraint, func(n *Type) bool { return c.isArrayOrTupleType(n) && !c.isGenericTupleType(n) }) {
					newElement = constraint
				}
			}
			newElements = append(newElements, newElement)
		}
		return c.createTupleTypeEx(newElements, elementInfos, t.TargetTupleType().readonly)
	}
	return t
}

func (c *Checker) getNextBaseConstraint(t *Type, stack []RecursionId) *Type {
	if t == nil {
		return nil
	}
	constraint := c.getResolvedBaseConstraint(t, stack)
	if constraint == c.noConstraintType || constraint == c.circularConstraintType {
		return nil
	}
	return constraint
}

// Return true if type might be of the given kind. A union or intersection type might be of a given
// kind if at least one constituent type is of the given kind.
func (c *Checker) maybeTypeOfKind(t *Type, kind TypeFlags) bool {
	if t.flags&kind != 0 {
		return true
	}
	if t.flags&TypeFlagsUnionOrIntersection != 0 {
		for _, t := range t.Types() {
			if c.maybeTypeOfKind(t, kind) {
				return true
			}
		}
	}
	return false
}

func (c *Checker) maybeTypeOfKindConsideringBaseConstraint(t *Type, kind TypeFlags) bool {
	if c.maybeTypeOfKind(t, kind) {
		return true
	}
	baseConstraint := c.getBaseConstraintOrType(t)
	return baseConstraint != nil && c.maybeTypeOfKind(baseConstraint, kind)
}

func (c *Checker) allTypesAssignableToKind(source *Type, kind TypeFlags) bool {
	return c.allTypesAssignableToKindEx(source, kind, false)
}

func (c *Checker) allTypesAssignableToKindEx(source *Type, kind TypeFlags, strict bool) bool {
	if source.flags&TypeFlagsUnion != 0 {
		return core.Every(source.Types(), func(subType *Type) bool {
			return c.allTypesAssignableToKindEx(subType, kind, strict)
		})
	}
	return c.isTypeAssignableToKindEx(source, kind, strict)
}

func (c *Checker) isTypeAssignableToKind(source *Type, kind TypeFlags) bool {
	return c.isTypeAssignableToKindEx(source, kind, false)
}

func (c *Checker) isTypeAssignableToKindEx(source *Type, kind TypeFlags, strict bool) bool {
	if source.flags&kind != 0 {
		return true
	}
	if strict && source.flags&(TypeFlagsAnyOrUnknown|TypeFlagsVoid|TypeFlagsUndefined|TypeFlagsNull) != 0 {
		return false
	}
	return kind&TypeFlagsNumberLike != 0 && c.isTypeAssignableTo(source, c.numberType) ||
		kind&TypeFlagsBigIntLike != 0 && c.isTypeAssignableTo(source, c.bigintType) ||
		kind&TypeFlagsStringLike != 0 && c.isTypeAssignableTo(source, c.stringType) ||
		kind&TypeFlagsBooleanLike != 0 && c.isTypeAssignableTo(source, c.booleanType) ||
		kind&TypeFlagsVoid != 0 && c.isTypeAssignableTo(source, c.voidType) ||
		kind&TypeFlagsNever != 0 && c.isTypeAssignableTo(source, c.neverType) ||
		kind&TypeFlagsNull != 0 && c.isTypeAssignableTo(source, c.nullType) ||
		kind&TypeFlagsUndefined != 0 && c.isTypeAssignableTo(source, c.undefinedType) ||
		kind&TypeFlagsESSymbol != 0 && c.isTypeAssignableTo(source, c.esSymbolType) ||
		kind&TypeFlagsNonPrimitive != 0 && c.isTypeAssignableTo(source, c.nonPrimitiveType)
}

func isConstEnumObjectType(t *Type) bool {
	return t.objectFlags&ObjectFlagsAnonymous != 0 && t.symbol != nil && isConstEnumSymbol(t.symbol)
}

func isConstEnumSymbol(symbol *ast.Symbol) bool {
	return symbol.Flags&ast.SymbolFlagsConstEnum != 0
}

func (c *Checker) compareProperties(sourceProp *ast.Symbol, targetProp *ast.Symbol, compareTypes func(source *Type, target *Type) Ternary) Ternary {
	// Two members are considered identical when
	// - they are public properties with identical names, optionality, and types,
	// - they are private or protected properties originating in the same declaration and having identical types
	if sourceProp == targetProp {
		return TernaryTrue
	}
	sourcePropAccessibility := getDeclarationModifierFlagsFromSymbol(sourceProp) & ast.ModifierFlagsNonPublicAccessibilityModifier
	targetPropAccessibility := getDeclarationModifierFlagsFromSymbol(targetProp) & ast.ModifierFlagsNonPublicAccessibilityModifier
	if sourcePropAccessibility != targetPropAccessibility {
		return TernaryFalse
	}
	if sourcePropAccessibility != ast.ModifierFlagsNone {
		if c.getTargetSymbol(sourceProp) != c.getTargetSymbol(targetProp) {
			return TernaryFalse
		}
	} else {
		if (sourceProp.Flags & ast.SymbolFlagsOptional) != (targetProp.Flags & ast.SymbolFlagsOptional) {
			return TernaryFalse
		}
	}
	if c.isReadonlySymbol(sourceProp) != c.isReadonlySymbol(targetProp) {
		return TernaryFalse
	}
	return compareTypes(c.getNonMissingTypeOfSymbol(sourceProp), c.getNonMissingTypeOfSymbol(targetProp))
}

func compareTypesEqual(s *Type, t *Type) Ternary {
	if s == t {
		return TernaryTrue
	}
	return TernaryFalse
}

func (c *Checker) markPropertyAsReferenced(prop *ast.Symbol, nodeForCheckWriteOnly *ast.Node, isSelfTypeAccess bool) {
	if prop.Flags&ast.SymbolFlagsClassMember == 0 || prop.ValueDeclaration == nil {
		return
	}
	hasPrivateModifier := ast.HasModifier(prop.ValueDeclaration, ast.ModifierFlagsPrivate)
	hasPrivateIdentifier := prop.ValueDeclaration.Name() != nil && ast.IsPrivateIdentifier(prop.ValueDeclaration.Name())
	if !hasPrivateModifier && !hasPrivateIdentifier {
		return
	}
	if nodeForCheckWriteOnly != nil && ast.IsWriteOnlyAccess(nodeForCheckWriteOnly) && prop.Flags&ast.SymbolFlagsSetAccessor == 0 {
		return
	}
	if isSelfTypeAccess {
		// Find any FunctionLikeDeclaration because those create a new 'this' binding. But this should only matter for methods (or getters/setters).
		containingMethod := ast.FindAncestor(nodeForCheckWriteOnly, ast.IsFunctionLikeDeclaration)
		if containingMethod != nil && containingMethod.Symbol() == prop {
			return
		}
	}
	target := prop
	if prop.CheckFlags&ast.CheckFlagsInstantiated != 0 {
		target = c.valueSymbolLinks.Get(prop).target
	}
	c.symbolReferenceLinks.Get(target).referenceKinds |= ast.SymbolFlagsAll
}

func (c *Checker) expandSignatureParametersWithTupleMembers(signature *Signature, restType *TypeReference, restIndex int, restSymbol *ast.Symbol) []*ast.Symbol {
	elementTypes := c.getTypeArguments(restType.AsType())
	elementInfos := restType.TargetTupleType().elementInfos
	associatedNames := c.getUniqAssociatedNamesFromTupleType(restType, restSymbol)
	expanded := append(make([]*ast.Symbol, 0, restIndex+len(elementTypes)), signature.parameters[:restIndex]...)
	for i, t := range elementTypes {
		flags := elementInfos[i].flags
		checkFlags := ast.CheckFlagsNone
		switch {
		case flags&ElementFlagsVariable != 0:
			checkFlags = ast.CheckFlagsRestParameter
		case flags&ElementFlagsOptional != 0:
			checkFlags = ast.CheckFlagsOptionalParameter
		}
		symbol := c.newSymbolEx(ast.SymbolFlagsFunctionScopedVariable, associatedNames[i], checkFlags)
		links := c.valueSymbolLinks.Get(symbol)
		if flags&ElementFlagsRest != 0 {
			links.resolvedType = c.createArrayType(t)
		} else {
			links.resolvedType = t
		}
		expanded = append(expanded, symbol)
	}
	return expanded
}

func (c *Checker) getUniqAssociatedNamesFromTupleType(t *TypeReference, restSymbol *ast.Symbol) []string {
	elementInfos := t.TargetTupleType().elementInfos
	names := make([]string, len(elementInfos))
	counters := make(map[string]int)
	for i, info := range elementInfos {
		names[i] = c.getTupleElementLabel(info, restSymbol, i)
		// count duplicates using negative values
		counters[names[i]]--
	}
	for i, name := range names {
		if counters[name] == -1 {
			continue
		}
		for {
			if counters[name] < 0 {
				// switch to a positive suffix counter
				counters[name] = 0
			}
			counters[name]++
			candidateName := name + "_" + strconv.Itoa(counters[name])
			if counters[candidateName] == 0 {
				names[i] = candidateName
				break
			}
		}
	}
	return names
}

func hasRestParameter(signature *ast.Node) bool {
	last := core.LastOrNil(signature.Parameters())
	return last != nil && isRestParameter(last)
}

func isRestParameter(param *ast.Node) bool {
	return param.AsParameterDeclaration().DotDotDotToken != nil
}

func getNameFromIndexInfo(info *IndexInfo) string {
	if info.declaration != nil {
		return scanner.DeclarationNameToString(info.declaration.Parameters()[0].Name())
	}
	return "x"
}

func (c *Checker) isUnknownLikeUnionType(t *Type) bool {
	if c.strictNullChecks && t.flags&TypeFlagsUnion != 0 {
		if t.objectFlags&ObjectFlagsIsUnknownLikeUnionComputed == 0 {
			t.objectFlags |= ObjectFlagsIsUnknownLikeUnionComputed
			types := t.Types()
			if len(types) >= 3 && types[0].flags&TypeFlagsUndefined != 0 && types[1].flags&TypeFlagsNull != 0 && core.Some(types, c.IsEmptyAnonymousObjectType) {
				t.objectFlags |= ObjectFlagsIsUnknownLikeUnion
			}
		}
		return t.objectFlags&ObjectFlagsIsUnknownLikeUnion != 0
	}
	return false
}

func (c *Checker) containsUndefinedType(t *Type) bool {
	if t.flags&TypeFlagsUnion != 0 {
		t = t.Types()[0]
	}
	return t.flags&TypeFlagsUndefined != 0
}

func (c *Checker) typeHasCallOrConstructSignatures(t *Type) bool {
	return t.flags&TypeFlagsStructuredType != 0 && len(c.resolveStructuredTypeMembers(t).signatures) != 0
}

func (c *Checker) getNormalizedType(t *Type, writing bool) *Type {
	for {
		var n *Type
		switch {
		case isFreshLiteralType(t):
			n = t.AsLiteralType().regularType
		case c.isGenericTupleType(t):
			n = c.getNormalizedTupleType(t, writing)
		case t.objectFlags&ObjectFlagsReference != 0:
			if t.AsTypeReference().node != nil {
				n = c.createTypeReference(t.Target(), c.getTypeArguments(t))
			} else {
				n = c.getSingleBaseForNonAugmentingSubtype(t)
				if n == nil {
					n = t
				}
			}
		case t.flags&TypeFlagsUnionOrIntersection != 0:
			n = c.getNormalizedUnionOrIntersectionType(t, writing)
		case t.flags&TypeFlagsSubstitution != 0:
			if writing {
				n = t.AsSubstitutionType().baseType
			} else {
				n = c.getSubstitutionIntersection(t)
			}
		case t.flags&TypeFlagsSimplifiable != 0:
			n = c.getSimplifiedType(t, writing)
		default:
			return t
		}
		if n == t {
			return n
		}
		t = n
	}
}

func (c *Checker) getSimplifiedType(t *Type, writing bool) *Type {
	switch {
	case t.flags&TypeFlagsIndexedAccess != 0:
		return c.getSimplifiedIndexedAccessType(t, writing)
	case t.flags&TypeFlagsConditional != 0:
		return c.getSimplifiedConditionalType(t, writing)
	}
	return t
}

// Transform an indexed access to a simpler form, if possible. Return the simpler form, or return
// the type itself if no transformation is possible. The writing flag indicates that the type is
// the target of an assignment.
func (c *Checker) getSimplifiedIndexedAccessType(t *Type, writing bool) *Type {
	key := CachedTypeKey{kind: core.IfElse(writing, CachedTypeKindIndexedAccessForWriting, CachedTypeKindIndexedAccessForReading), typeId: t.id}
	if cached := c.cachedTypes[key]; cached != nil {
		return core.IfElse(cached == c.circularConstraintType, t, cached)
	}
	c.cachedTypes[key] = t
	result := c.getSimplifiedIndexedAccessTypeWorker(t, writing)
	if result != t {
		// If the simplification is a union type that includes t, remove t from the type.
		result = c.removeType(result, t)
		c.cachedTypes[key] = result
	}
	return result
}

func (c *Checker) getSimplifiedIndexedAccessTypeWorker(t *Type, writing bool) *Type {
	// We recursively simplify the object type as it may in turn be an indexed access type. For example, with
	// '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type.
	objectType := c.getSimplifiedType(t.AsIndexedAccessType().objectType, writing)
	indexType := c.getSimplifiedType(t.AsIndexedAccessType().indexType, writing)
	// T[A | B] -> T[A] | T[B] (reading)
	// T[A | B] -> T[A] & T[B] (writing)
	distributedOverIndex := c.distributeObjectOverIndexType(objectType, indexType, writing)
	if distributedOverIndex != nil {
		return distributedOverIndex
	}
	// Only do the inner distributions if the index can no longer be instantiated to cause index distribution again
	if indexType.flags&TypeFlagsInstantiable == 0 {
		// (T | U)[K] -> T[K] | U[K] (reading)
		// (T | U)[K] -> T[K] & U[K] (writing)
		// (T & U)[K] -> T[K] & U[K]
		distributedOverObject := c.distributeIndexOverObjectType(objectType, indexType, writing)
		if distributedOverObject != nil {
			return distributedOverObject
		}
	}
	// So ultimately (reading):
	// ((A & B) | C)[K1 | K2] -> ((A & B) | C)[K1] | ((A & B) | C)[K2] -> (A & B)[K1] | C[K1] | (A & B)[K2] | C[K2] -> (A[K1] & B[K1]) | C[K1] | (A[K2] & B[K2]) | C[K2]
	// A generic tuple type indexed by a number exists only when the index type doesn't select a
	// fixed element. We simplify to either the combined type of all elements (when the index type
	// the actual number type) or to the combined type of all non-fixed elements.
	if c.isGenericTupleType(objectType) && indexType.flags&TypeFlagsNumberLike != 0 {
		elementType := c.getElementTypeOfSliceOfTupleType(objectType, core.IfElse(indexType.flags&TypeFlagsNumber != 0, 0, objectType.TargetTupleType().fixedLength), 0 /*endSkipCount*/, writing, false)
		if elementType != nil {
			return elementType
		}
	}
	// If the object type is a mapped type { [P in K]: E }, where K is generic, or { [P in K as N]: E }, where
	// K is generic and N is assignable to P, instantiate E using a mapper that substitutes the index type for P.
	// For example, for an index access { [P in K]: Box<T[P]> }[X], we construct the type Box<T[X]>.
	if c.isGenericMappedType(objectType) {
		if c.getMappedTypeNameTypeKind(objectType) != MappedTypeNameTypeKindRemapping {
			return c.mapType(c.substituteIndexedMappedType(objectType, t.AsIndexedAccessType().indexType), func(t *Type) *Type {
				return c.getSimplifiedType(t, writing)
			})
		}
	}
	return t
}

func (c *Checker) distributeObjectOverIndexType(objectType *Type, indexType *Type, writing bool) *Type {
	// T[A | B] -> T[A] | T[B] (reading)
	// T[A | B] -> T[A] & T[B] (writing)
	if indexType.flags&TypeFlagsUnion != 0 {
		types := core.Map(indexType.Types(), func(t *Type) *Type {
			return c.getSimplifiedType(c.getIndexedAccessType(objectType, t), writing)
		})
		if writing {
			return c.getIntersectionType(types)
		}
		return c.getUnionType(types)
	}
	return nil
}

func (c *Checker) distributeIndexOverObjectType(objectType *Type, indexType *Type, writing bool) *Type {
	// (T | U)[K] -> T[K] | U[K] (reading)
	// (T | U)[K] -> T[K] & U[K] (writing)
	// (T & U)[K] -> T[K] & U[K]
	if objectType.flags&TypeFlagsUnion != 0 || objectType.flags&TypeFlagsIntersection != 0 && !c.shouldDeferIndexType(objectType, IndexFlagsNone) {
		types := core.Map(objectType.Types(), func(t *Type) *Type {
			return c.getSimplifiedType(c.getIndexedAccessType(t, indexType), writing)
		})
		if objectType.flags&TypeFlagsIntersection != 0 || writing {
			return c.getIntersectionType(types)
		}
		return c.getUnionType(types)
	}
	return nil
}

func (c *Checker) getSimplifiedConditionalType(t *Type, writing bool) *Type {
	checkType := t.AsConditionalType().checkType
	extendsType := t.AsConditionalType().extendsType
	trueType := c.getTrueTypeFromConditionalType(t)
	falseType := c.getFalseTypeFromConditionalType(t)
	// Simplifications for types of the form `T extends U ? T : never` and `T extends U ? never : T`.
	if falseType.flags&TypeFlagsNever != 0 && c.getActualTypeVariable(trueType) == c.getActualTypeVariable(checkType) {
		if checkType.flags&TypeFlagsAny != 0 || c.isTypeAssignableTo(c.getRestrictiveInstantiation(checkType), c.getRestrictiveInstantiation(extendsType)) {
			return c.getSimplifiedType(trueType, writing)
		} else if c.isIntersectionEmpty(checkType, extendsType) {
			return c.neverType
		}
	} else if trueType.flags&TypeFlagsNever != 0 && c.getActualTypeVariable(falseType) == c.getActualTypeVariable(checkType) {
		if checkType.flags&TypeFlagsAny == 0 && c.isTypeAssignableTo(c.getRestrictiveInstantiation(checkType), c.getRestrictiveInstantiation(extendsType)) {
			return c.neverType
		} else if checkType.flags&TypeFlagsAny != 0 || c.isIntersectionEmpty(checkType, extendsType) {
			return c.getSimplifiedType(falseType, writing)
		}
	}
	return t
}

// Invokes union simplification logic to determine if an intersection is considered empty as a union constituent
func (c *Checker) isIntersectionEmpty(type1 *Type, type2 *Type) bool {
	return c.getUnionType([]*Type{c.intersectTypes(type1, type2), c.neverType}).flags&TypeFlagsNever != 0
}

func (c *Checker) getSimplifiedTypeOrConstraint(t *Type) *Type {
	if simplified := c.getSimplifiedType(t, false /*writing*/); simplified != t {
		return simplified
	}
	return c.getConstraintOfType(t)
}

func (c *Checker) getNormalizedUnionOrIntersectionType(t *Type, writing bool) *Type {
	if reduced := c.getReducedType(t); reduced != t {
		return reduced
	}
	if t.flags&TypeFlagsIntersection != 0 && c.shouldNormalizeIntersection(t) {
		// Normalization handles cases like
		// Partial<T>[K] & ({} | null) ==>
		// Partial<T>[K] & {} | Partial<T>[K} & null ==>
		// (T[K] | undefined) & {} | (T[K] | undefined) & null ==>
		// T[K] & {} | undefined & {} | T[K] & null | undefined & null ==>
		// T[K] & {} | T[K] & null
		types := t.Types()
		normalizedTypes := core.SameMap(types, func(u *Type) *Type { return c.getNormalizedType(u, writing) })
		if !core.Same(normalizedTypes, types) {
			return c.getIntersectionType(normalizedTypes)
		}
	}
	return t
}

func (c *Checker) shouldNormalizeIntersection(t *Type) bool {
	hasInstantiable := false
	hasNullableOrEmpty := false
	for _, t := range t.Types() {
		hasInstantiable = hasInstantiable || t.flags&TypeFlagsInstantiable != 0
		hasNullableOrEmpty = hasNullableOrEmpty || t.flags&TypeFlagsNullable != 0 || c.IsEmptyAnonymousObjectType(t)
		if hasInstantiable && hasNullableOrEmpty {
			return true
		}
	}
	return false
}

func (c *Checker) getNormalizedTupleType(t *Type, writing bool) *Type {
	elements := c.getElementTypes(t)
	normalizedElements := core.SameMap(elements, func(t *Type) *Type {
		if t.flags&TypeFlagsSimplifiable != 0 {
			return c.getSimplifiedType(t, writing)
		}
		return t
	})
	if !core.Same(elements, normalizedElements) {
		return c.createNormalizedTupleType(t.Target(), normalizedElements)
	}
	return t
}

func (c *Checker) getSingleBaseForNonAugmentingSubtype(t *Type) *Type {
	if t.objectFlags&ObjectFlagsReference == 0 || t.Target().objectFlags&ObjectFlagsClassOrInterface == 0 {
		return nil
	}
	key := CachedTypeKey{kind: CachedTypeKindEquivalentBaseType, typeId: t.id}
	if t.objectFlags&ObjectFlagsIdenticalBaseTypeCalculated != 0 {
		return c.cachedTypes[key]
	}
	t.objectFlags |= ObjectFlagsIdenticalBaseTypeCalculated
	target := t.Target()
	if target.objectFlags&ObjectFlagsClass != 0 {
		baseTypeNode := getBaseTypeNodeOfClass(target)
		// A base type expression may circularly reference the class itself (e.g. as an argument to function call), so we only
		// check for base types specified as simple qualified names.
		if baseTypeNode != nil && !ast.IsIdentifier(baseTypeNode.Expression()) && !ast.IsPropertyAccessExpression(baseTypeNode.Expression()) {
			return nil
		}
	}
	bases := c.getBaseTypes(target)
	if len(bases) != 1 {
		return nil
	}
	if len(c.getMembersOfSymbol(t.symbol)) != 0 {
		// If the interface has any members, they may subtype members in the base, so we should do a full structural comparison
		return nil
	}
	var instantiatedBase *Type
	typeParameters := target.AsInterfaceType().TypeParameters()
	if len(typeParameters) == 0 {
		instantiatedBase = bases[0]
	} else {
		instantiatedBase = c.instantiateType(bases[0], newTypeMapper(typeParameters, c.getTypeArguments(t)[:len(typeParameters)]))
	}
	if len(c.getTypeArguments(t)) > len(typeParameters) {
		instantiatedBase = c.getTypeWithThisArgument(instantiatedBase, core.LastOrNil(c.getTypeArguments(t)), false)
	}
	c.cachedTypes[key] = instantiatedBase
	return instantiatedBase
}

func (c *Checker) getModifiersTypeFromMappedType(t *Type) *Type {
	m := t.AsMappedType()
	if m.modifiersType == nil {
		if c.isMappedTypeWithKeyofConstraintDeclaration(t) {
			// If the constraint declaration is a 'keyof T' node, the modifiers type is T. We check
			// AST nodes here because, when T is a non-generic type, the logic below eagerly resolves
			// 'keyof T' to a literal union type and we can't recover T from that type.
			m.modifiersType = c.instantiateType(c.getTypeFromTypeNode(c.getConstraintDeclarationForMappedType(t).Type()), m.mapper)
		} else {
			// Otherwise, get the declared constraint type, and if the constraint type is a type parameter,
			// get the constraint of that type parameter. If the resulting type is an indexed type 'keyof T',
			// the modifiers type is T. Otherwise, the modifiers type is unknown.
			declaredType := c.getTypeFromMappedTypeNode(m.declaration.AsNode())
			constraint := c.getConstraintTypeFromMappedType(declaredType)
			extendedConstraint := constraint
			if constraint != nil && constraint.flags&TypeFlagsTypeParameter != 0 {
				extendedConstraint = c.getConstraintOfTypeParameter(constraint)
			}
			if extendedConstraint != nil && extendedConstraint.flags&TypeFlagsIndex != 0 {
				m.modifiersType = c.instantiateType(extendedConstraint.AsIndexType().target, m.mapper)
			} else {
				m.modifiersType = c.unknownType
			}
		}
	}
	return m.modifiersType
}

func (c *Checker) extractTypesOfKind(t *Type, kind TypeFlags) *Type {
	return c.filterType(t, func(t *Type) bool { return t.flags&kind != 0 })
}

func (c *Checker) getRegularTypeOfObjectLiteral(t *Type) *Type {
	if !(isObjectLiteralType(t) && t.objectFlags&ObjectFlagsFreshLiteral != 0) {
		return t
	}
	key := CachedTypeKey{kind: CachedTypeKindRegularObjectLiteral, typeId: t.id}
	if cached := c.cachedTypes[key]; cached != nil {
		return cached
	}
	resolved := c.resolveStructuredTypeMembers(t)
	members := c.transformTypeOfMembers(t, c.getRegularTypeOfObjectLiteral)
	regular := c.newAnonymousType(t.symbol, members, resolved.CallSignatures(), resolved.ConstructSignatures(), resolved.indexInfos)
	regular.flags = resolved.flags
	regular.objectFlags |= resolved.objectFlags & ^ObjectFlagsFreshLiteral
	c.cachedTypes[key] = regular
	return regular
}

func (c *Checker) transformTypeOfMembers(t *Type, f func(propertyType *Type) *Type) ast.SymbolTable {
	members := make(ast.SymbolTable)
	for _, property := range c.getPropertiesOfObjectType(t) {
		original := c.getTypeOfSymbol(property)
		updated := f(original)
		if updated != original {
			property = c.createSymbolWithType(property, updated)
		}
		members[property.Name] = property
	}
	return members
}

func (c *Checker) markLinkedReferences(location *ast.Node, hint ReferenceHint, propSymbol *ast.Symbol, parentType *Type) {
	if !c.canCollectSymbolAliasAccessibilityData {
		return
	}
	if location.Flags&ast.NodeFlagsAmbient != 0 && !ast.IsPropertySignatureDeclaration(location) && !ast.IsPropertyDeclaration(location) {
		// References within types and declaration files are never going to contribute to retaining a JS import,
		// except for properties (which can be decorated).
		return
	}
	switch hint {
	case ReferenceHintIdentifier:
		c.markIdentifierAliasReferenced(location)
	case ReferenceHintProperty:
		c.markPropertyAliasReferenced(location, propSymbol, parentType)
	case ReferenceHintExportAssignment:
		c.markExportAssignmentAliasReferenced(location)
	case ReferenceHintJsx:
		c.markJsxAliasReferenced(location)
	case ReferenceHintExportImportEquals:
		c.markImportEqualsAliasReferenced(location)
	case ReferenceHintExportSpecifier:
		c.markExportSpecifierAliasReferenced(location)
	case ReferenceHintDecorator:
		c.markDecoratorAliasReferenced(location)
	case ReferenceHintUnspecified:
		if ast.IsJsxTagName(location) && isJsxIntrinsicTagName(location) {
			return // builtin JSX tag names aren't real type refs by most metrics, but are expressions, so must be filtered
		}
		if ast.FindAncestor(location, func(n *ast.Node) bool { return ast.IsMetaProperty(n) }) != nil {
			return // identifiers in meta properties shouldn't be resolved, but are expressions, so must be filtered
		}
		// Identifiers in expression contexts are emitted, so we need to follow their referenced aliases and mark them as used
		// Some non-expression identifiers are also treated as expression identifiers for this purpose, eg, `a` in `b = {a}` or `q` in `import r = q`
		// This is the exception, rather than the rule - most non-expression identifiers are declaration names.
		if ast.IsIdentifier(location) &&
			(ast.IsExpressionNode(location) ||
				ast.IsShorthandPropertyAssignment(location.Parent) ||
				(ast.IsImportEqualsDeclaration(location.Parent) &&
					location.Parent.AsImportEqualsDeclaration().ModuleReference == location)) &&
			shouldMarkIdentifierAliasReferenced(location) {
			if ast.IsPropertyAccessOrQualifiedName(location.Parent) {
				var left *ast.Node
				if ast.IsPropertyAccessExpression(location.Parent) {
					left = location.Parent.Expression()
				} else {
					left = location.Parent.AsQualifiedName().Left
				}
				if left != location {
					return // Only mark the LHS (the RHS is a property lookup)
				}
			}
			c.markIdentifierAliasReferenced(location)
			return
		}
		if ast.IsPropertyAccessOrQualifiedName(location) {
			topProp := location
			for ast.IsPropertyAccessOrQualifiedName(topProp) {
				if ast.IsPartOfTypeNode(topProp) {
					return
				}
				topProp = topProp.Parent
			}
			c.markPropertyAliasReferenced(location, nil /*propSymbol*/, nil /*parentType*/)
			return
		}
		if ast.IsExportAssignment(location) {
			c.markExportAssignmentAliasReferenced(location)
			return
		}
		if ast.IsJsxOpeningLikeElement(location) || ast.IsJsxOpeningFragment(location) {
			c.markJsxAliasReferenced(location)
			return
		}
		if ast.IsImportEqualsDeclaration(location) {
			if isInternalModuleImportEqualsDeclaration(location) || c.checkExternalImportOrExportDeclaration(location) {
				c.markImportEqualsAliasReferenced(location)
				return
			}
			return
		}
		if ast.IsExportSpecifier(location) {
			c.markExportSpecifierAliasReferenced(location)
			return
		}
		if !c.compilerOptions.EmitDecoratorMetadata.IsTrue() {
			return
		}
		if !ast.CanHaveDecorators(location) || !ast.HasDecorators(location) || location.Modifiers() == nil || !ast.NodeCanBeDecorated(c.legacyDecorators, location, location.Parent, location.Parent.Parent) {
			return
		}

		c.markDecoratorAliasReferenced(location)
		return
	default:
		panic("Unhandled reference hint")
	}
}

func isExportOrExportExpression(location *ast.Node) bool {
	return ast.FindAncestor(location, func(n *ast.Node) bool {
		parent := n.Parent
		if parent != nil {
			if ast.IsAnyExportAssignment(parent) {
				return parent.Expression() == n && ast.IsEntityNameExpression(n)
			}
			if ast.IsExportSpecifier(parent) {
				return parent.AsExportSpecifier().Name() == n || parent.PropertyName() == n
			}
		}
		return false
	}) != nil
}

func shouldMarkIdentifierAliasReferenced(node *ast.IdentifierNode) bool {
	parent := node.Parent
	if parent != nil {
		// A property access expression LHS? checkPropertyAccessExpression will handle that.
		if ast.IsPropertyAccessExpression(parent) && parent.Expression() == node {
			return false
		}
		// Next two check for an identifier inside a type only export.
		if ast.IsExportSpecifier(parent) && parent.IsTypeOnly() {
			return false
		}
		if parent.Parent != nil {
			greatGrandparent := parent.Parent.Parent
			if greatGrandparent != nil && ast.IsExportDeclaration(greatGrandparent) && greatGrandparent.IsTypeOnly() {
				return false
			}
		}
	}
	return true
}

func isInternalModuleImportEqualsDeclaration(node *ast.Node) bool {
	return node.Kind == ast.KindImportEqualsDeclaration &&
		node.AsImportEqualsDeclaration().ModuleReference.Kind != ast.KindExternalModuleReference
}

func (c *Checker) markIdentifierAliasReferenced(location *ast.IdentifierNode) {
	symbol := c.getResolvedSymbol(location)
	if symbol != nil && symbol != c.argumentsSymbol && symbol != c.unknownSymbol && !ast.IsThisInTypeQuery(location) {
		c.markAliasReferenced(symbol, location)
	}
}

func (c *Checker) markPropertyAliasReferenced(location *ast.Node /*PropertyAccessExpression | QualifiedName*/, propSymbol *ast.Symbol, parentType *Type) {
	if isPartOfImportEqualsModuleReference(location) {
		return
	}
	var left *ast.Node
	if ast.IsPropertyAccessExpression(location) {
		left = location.Expression()
	} else {
		left = location.AsQualifiedName().Left
	}
	if ast.IsThisIdentifier(left) || !ast.IsIdentifier(left) {
		return
	}
	parentSymbol := c.getResolvedSymbol(left)
	if parentSymbol == nil || parentSymbol == c.unknownSymbol {
		return
	}
	// In `Foo.Bar.Baz`, 'Foo' is not referenced if 'Bar' is a const enum or a module containing only const enums.
	// `Foo` is also not referenced in `enum FooCopy { Bar = Foo.Bar }`, because the enum member value gets inlined
	// here even if `Foo` is not a const enum.
	//
	// The exceptions are:
	//   1. if 'isolatedModules' is enabled, because the const enum value will not be inlined, and
	//   2. if 'preserveConstEnums' is enabled and the expression is itself an export, e.g. `export = Foo.Bar.Baz`.
	//
	// The property lookup is deferred as much as possible, in as many situations as possible, to avoid alias marking
	// pulling on types/symbols it doesn't strictly need to.
	if c.compilerOptions.GetIsolatedModules() || (c.compilerOptions.ShouldPreserveConstEnums() && isExportOrExportExpression(location)) {
		c.markAliasReferenced(parentSymbol, location)
		return
	}
	// Hereafter, this relies on type checking - but every check prior to this only used symbol information
	leftType := parentType
	if leftType == nil {
		leftType = c.checkExpressionCached(left)
	}
	if IsTypeAny(leftType) || leftType == c.silentNeverType {
		c.markAliasReferenced(parentSymbol, location)
		return
	}
	prop := propSymbol
	if prop == nil && parentType == nil {
		var right *ast.Node
		if ast.IsPropertyAccessExpression(location) {
			right = location.AsPropertyAccessExpression().Name()
		} else {
			right = location.AsQualifiedName().Right
		}
		var lexicallyScopedSymbol *ast.Symbol
		if ast.IsPrivateIdentifier(right) {
			lexicallyScopedSymbol = c.lookupSymbolForPrivateIdentifierDeclaration(right.Text(), right)
		}
		assignmentKind := getAssignmentTargetKind(location)
		var apparentType *Type
		if assignmentKind != AssignmentKindNone || c.isMethodAccessForCall(location) {
			apparentType = c.getApparentType(c.getWidenedType(leftType))
		} else {
			apparentType = c.getApparentType(leftType)
		}
		if ast.IsPrivateIdentifier(right) {
			if lexicallyScopedSymbol != nil {
				prop = c.getPrivateIdentifierPropertyOfType(apparentType, lexicallyScopedSymbol)
			}
		} else {
			prop = c.getPropertyOfType(apparentType, right.Text())
		}
	}
	if !(prop != nil && (isConstEnumOrConstEnumOnlyModule(prop) || prop.Flags&ast.SymbolFlagsEnumMember != 0 && location.Parent.Kind == ast.KindEnumMember)) {
		c.markAliasReferenced(parentSymbol, location)
	}
}

func isPartOfImportEqualsModuleReference(location *ast.Node) bool {
	importEquals := ast.FindAncestorKind(location, ast.KindImportEqualsDeclaration)
	if importEquals == nil {
		return false
	}
	for node := location; node != nil && node != importEquals; node = node.Parent {
		if node == importEquals.AsImportEqualsDeclaration().ModuleReference {
			return true
		}
	}
	return false
}

func (c *Checker) markExportAssignmentAliasReferenced(location *ast.Node /*ExportAssignment*/) {
	id := location.Expression()
	if ast.IsIdentifier(id) {
		sym := c.getExportSymbolOfValueSymbolIfExported(c.resolveEntityName(id, ast.SymbolFlagsAll, true /*ignoreErrors*/, true /*dontResolveAlias*/, location))
		if sym != nil {
			c.markAliasReferenced(sym, id)
		}
	}
}

func (c *Checker) markJsxAliasReferenced(node *ast.Node /*JsxOpeningLikeElement | JsxOpeningFragment*/) {
	if c.getJsxNamespaceContainerForImplicitImport(node) != nil {
		return
	}
	// The reactNamespace/jsxFactory's root symbol should be marked as 'used' so we don't incorrectly elide its import.
	// And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error.
	jsxFactoryRefErr := core.IfElse(c.compilerOptions.Jsx == core.JsxEmitReact, diagnostics.This_JSX_tag_requires_0_to_be_in_scope_but_it_could_not_be_found, nil)
	jsxFactoryNamespace := c.getJsxNamespace(node)
	jsxFactoryLocation := node
	if ast.IsJsxOpeningLikeElement(node) {
		jsxFactoryLocation = node.TagName()
	}
	shouldFactoryRefErr := c.compilerOptions.Jsx != core.JsxEmitPreserve && c.compilerOptions.Jsx != core.JsxEmitReactNative
	// #38720/60122, allow null as jsxFragmentFactory
	var jsxFactorySym *ast.Symbol
	if !(ast.IsJsxOpeningFragment(node) && jsxFactoryNamespace == "null") {
		flags := ast.SymbolFlagsValue
		if !shouldFactoryRefErr {
			flags &^= ast.SymbolFlagsEnum
		}
		jsxFactorySym = c.resolveName(jsxFactoryLocation, jsxFactoryNamespace, flags, jsxFactoryRefErr, true /*isUse*/, false /*excludeGlobals*/)
	}
	if jsxFactorySym != nil {
		// Mark local symbol as referenced here because it might not have been marked
		// if jsx emit was not jsxFactory as there wont be error being emitted
		c.symbolReferenced(jsxFactorySym, ast.SymbolFlagsAll)
		// If react/jsxFactory symbol is alias, mark it as referenced
		if c.canCollectSymbolAliasAccessibilityData && jsxFactorySym.Flags&ast.SymbolFlagsAlias != 0 && c.getTypeOnlyAliasDeclaration(jsxFactorySym) == nil {
			c.markAliasSymbolAsReferenced(jsxFactorySym)
		}
	}
	// if JsxFragment, additionally mark jsx pragma as referenced, since `getJsxNamespace` above would have resolved to only the fragment factory if they are distinct
	if ast.IsJsxOpeningFragment(node) {
		file := ast.GetSourceFileOfNode(node)
		entity := c.getJsxFactoryEntity(file.AsNode())
		if entity != nil {
			localJsxNamespace := ast.GetFirstIdentifier(entity).Text()
			flags := ast.SymbolFlagsValue
			if !shouldFactoryRefErr {
				flags &^= ast.SymbolFlagsEnum
			}
			c.resolveName(jsxFactoryLocation, localJsxNamespace, flags, jsxFactoryRefErr, true /*isUse*/, false /*excludeGlobals*/)
		}
	}
}

func (c *Checker) markImportEqualsAliasReferenced(location *ast.Node /*ImportEqualsDeclaration*/) {
	if ast.HasSyntacticModifier(location, ast.ModifierFlagsExport) {
		c.markExportAsReferenced(location)
	}
}

func (c *Checker) markExportSpecifierAliasReferenced(location *ast.ExportSpecifierNode) {
	if location.Parent.Parent.ModuleSpecifier() == nil && !location.IsTypeOnly() && !location.Parent.Parent.IsTypeOnly() {
		exportedName := location.PropertyNameOrName()
		if exportedName.Kind == ast.KindStringLiteral {
			return // Skip for invalid syntax like this: export { "x" }
		}
		symbol := c.resolveName(exportedName, exportedName.Text(), ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/)
		if symbol != nil && (symbol == c.undefinedSymbol || symbol == c.globalThisSymbol || symbol.Declarations != nil && ast.IsGlobalSourceFile(ast.GetDeclarationContainer(symbol.Declarations[0]))) {
			// Do nothing, non-local symbol
		} else {
			target := symbol
			if target != nil && target.Flags&ast.SymbolFlagsAlias != 0 {
				target = c.resolveAlias(target)
			}
			if target == nil || c.getSymbolFlags(target)&ast.SymbolFlagsValue != 0 {
				c.markExportAsReferenced(location)            // marks export as used
				c.markIdentifierAliasReferenced(exportedName) // marks target of export as used
			}
		}
	}
}

func (c *Checker) checkExternalEmitHelpers(location *ast.Node, helpers ExternalEmitHelpers) {
	if !c.compilerOptions.ImportHelpers.IsTrue() {
		return
	}
	sourceFile := ast.GetSourceFileOfNode(location)
	if !ast.IsEffectiveExternalModule(sourceFile, c.compilerOptions) || location.Flags&ast.NodeFlagsAmbient != 0 {
		return
	}
	helpersModule := c.resolveHelpersModule(sourceFile, location)
	if helpersModule == c.unknownSymbol {
		return
	}
	links := c.sourceFileLinks.Get(sourceFile)
	if links.requestedExternalEmitHelpers&helpers != helpers {
		uncheckedHelpers := helpers &^ links.requestedExternalEmitHelpers
		for helper := ExternalEmitHelpersFirstEmitHelper; helper <= ExternalEmitHelpersLastEmitHelper; helper <<= 1 {
			if uncheckedHelpers&helper == 0 {
				continue
			}
			for _, name := range c.getHelperNames(helper) {
				symbol := c.resolveSymbol(c.getSymbol(c.getExportsOfModule(helpersModule), name, ast.SymbolFlagsValue))
				if symbol == nil {
					c.error(location, diagnostics.This_syntax_requires_an_imported_helper_named_1_which_does_not_exist_in_0_Consider_upgrading_your_version_of_0, externalHelpersModuleNameText, name)
				} else if helper&ExternalEmitHelpersClassPrivateFieldGet != 0 {
					if !c.hasSignatureWithArityGreaterThan(symbol, 3) {
						c.error(location, diagnostics.This_syntax_requires_an_imported_helper_named_1_with_2_parameters_which_is_not_compatible_with_the_one_in_0_Consider_upgrading_your_version_of_0, externalHelpersModuleNameText, name, 4)
					}
				} else if helper&ExternalEmitHelpersClassPrivateFieldSet != 0 {
					if !c.hasSignatureWithArityGreaterThan(symbol, 4) {
						c.error(location, diagnostics.This_syntax_requires_an_imported_helper_named_1_with_2_parameters_which_is_not_compatible_with_the_one_in_0_Consider_upgrading_your_version_of_0, externalHelpersModuleNameText, name, 5)
					}
				}
			}
		}
	}
	links.requestedExternalEmitHelpers |= helpers
}

func (c *Checker) hasSignatureWithArityGreaterThan(symbol *ast.Symbol, arity int) bool {
	for _, signature := range c.getSignaturesOfSymbol(symbol) {
		if c.getParameterCount(signature) > arity {
			return true
		}
	}
	return false
}

func (c *Checker) getHelperNames(helper ExternalEmitHelpers) []string {
	switch helper {
	case ExternalEmitHelpersRest:
		return []string{"__rest"}
	case ExternalEmitHelpersDecorate:
		if c.legacyDecorators {
			return []string{"__decorate"}
		}
		return []string{"__esDecorate", "__runInitializers"}
	case ExternalEmitHelpersMetadata:
		return []string{"__metadata"}
	case ExternalEmitHelpersParam:
		return []string{"__param"}
	case ExternalEmitHelpersAwaiter:
		return []string{"__awaiter"}
	case ExternalEmitHelpersAwait:
		return []string{"__await"}
	case ExternalEmitHelpersAsyncGenerator:
		return []string{"__asyncGenerator"}
	case ExternalEmitHelpersAsyncDelegator:
		return []string{"__asyncDelegator"}
	case ExternalEmitHelpersAsyncValues:
		return []string{"__asyncValues"}
	case ExternalEmitHelpersExportStar:
		return []string{"__exportStar"}
	case ExternalEmitHelpersImportStar:
		return []string{"__importStar"}
	case ExternalEmitHelpersImportDefault:
		return []string{"__importDefault"}
	case ExternalEmitHelpersMakeTemplateObject:
		return []string{"__makeTemplateObject"}
	case ExternalEmitHelpersClassPrivateFieldGet:
		return []string{"__classPrivateFieldGet"}
	case ExternalEmitHelpersClassPrivateFieldSet:
		return []string{"__classPrivateFieldSet"}
	case ExternalEmitHelpersClassPrivateFieldIn:
		return []string{"__classPrivateFieldIn"}
	case ExternalEmitHelpersSetFunctionName:
		return []string{"__setFunctionName"}
	case ExternalEmitHelpersPropKey:
		return []string{"__propKey"}
	case ExternalEmitHelpersAddDisposableResourceAndDisposeResources:
		return []string{"__addDisposableResource", "__disposeResources"}
	case ExternalEmitHelpersRewriteRelativeImportExtension:
		return []string{"__rewriteRelativeImportExtension"}
	default:
		panic("Unrecognized helper")
	}
}

func (c *Checker) resolveHelpersModule(file *ast.SourceFile, errorNode *ast.Node) *ast.Symbol {
	links := c.sourceFileLinks.Get(file)
	if links.externalHelpersModule == nil {
		location := c.program.GetImportHelpersImportSpecifier(file.Path())
		helpersModule := c.resolveExternalModule(location, externalHelpersModuleNameText, diagnostics.This_syntax_requires_an_imported_helper_but_module_0_cannot_be_found, errorNode, false /*isForAugmentation*/)
		if helpersModule == nil {
			helpersModule = c.unknownSymbol
		}
		links.externalHelpersModule = helpersModule
	}
	return links.externalHelpersModule
}

func (c *Checker) markDecoratorAliasReferenced(node *ast.Node /*HasDecorators*/) {
	if c.compilerOptions.EmitDecoratorMetadata.IsFalseOrUnknown() {
		return
	}
	firstDecorator := core.FirstOrNil(node.Decorators())
	if firstDecorator == nil {
		return
	}

	c.checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpersMetadata)

	// we only need to perform these checks if we are emitting serialized type metadata for the target of a decorator.
	switch node.Kind {
	case ast.KindClassDeclaration:
		ctor := ast.GetFirstConstructorWithBody(node)
		if ctor != nil {
			for _, p := range ctor.Parameters() {
				c.markDecoratorMedataDataTypeNodeAsReferenced(c.getParameterTypeNodeForDecoratorCheck(p))
			}
		}
	case ast.KindGetAccessor, ast.KindSetAccessor:
		otherKind := ast.KindSetAccessor
		if node.Kind == ast.KindSetAccessor {
			otherKind = ast.KindGetAccessor
		}
		otherAccessor := ast.GetDeclarationOfKind(c.getSymbolOfDeclaration(node), otherKind)
		annotation := c.getAnnotatedAccessorTypeNode(node)
		if annotation == nil && otherAccessor != nil {
			annotation = c.getAnnotatedAccessorTypeNode(otherAccessor)
		}
		c.markDecoratorMedataDataTypeNodeAsReferenced(annotation)
	case ast.KindMethodDeclaration:
		for _, p := range node.Parameters() {
			c.markDecoratorMedataDataTypeNodeAsReferenced(c.getParameterTypeNodeForDecoratorCheck(p))
		}
		c.markDecoratorMedataDataTypeNodeAsReferenced(node.Type())
	case ast.KindPropertyDeclaration:
		c.markDecoratorMedataDataTypeNodeAsReferenced(node.Type())
	case ast.KindParameter:
		c.markDecoratorMedataDataTypeNodeAsReferenced(c.getParameterTypeNodeForDecoratorCheck(node))
		containingSignature := node.Parent
		for _, p := range containingSignature.Parameters() {
			c.markDecoratorMedataDataTypeNodeAsReferenced(c.getParameterTypeNodeForDecoratorCheck(p))
		}
		c.markDecoratorMedataDataTypeNodeAsReferenced(containingSignature.Type())
	}
}

func (c *Checker) getParameterTypeNodeForDecoratorCheck(node *ast.ParameterDeclarationNode) *ast.Node {
	typeNode := node.Type()
	if node.AsParameterDeclaration().DotDotDotToken != nil {
		return ast.GetRestParameterElementType(typeNode)
	}
	return typeNode
}

func (c *Checker) markDecoratorMedataDataTypeNodeAsReferenced(node *ast.Node /*TypeNode*/) {
	entityName := c.getEntityNameForDecoratorMetadata(node)
	if entityName != nil && ast.IsEntityName(entityName) {
		c.markEntityNameOrEntityExpressionAsReference(entityName, true)
	}
}

func (c *Checker) getEntityNameForDecoratorMetadata(node *ast.Node) *ast.Node {
	if node == nil {
		return node
	}
	switch node.Kind {
	case ast.KindIntersectionType:
		return c.getEntityNameForDecoratorMetadataFromTypeList(node.AsIntersectionTypeNode().Types.Nodes)
	case ast.KindUnionType:
		return c.getEntityNameForDecoratorMetadataFromTypeList(node.AsUnionTypeNode().Types.Nodes)
	case ast.KindConditionalType:
		return c.getEntityNameForDecoratorMetadataFromTypeList([]*ast.Node{node.AsConditionalTypeNode().TrueType, node.AsConditionalTypeNode().FalseType})
	case ast.KindParenthesizedType:
		return c.getEntityNameForDecoratorMetadata(node.AsParenthesizedTypeNode().Type)
	case ast.KindNamedTupleMember:
		return c.getEntityNameForDecoratorMetadata(node.AsNamedTupleMember().Type)
	case ast.KindTypeReference:
		return node.AsTypeReferenceNode().TypeName
	}
	return nil
}

func (c *Checker) getEntityNameForDecoratorMetadataFromTypeList(typeNodes []*ast.Node) *ast.Node {
	var commonEntityName *ast.Node
	for _, typeNode := range typeNodes {
		if typeNode.Kind == ast.KindNeverKeyword {
			continue // Always elide `never` from the union/intersection if possible
		}
		if !c.strictNullChecks && (typeNode.Kind == ast.KindLiteralType && typeNode.AsLiteralTypeNode().Literal.Kind == ast.KindNullKeyword || typeNode.Kind == ast.KindUndefinedKeyword) {
			continue // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks
		}
		individualEntityName := c.getEntityNameForDecoratorMetadata(typeNode)
		if individualEntityName == nil {
			// Individual is something like string number
			// So it would be serialized to either that type or object
			// Safe to return here
			return nil
		}

		if commonEntityName == nil {
			commonEntityName = individualEntityName
		} else {
			// Note this is in sync with the transformation that happens for type node.
			// Keep this in sync with serializeUnionOrIntersectionType
			// Verify if they refer to same entity and is identifier
			// return undefined if they dont match because we would emit object
			if !ast.IsIdentifier(commonEntityName) || !ast.IsIdentifier(individualEntityName) || commonEntityName.AsIdentifier().Text != individualEntityName.AsIdentifier().Text {
				return nil
			}
		}
	}
	return commonEntityName
}

func (c *Checker) markAliasReferenced(symbol *ast.Symbol, location *ast.Node) {
	if !c.canCollectSymbolAliasAccessibilityData {
		return
	}
	if ast.IsNonLocalAlias(symbol, ast.SymbolFlagsValue /*excludes*/) && !IsInTypeQuery(location) {
		target := c.resolveAlias(symbol)
		if c.getSymbolFlagsEx(symbol, true /*excludeTypeOnlyMeanings*/, false /*excludeLocalMeanings*/)&(ast.SymbolFlagsValue|ast.SymbolFlagsExportValue) != 0 {
			// An alias resolving to a const enum cannot be elided if (1) 'isolatedModules' is enabled
			// (because the const enum value will not be inlined), or if (2) the alias is an export
			// of a const enum declaration that will be preserved.
			if c.compilerOptions.GetIsolatedModules() ||
				c.compilerOptions.ShouldPreserveConstEnums() && isExportOrExportExpression(location) ||
				!isConstEnumOrConstEnumOnlyModule(c.getExportSymbolOfValueSymbolIfExported(target)) {
				c.markAliasSymbolAsReferenced(symbol)
			}
		}
	}
}

// When an alias symbol is referenced, we need to mark the entity it references as referenced and in turn repeat that until
// we reach a non-alias or an exported entity (which is always considered referenced). We do this by checking the target of
// the alias as an expression (which recursively takes us back here if the target references another alias).
func (c *Checker) markAliasSymbolAsReferenced(symbol *ast.Symbol) {
	links := c.aliasSymbolLinks.Get(symbol)
	if !links.referenced {
		links.referenced = true
		node := c.getDeclarationOfAliasSymbol(symbol)
		if node == nil {
			panic("Unexpected nil in markAliasSymbolAsReferenced")
		}
		// We defer checking of the reference of an `import =` until the import itself is referenced,
		// This way a chain of imports can be elided if ultimately the final input is only used in a type
		// position.
		if ast.IsImportEqualsDeclaration(node) && node.AsImportEqualsDeclaration().ModuleReference.Kind != ast.KindExternalModuleReference {
			if c.getSymbolFlags(c.resolveSymbol(symbol))&ast.SymbolFlagsValue != 0 {
				// import foo = <symbol>
				left := ast.GetFirstIdentifier(node.AsImportEqualsDeclaration().ModuleReference)
				c.markIdentifierAliasReferenced(left)
			}
		}
	}
}

func (c *Checker) markExportAsReferenced(node *ast.Node /*ImportEqualsDeclaration | ExportSpecifier*/) {
	symbol := c.getSymbolOfDeclaration(node)
	target := c.resolveAlias(symbol)
	if target != nil {
		markAlias := target == c.unknownSymbol ||
			((c.getSymbolFlagsEx(symbol, true /*excludeTypeOnlyMeanings*/, false /*excludeLocalMeanings*/)&ast.SymbolFlagsValue != 0) && !isConstEnumOrConstEnumOnlyModule(target))
		if markAlias {
			c.markAliasSymbolAsReferenced(symbol)
		}
	}
}

func (c *Checker) markEntityNameOrEntityExpressionAsReference(typeName *ast.Node /*EntityNameOrEntityNameExpression | nil*/, forDecoratorMetadata bool) {
	if typeName == nil {
		return
	}

	rootName := ast.GetFirstIdentifier(typeName)
	meaning := core.IfElse(typeName.Kind == ast.KindIdentifier, ast.SymbolFlagsType, ast.SymbolFlagsNamespace) | ast.SymbolFlagsAlias
	rootSymbol := c.resolveName(rootName, rootName.Text(), meaning, nil /*nameNotFoundMessage*/, true /*isUse*/, false /*excludeGlobals*/)

	if rootSymbol != nil && rootSymbol.Flags&ast.SymbolFlagsAlias != 0 {
		if c.canCollectSymbolAliasAccessibilityData &&
			c.symbolIsValue(rootSymbol) &&
			!isConstEnumOrConstEnumOnlyModule(c.resolveAlias(rootSymbol)) &&
			c.getTypeOnlyAliasDeclaration(rootSymbol) == nil {
			c.markAliasSymbolAsReferenced(rootSymbol)
		} else if forDecoratorMetadata &&
			c.compilerOptions.GetIsolatedModules() &&
			c.compilerOptions.GetEmitModuleKind() >= core.ModuleKindES2015 &&
			!c.symbolIsValue(rootSymbol) &&
			!core.Some(rootSymbol.Declarations, ast.IsTypeOnlyImportOrExportDeclaration) {
			diag := c.error(typeName, diagnostics.A_type_referenced_in_a_decorated_signature_must_be_imported_with_import_type_or_a_namespace_import_when_isolatedModules_and_emitDecoratorMetadata_are_enabled)
			var aliasDeclaration *ast.Node
			for _, decl := range rootSymbol.Declarations {
				if ast.IsAliasSymbolDeclaration(decl) {
					aliasDeclaration = decl
					break
				}
			}
			if aliasDeclaration != nil {
				diag.SetRelatedInfo([]*ast.Diagnostic{createDiagnosticForNode(aliasDeclaration, diagnostics.X_0_was_imported_here, rootName.Text())})
			}
		}
	}
}

func getEntityNameFromTypeNode(node *ast.TypeNode) *ast.Node {
	switch node.Kind {
	case ast.KindTypeReference:
		return node.AsTypeReferenceNode().TypeName

	case ast.KindExpressionWithTypeArguments:
		if ast.IsEntityNameExpression(node.Expression()) {
			return node.Expression()
		}
		return nil

	// These aren't valid TypeNodes, but we treat them as such because of `isPartOfTypeNode`, which returns `true` for things that aren't `TypeNode`s.
	case ast.KindIdentifier, ast.KindQualifiedName:
		return node
	}

	return nil
}

// If a TypeNode can be resolved to a value symbol imported from an external module, it is
// marked as referenced to prevent import elision.
func (c *Checker) markTypeNodeAsReferenced(node *ast.TypeNode) {
	if node != nil {
		c.markEntityNameOrEntityExpressionAsReference(getEntityNameFromTypeNode(node), false /*forDecoratorMetadata*/)
	}
}

func (c *Checker) GetPromisedTypeOfPromise(t *Type) *Type {
	return c.getPromisedTypeOfPromiseEx(t, nil, nil)
}

// Gets the "promised type" of a promise.
// @param type The type of the promise.
// @remarks The "promised type" of a type is the type of the "value" parameter of the "onfulfilled" callback.
func (c *Checker) getPromisedTypeOfPromiseEx(t *Type, errorNode *ast.Node, thisTypeForErrorOut **Type) *Type {
	//  { // type
	//      then( // thenFunction
	//          onfulfilled: ( // onfulfilledParameterType
	//              value: T // valueParameterType
	//          ) => any
	//      ): any;
	//  }
	if IsTypeAny(t) {
		return nil
	}
	key := CachedTypeKey{kind: CachedTypeKindPromisedTypeOfPromise, typeId: t.id}
	if cached := c.cachedTypes[key]; cached != nil {
		return cached
	}
	if c.isReferenceToType(t, c.getGlobalPromiseType()) {
		result := c.getTypeArguments(t)[0]
		c.cachedTypes[key] = result
		return result
	}
	// primitives with a `{ then() }` won't be unwrapped/adopted.
	if c.allTypesAssignableToKind(c.getBaseConstraintOrType(t), TypeFlagsPrimitive|TypeFlagsNever) {
		return nil
	}
	thenFunction := c.getTypeOfPropertyOfType(t, "then")
	// TODO: GH#18217
	if IsTypeAny(thenFunction) {
		return nil
	}
	var thenSignatures []*Signature
	if thenFunction != nil {
		thenSignatures = c.getSignaturesOfType(thenFunction, SignatureKindCall)
	}
	if len(thenSignatures) == 0 {
		if errorNode != nil {
			c.error(errorNode, diagnostics.A_promise_must_have_a_then_method)
		}
		return nil
	}
	var thisTypeForError *Type
	var candidates []*Signature
	for _, thenSignature := range thenSignatures {
		thisType := c.getThisTypeOfSignature(thenSignature)
		if thisType != nil && thisType != c.voidType && !c.isTypeRelatedTo(t, thisType, c.subtypeRelation) {
			thisTypeForError = thisType
		} else {
			candidates = append(candidates, thenSignature)
		}
	}
	if len(candidates) == 0 {
		debug.Assert(thisTypeForError != nil)
		if thisTypeForErrorOut != nil {
			*thisTypeForErrorOut = thisTypeForError
		}
		if errorNode != nil {
			c.error(errorNode, diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1, c.TypeToString(t), c.TypeToString(thisTypeForError))
		}
		return nil
	}
	onfulfilledParameterType := c.getTypeWithFacts(c.getUnionType(core.Map(candidates, c.getTypeOfFirstParameterOfSignature)), TypeFactsNEUndefinedOrNull)
	if IsTypeAny(onfulfilledParameterType) {
		return nil
	}
	onfulfilledParameterSignatures := c.getSignaturesOfType(onfulfilledParameterType, SignatureKindCall)
	if len(onfulfilledParameterSignatures) == 0 {
		if errorNode != nil {
			c.error(errorNode, diagnostics.The_first_parameter_of_the_then_method_of_a_promise_must_be_a_callback)
		}
		return nil
	}
	result := c.getUnionTypeEx(core.Map(onfulfilledParameterSignatures, c.getTypeOfFirstParameterOfSignature), UnionReductionSubtype, nil, nil)
	c.cachedTypes[key] = result
	return result
}

func (c *Checker) getTypeOfFirstParameterOfSignature(signature *Signature) *Type {
	return c.getTypeOfFirstParameterOfSignatureWithFallback(signature, c.neverType)
}

func (c *Checker) getTypeOfFirstParameterOfSignatureWithFallback(signature *Signature, fallbackType *Type) *Type {
	if len(signature.parameters) > 0 {
		return c.getTypeAtPosition(signature, 0)
	}
	return fallbackType
}

func getMappedTypeModifiers(t *Type) MappedTypeModifiers {
	declaration := t.AsMappedType().declaration
	var modifiers MappedTypeModifiers
	if declaration.ReadonlyToken != nil {
		modifiers |= core.IfElse(declaration.ReadonlyToken.Kind == ast.KindMinusToken, MappedTypeModifiersExcludeReadonly, MappedTypeModifiersIncludeReadonly)
	}
	if declaration.QuestionToken != nil {
		modifiers |= core.IfElse(declaration.QuestionToken.Kind == ast.KindMinusToken, MappedTypeModifiersExcludeOptional, MappedTypeModifiersIncludeOptional)
	}
	return modifiers
}

// Return -1, 0, or 1, where -1 means optionality is stripped (i.e. -?), 0 means optionality is unchanged, and 1 means
// optionality is added (i.e. +?).
func getMappedTypeOptionality(t *Type) int {
	modifiers := getMappedTypeModifiers(t)
	switch {
	case modifiers&MappedTypeModifiersExcludeOptional != 0:
		return -1
	case modifiers&MappedTypeModifiersIncludeOptional != 0:
		return 1
	}
	return 0
}

// Return -1, 0, or 1, for stripped, unchanged, or added optionality respectively. When a homomorphic mapped type doesn't
// modify optionality, recursively consult the optionality of the type being mapped over to see if it strips or adds optionality.
// For intersections, return -1 or 1 when all constituents strip or add optionality, otherwise return 0.
func (c *Checker) getCombinedMappedTypeOptionality(t *Type) int {
	if t.objectFlags&ObjectFlagsMapped != 0 {
		optionality := getMappedTypeOptionality(t)
		if optionality != 0 {
			return optionality
		}
		return c.getCombinedMappedTypeOptionality(c.getModifiersTypeFromMappedType(t))
	}
	if t.flags&TypeFlagsIntersection != 0 {
		optionality := c.getCombinedMappedTypeOptionality(t.Types()[0])
		for _, t := range t.Types()[1:] {
			if c.getCombinedMappedTypeOptionality(t) != optionality {
				return 0
			}
		}
		return optionality
	}
	return 0
}

func isPartialMappedType(t *Type) bool {
	return t.objectFlags&ObjectFlagsMapped != 0 && getMappedTypeModifiers(t)&MappedTypeModifiersIncludeOptional != 0
}

func (c *Checker) getOptionalExpressionType(exprType *Type, expression *ast.Node) *Type {
	switch {
	case ast.IsExpressionOfOptionalChainRoot(expression):
		return c.GetNonNullableType(exprType)
	case ast.IsOptionalChain(expression):
		return c.removeOptionalTypeMarker(exprType)
	default:
		return exprType
	}
}

func (c *Checker) removeOptionalTypeMarker(t *Type) *Type {
	if c.strictNullChecks {
		return c.removeType(t, c.optionalType)
	}
	return t
}

func (c *Checker) propagateOptionalTypeMarker(t *Type, node *ast.Node, wasOptional bool) *Type {
	if wasOptional {
		if ast.IsOutermostOptionalChain(node) {
			return c.getOptionalType(t, false)
		}
		return c.addOptionalTypeMarker(t)
	}
	return t
}

func (c *Checker) removeMissingType(t *Type, isOptional bool) *Type {
	if c.exactOptionalPropertyTypes && isOptional {
		return c.removeType(t, c.missingType)
	}
	return t
}

func (c *Checker) removeMissingOrUndefinedType(t *Type) *Type {
	if c.exactOptionalPropertyTypes {
		return c.removeType(t, c.missingType)
	}
	return c.getTypeWithFacts(t, TypeFactsNEUndefined)
}

func (c *Checker) removeDefinitelyFalsyTypes(t *Type) *Type {
	return c.filterType(t, func(t *Type) bool { return c.hasTypeFacts(t, TypeFactsTruthy) })
}

func (c *Checker) extractDefinitelyFalsyTypes(t *Type) *Type {
	return c.mapType(t, c.getDefinitelyFalsyPartOfType)
}

func (c *Checker) getDefinitelyFalsyPartOfType(t *Type) *Type {
	switch {
	case t.flags&TypeFlagsString != 0:
		return c.emptyStringType
	case t.flags&TypeFlagsNumber != 0:
		return c.zeroType
	case t.flags&TypeFlagsBigInt != 0:
		return c.zeroBigIntType
	case t == c.regularFalseType || t == c.falseType ||
		t.flags&(TypeFlagsVoid|TypeFlagsUndefined|TypeFlagsNull|TypeFlagsAnyOrUnknown) != 0 ||
		t.flags&TypeFlagsStringLiteral != 0 && getStringLiteralValue(t) == "" ||
		t.flags&TypeFlagsNumberLiteral != 0 && getNumberLiteralValue(t) == 0 ||
		t.flags&TypeFlagsBigIntLiteral != 0 && isZeroBigInt(t):
		return t
	}
	return c.neverType
}

func (c *Checker) getConstraintDeclaration(t *Type) *ast.Node {
	if t.symbol != nil {
		for _, d := range t.symbol.Declarations {
			if ast.IsTypeParameterDeclaration(d) {
				if constraint := d.AsTypeParameterDeclaration().Constraint; constraint != nil {
					return constraint
				}
			}
		}
	}
	return nil
}

func (c *Checker) getTemplateLiteralType(texts []string, types []*Type) *Type {
	unionIndex := core.FindIndex(types, func(t *Type) bool {
		return t.flags&(TypeFlagsNever|TypeFlagsUnion) != 0
	})
	if unionIndex >= 0 {
		if !c.checkCrossProductUnion(types) {
			return c.errorType
		}
		return c.mapType(types[unionIndex], func(t *Type) *Type {
			return c.getTemplateLiteralType(texts, core.ReplaceElement(types, unionIndex, t))
		})
	}
	if slices.Contains(types, c.wildcardType) {
		return c.wildcardType
	}
	var newTypes []*Type
	var newTexts []string
	var sb strings.Builder
	sb.WriteString(texts[0])
	var addSpans func([]string, []*Type) bool
	addSpans = func(texts []string, types []*Type) bool {
		for i, t := range types {
			switch {
			case t.flags&(TypeFlagsLiteral|TypeFlagsNull|TypeFlagsUndefined) != 0:
				sb.WriteString(c.getTemplateStringForType(t))
				sb.WriteString(texts[i+1])
			case t.flags&TypeFlagsTemplateLiteral != 0:
				sb.WriteString(t.AsTemplateLiteralType().texts[0])
				if !addSpans(t.AsTemplateLiteralType().texts, t.AsTemplateLiteralType().types) {
					return false
				}
				sb.WriteString(texts[i+1])
			case c.isGenericIndexType(t) || c.isPatternLiteralPlaceholderType(t):
				newTypes = append(newTypes, t)
				newTexts = append(newTexts, stringutil.CombineSurrogatePairs(sb.String()))
				sb.Reset()
				sb.WriteString(texts[i+1])
			default:
				return false
			}
		}
		return true
	}
	if !addSpans(texts, types) {
		return c.stringType
	}
	if len(newTypes) == 0 {
		return c.getStringLiteralType(stringutil.CombineSurrogatePairs(sb.String()))
	}
	newTexts = append(newTexts, stringutil.CombineSurrogatePairs(sb.String()))
	if core.Every(newTexts, func(t string) bool { return t == "" }) {
		if core.Every(newTypes, func(t *Type) bool { return t.flags&TypeFlagsString != 0 }) {
			return c.stringType
		}
		// Normalize `${Mapping<xxx>}` into Mapping<xxx>
		if len(newTypes) == 1 && c.isPatternLiteralType(newTypes[0]) {
			return newTypes[0]
		}
	}
	key := getTemplateTypeKey(newTexts, newTypes)
	t := c.templateLiteralTypes[key]
	if t == nil {
		t = c.newTemplateLiteralType(newTexts, newTypes)
		c.templateLiteralTypes[key] = t
	}
	return t
}

func (c *Checker) getTemplateStringForType(t *Type) string {
	switch {
	case t.flags&(TypeFlagsStringLiteral|TypeFlagsNumberLiteral|TypeFlagsBooleanLiteral|TypeFlagsBigIntLiteral) != 0:
		return evaluator.AnyToString(t.AsLiteralType().value)
	case t.flags&TypeFlagsNullable != 0:
		return t.AsIntrinsicType().intrinsicName
	}
	return ""
}

func (c *Checker) getStringMappingType(symbol *ast.Symbol, t *Type) *Type {
	switch {
	case t.flags&(TypeFlagsUnion|TypeFlagsNever) != 0:
		return c.mapType(t, func(t *Type) *Type { return c.getStringMappingType(symbol, t) })
	case t.flags&TypeFlagsStringLiteral != 0:
		return c.getStringLiteralType(applyStringMapping(symbol, getStringLiteralValue(t)))
	case t.flags&TypeFlagsTemplateLiteral != 0:
		return c.getTemplateLiteralType(c.applyTemplateStringMapping(symbol, t.AsTemplateLiteralType().texts, t.AsTemplateLiteralType().types))
	case t.flags&TypeFlagsStringMapping != 0 && symbol == t.symbol:
		return t
	case t.flags&(TypeFlagsAny|TypeFlagsString|TypeFlagsStringMapping) != 0 || c.isGenericIndexType(t):
		return c.getStringMappingTypeForGenericType(symbol, t)
	case c.isPatternLiteralPlaceholderType(t):
		return c.getStringMappingTypeForGenericType(symbol, c.getTemplateLiteralType([]string{"", ""}, []*Type{t}))
	default:
		return t
	}
}

func applyStringMapping(symbol *ast.Symbol, str string) string {
	switch intrinsicTypeKinds[symbol.Name] {
	case IntrinsicTypeKindUppercase:
		return stringutil.ToUpperJS(str)
	case IntrinsicTypeKindLowercase:
		return stringutil.ToLowerJS(str)
	case IntrinsicTypeKindCapitalize:
		_, size := stringutil.DecodeJSStringRune(str)
		return stringutil.ToUpperJS(str[:size]) + str[size:]
	case IntrinsicTypeKindUncapitalize:
		_, size := stringutil.DecodeJSStringRune(str)
		return stringutil.ToLowerJS(str[:size]) + str[size:]
	}
	return str
}

func (c *Checker) applyTemplateStringMapping(symbol *ast.Symbol, texts []string, types []*Type) ([]string, []*Type) {
	switch intrinsicTypeKinds[symbol.Name] {
	case IntrinsicTypeKindUppercase, IntrinsicTypeKindLowercase:
		return core.Map(texts, func(t string) string { return applyStringMapping(symbol, t) }),
			core.Map(types, func(t *Type) *Type { return c.getStringMappingType(symbol, t) })
	case IntrinsicTypeKindCapitalize, IntrinsicTypeKindUncapitalize:
		if texts[0] != "" {
			newTexts := slices.Clone(texts)
			newTexts[0] = applyStringMapping(symbol, newTexts[0])
			return newTexts, types
		}
		newTypes := slices.Clone(types)
		newTypes[0] = c.getStringMappingType(symbol, newTypes[0])
		return texts, newTypes
	}
	return texts, types
}

func (c *Checker) getStringMappingTypeForGenericType(symbol *ast.Symbol, t *Type) *Type {
	key := StringMappingKey{s: symbol, t: t}
	result := c.stringMappingTypes[key]
	if result == nil {
		result = c.newStringMappingType(symbol, t)
		c.stringMappingTypes[key] = result
	}
	return result
}

// Given an indexed access on a mapped type of the form { [P in K]: E }[X], return an instantiation of E where P is
// replaced with X. Since this simplification doesn't account for mapped type modifiers, add 'undefined' to the
// resulting type if the mapped type includes a '?' modifier or if the modifiers type indicates that some properties
// are optional. If the modifiers type is generic, conservatively estimate optionality by recursively looking for
// mapped types that include '?' modifiers.
func (c *Checker) substituteIndexedMappedType(objectType *Type, index *Type) *Type {
	mapper := newSimpleTypeMapper(c.getTypeParameterFromMappedType(objectType), index)
	templateMapper := c.combineTypeMappers(objectType.AsMappedType().mapper, mapper)
	instantiatedTemplateType := c.instantiateType(c.getTemplateTypeFromMappedType(core.OrElse(objectType.AsMappedType().target, objectType)), templateMapper)
	isOptional := getMappedTypeOptionality(objectType) > 0
	if !isOptional {
		if c.isGenericType(objectType) {
			isOptional = c.getCombinedMappedTypeOptionality(c.getModifiersTypeFromMappedType(objectType)) > 0
		} else {
			isOptional = c.couldAccessOptionalProperty(objectType, index)
		}
	}
	return c.addOptionalityEx(instantiatedTemplateType, true /*isProperty*/, isOptional)
}

// Return true if an indexed access with the given object and index types could access an optional property.
func (c *Checker) couldAccessOptionalProperty(objectType *Type, indexType *Type) bool {
	indexConstraint := c.getBaseConstraintOfType(indexType)
	return indexConstraint != nil && core.Some(c.getPropertiesOfType(objectType), func(p *ast.Symbol) bool {
		return p.Flags&ast.SymbolFlagsOptional != 0 && c.isTypeAssignableTo(c.getLiteralTypeFromProperty(p, TypeFlagsStringOrNumberLiteralOrUnique, false), indexConstraint)
	})
}

func (c *Checker) getTypeOfPropertyOrIndexSignatureOfType(t *Type, name string) *Type {
	propType := c.getTypeOfPropertyOfType(t, name)
	if propType != nil {
		return propType
	}
	indexInfo := c.getApplicableIndexInfoForName(t, name)
	if indexInfo != nil {
		return c.addOptionalityEx(indexInfo.valueType, true /*isProperty*/, true /*isOptional*/)
	}
	return nil
}

/**
 * Whoa! Do you really want to use this function?
 *
 * Unless you're trying to get the *non-apparent* type for a
 * value-literal type or you're authoring relevant portions of this algorithm,
 * you probably meant to use 'getApparentTypeOfContextualType'.
 * Otherwise this may not be very useful.
 *
 * In cases where you *are* working on this function, you should understand
 * when it is appropriate to use 'getContextualType' and 'getApparentTypeOfContextualType'.
 *
 *   - Use 'getContextualType' when you are simply going to propagate the result to the expression.
 *   - Use 'getApparentTypeOfContextualType' when you're going to need the members of the type.
 *
 * @param node the expression whose contextual type will be returned.
 * @returns the contextual type of an expression.
 */
func (c *Checker) getContextualType(node *ast.Node, contextFlags ContextFlags) *Type {
	if node.Flags&ast.NodeFlagsInWithStatement != 0 {
		// We cannot answer semantic questions within a with block, do not proceed any further
		return nil
	}
	// Cached contextual types are obtained with no ContextFlags, so we can only consult them for
	// requests with no ContextFlags.
	index := c.findContextualNode(node, contextFlags == ContextFlagsNone /*includeCaches*/)
	if index >= 0 {
		return c.contextualInfos[index].t
	}
	parent := node.Parent
	switch parent.Kind {
	case ast.KindVariableDeclaration, ast.KindParameter, ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindBindingElement:
		return c.getContextualTypeForInitializerExpression(node, contextFlags)
	case ast.KindArrowFunction, ast.KindReturnStatement:
		return c.getContextualTypeForReturnExpression(node, contextFlags)
	case ast.KindYieldExpression:
		return c.getContextualTypeForYieldOperand(parent, contextFlags)
	case ast.KindAwaitExpression:
		return c.getContextualTypeForAwaitOperand(parent, contextFlags)
	case ast.KindCallExpression, ast.KindNewExpression:
		return c.getContextualTypeForArgument(parent, node)
	case ast.KindDecorator:
		return c.getContextualTypeForDecorator(parent)
	case ast.KindTypeAssertionExpression, ast.KindAsExpression:
		if ast.IsConstAssertion(parent) {
			return c.getContextualType(parent, contextFlags)
		}
		return c.getTypeFromTypeNode(parent.Type())
	case ast.KindBinaryExpression:
		return c.getContextualTypeForBinaryOperand(node, contextFlags)
	case ast.KindPropertyAssignment,
		ast.KindShorthandPropertyAssignment:
		return c.getContextualTypeForObjectLiteralElement(parent, contextFlags)
	case ast.KindSpreadAssignment:
		return c.getContextualType(parent.Parent, contextFlags)
	case ast.KindArrayLiteralExpression:
		t := c.getApparentTypeOfContextualType(parent, contextFlags)
		elementIndex := ast.IndexOfNode(parent.Elements(), node)
		if elementIndex < 0 {
			return nil
		}
		firstSpreadIndex, lastSpreadIndex := c.getSpreadIndices(parent)
		return c.getContextualTypeForElementExpression(t, elementIndex, len(parent.Elements()), firstSpreadIndex, lastSpreadIndex)
	case ast.KindConditionalExpression:
		return c.getContextualTypeForConditionalOperand(node, contextFlags)
	case ast.KindTemplateSpan:
		return c.getContextualTypeForSubstitutionExpression(parent.Parent, node)
	case ast.KindParenthesizedExpression:
		return c.getContextualType(parent, contextFlags)
	case ast.KindNonNullExpression:
		return c.getContextualType(parent, contextFlags)
	case ast.KindSatisfiesExpression:
		return c.getTypeFromTypeNode(parent.Type())
	case ast.KindExportAssignment:
		return c.tryGetTypeFromTypeNode(parent)
	case ast.KindJsxExpression:
		return c.getContextualTypeForJsxExpression(parent, contextFlags)
	case ast.KindJsxAttribute, ast.KindJsxSpreadAttribute:
		return c.getContextualTypeForJsxAttribute(parent, contextFlags)
	case ast.KindJsxOpeningElement, ast.KindJsxSelfClosingElement:
		return c.getContextualJsxElementAttributesType(parent, contextFlags)
	case ast.KindImportAttribute:
		return c.getContextualImportAttributeType(parent)
	}
	return nil
}

// In a variable, parameter or property declaration with a type annotation,
// the contextual type of an initializer expression is the type of the variable, parameter or property.
//
// Otherwise, in a parameter declaration of a contextually typed function expression,
// the contextual type of an initializer expression is the contextual type of the parameter.
//
// Otherwise, in a variable or parameter declaration with a binding pattern name,
// the contextual type of an initializer expression is the type implied by the binding pattern.
//
// Otherwise, in a binding pattern inside a variable or parameter declaration,
// the contextual type of an initializer expression is the type annotation of the containing declaration, if present.
func (c *Checker) getContextualTypeForInitializerExpression(node *ast.Node, contextFlags ContextFlags) *Type {
	declaration := node.Parent
	initializer := declaration.Initializer()
	if node == initializer {
		result := c.getContextualTypeForVariableLikeDeclaration(declaration, contextFlags)
		if result != nil {
			return result
		}
		if contextFlags&ContextFlagsSkipBindingPatterns == 0 && ast.IsBindingPattern(declaration.Name()) && len(declaration.Name().Elements()) > 0 {
			return c.getTypeFromBindingPattern(declaration.Name(), true /*includePatternInType*/, false /*reportErrors*/)
		}
	}
	return nil
}

func (c *Checker) getContextualTypeForVariableLikeDeclaration(declaration *ast.Node, contextFlags ContextFlags) *Type {
	typeNode := declaration.Type()
	if typeNode != nil {
		return c.getTypeFromTypeNode(typeNode)
	}
	switch declaration.Kind {
	case ast.KindParameter:
		return c.getContextuallyTypedParameterType(declaration)
	case ast.KindBindingElement:
		return c.getContextualTypeForBindingElement(declaration, contextFlags)
	case ast.KindPropertyDeclaration:
		if ast.IsStatic(declaration) {
			return c.getContextualTypeForStaticPropertyDeclaration(declaration, contextFlags)
		}
	}
	// By default, do nothing and return nil - only the above cases have context implied by a parent
	return nil
}

// Return contextual type of parameter or undefined if no contextual type is available
func (c *Checker) getContextuallyTypedParameterType(parameter *ast.Node) *Type {
	fn := parameter.Parent
	if !c.isContextSensitiveFunctionOrObjectLiteralMethod(fn) {
		return nil
	}
	iife := ast.GetImmediatelyInvokedFunctionExpression(fn)
	if iife != nil {
		args := c.getEffectiveCallArguments(iife)
		indexOfParameter := slices.Index(fn.Parameters(), parameter)
		if hasDotDotDotToken(parameter) {
			return c.getSpreadArgumentType(args, indexOfParameter, len(args), c.anyType, nil /*context*/, CheckModeNormal)
		}
		links := c.signatureLinks.Get(iife)
		cached := links.resolvedSignature
		links.resolvedSignature = c.anySignature
		var t *Type
		switch {
		case indexOfParameter < len(args):
			t = c.getWidenedLiteralType(c.checkExpression(args[indexOfParameter]))
		case parameter.Initializer() != nil:
			t = nil
		default:
			t = c.undefinedWideningType
		}
		links.resolvedSignature = cached
		return t
	}
	contextualSignature := c.getContextualSignature(fn)
	if contextualSignature != nil {
		index := slices.Index(fn.Parameters(), parameter) - core.IfElse(ast.GetThisParameter(fn) != nil, 1, 0)
		if hasDotDotDotToken(parameter) && core.LastOrNil(fn.Parameters()) == parameter {
			return c.getRestTypeAtPosition(contextualSignature, index, false)
		}
		return c.tryGetTypeAtPosition(contextualSignature, index)
	}
	return nil
}

func (c *Checker) isContextSensitiveFunctionOrObjectLiteralMethod(fn *ast.Node) bool {
	return (ast.IsFunctionExpressionOrArrowFunction(fn) || ast.IsObjectLiteralMethod(fn)) && c.isContextSensitiveFunctionLikeDeclaration(fn)
}

func (c *Checker) getSpreadArgumentType(args []*ast.Node, index int, argCount int, restType *Type, context *InferenceContext, checkMode CheckMode) *Type {
	inConstContext := c.isConstTypeVariable(restType, 0)
	if argCount > 0 && index >= argCount-1 {
		arg := args[argCount-1]
		if isSpreadArgument(arg) {
			// We are inferring from a spread expression in the last argument position, i.e. both the parameter
			// and the argument are ...x forms.
			var spreadType *Type
			if ast.IsSyntheticExpression(arg) {
				spreadType = arg.AsSyntheticExpression().Type.(*Type)
			} else {
				spreadType = c.checkExpressionWithContextualType(arg.Expression(), restType, context, checkMode)
			}
			if c.isArrayLikeType(spreadType) {
				return c.getMutableArrayOrTupleType(spreadType)
			}
			if ast.IsSpreadElement(arg) {
				arg = arg.Expression()
			}
			return c.createArrayTypeEx(c.checkIteratedTypeOrElementType(IterationUseSpread, spreadType, c.undefinedType, arg), inConstContext)
		}
	}
	var types []*Type
	var infos []TupleElementInfo
	for i := index; i < argCount; i++ {
		arg := args[i]
		var t *Type
		var info TupleElementInfo
		if isSpreadArgument(arg) {
			var spreadType *Type
			if ast.IsSyntheticExpression(arg) {
				spreadType = arg.AsSyntheticExpression().Type.(*Type)
			} else {
				spreadType = c.checkExpression(arg.Expression())
			}
			if c.isArrayLikeType(spreadType) {
				t = spreadType
				info.flags = ElementFlagsVariadic
			} else {
				if ast.IsSpreadElement(arg) {
					t = c.checkIteratedTypeOrElementType(IterationUseSpread, spreadType, c.undefinedType, arg.Expression())
				} else {
					t = c.checkIteratedTypeOrElementType(IterationUseSpread, spreadType, c.undefinedType, arg)
				}
				info.flags = ElementFlagsRest
			}
		} else {
			var contextualType *Type
			if isTupleType(restType) {
				contextualType = core.OrElse(c.getContextualTypeForElementExpression(restType, i-index, argCount-index, -1, -1), c.unknownType)
			} else {
				contextualType = c.getIndexedAccessTypeEx(restType, c.getNumberLiteralType(jsnum.Number(i-index)), AccessFlagsContextual, nil, nil)
			}
			argType := c.checkExpressionWithContextualType(arg, contextualType, context, checkMode)
			hasPrimitiveContextualType := inConstContext || c.maybeTypeOfKind(contextualType, TypeFlagsPrimitive|TypeFlagsIndex|TypeFlagsTemplateLiteral|TypeFlagsStringMapping)
			if hasPrimitiveContextualType {
				t = c.getRegularTypeOfLiteralType(argType)
			} else {
				t = c.getWidenedLiteralType(argType)
			}
			info.flags = ElementFlagsRequired
		}
		if ast.IsSyntheticExpression(arg) && arg.AsSyntheticExpression().TupleNameSource != nil {
			info.labeledDeclaration = arg.AsSyntheticExpression().TupleNameSource
		}
		types = append(types, t)
		infos = append(infos, info)
	}
	return c.createTupleTypeEx(types, infos, inConstContext && !someType(restType, c.isMutableArrayLikeType))
}

func (c *Checker) getMutableArrayOrTupleType(t *Type) *Type {
	switch {
	case t.flags&TypeFlagsUnion != 0:
		return c.mapType(t, c.getMutableArrayOrTupleType)
	case t.flags&TypeFlagsAny != 0 || c.isMutableArrayOrTuple(c.getBaseConstraintOrType(t)):
		return t
	case isTupleType(t):
		return c.createTupleTypeEx(c.getElementTypes(t), t.TargetTupleType().elementInfos, false /*readonly*/)
	}
	return c.createTupleTypeEx([]*Type{t}, []TupleElementInfo{{flags: ElementFlagsVariadic}}, false)
}

func (c *Checker) getContextualTypeForBindingElement(declaration *ast.Node, contextFlags ContextFlags) *Type {
	name := declaration.PropertyNameOrName()
	if ast.IsBindingPattern(name) || ast.IsComputedNonLiteralName(name) {
		return nil
	}
	parent := declaration.Parent.Parent
	parentType := c.getContextualTypeForVariableLikeDeclaration(parent, contextFlags)
	if parentType == nil {
		if !ast.IsBindingElement(parent) && parent.Initializer() != nil {
			parentType = c.checkDeclarationInitializer(parent, core.IfElse(hasDotDotDotToken(declaration), CheckModeRestBindingElement, CheckModeNormal), nil)
		}
	}
	if parentType == nil {
		return nil
	}
	if ast.IsArrayBindingPattern(parent.Name()) {
		index := slices.Index(declaration.Parent.Elements(), declaration)
		if index < 0 {
			return nil
		}
		return c.getContextualTypeForElementExpression(parentType, index, -1, -1, -1)
	}
	nameType := c.getLiteralTypeFromPropertyName(name)
	if isTypeUsableAsPropertyName(nameType) {
		return c.getTypeOfPropertyOfType(parentType, getPropertyNameFromType(nameType))
	}
	return nil
}

func (c *Checker) getContextualTypeForStaticPropertyDeclaration(declaration *ast.Node, contextFlags ContextFlags) *Type {
	if ast.IsExpression(declaration.Parent) {
		if parentType := c.getContextualType(declaration.Parent, contextFlags); parentType != nil {
			return c.getTypeOfPropertyOfContextualType(parentType, c.getSymbolOfDeclaration(declaration).Name)
		}
	}
	return nil
}

func (c *Checker) getContextualTypeForReturnExpression(node *ast.Node, contextFlags ContextFlags) *Type {
	fn := ast.GetContainingFunction(node)
	if fn != nil {
		contextualReturnType := c.getContextualReturnType(fn, contextFlags)
		if contextualReturnType != nil {
			functionFlags := ast.GetFunctionFlags(fn)
			if functionFlags&ast.FunctionFlagsGenerator != 0 {
				isAsyncGenerator := (functionFlags & ast.FunctionFlagsAsync) != 0
				if contextualReturnType.flags&TypeFlagsUnion != 0 {
					contextualReturnType = c.filterType(contextualReturnType, func(t *Type) bool {
						return c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, t, isAsyncGenerator) != nil
					})
				}
				iterationReturnType := c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, contextualReturnType, (functionFlags&ast.FunctionFlagsAsync) != 0)
				if iterationReturnType == nil {
					return nil
				}
				contextualReturnType = iterationReturnType
				// falls through to unwrap Promise for AsyncGenerators
			}
			if functionFlags&ast.FunctionFlagsAsync != 0 {
				// Get the awaited type without the `Awaited<T>` alias
				contextualAwaitedType := c.mapType(contextualReturnType, c.getAwaitedTypeNoAlias)
				if contextualAwaitedType == nil {
					return nil
				}
				return c.getUnionType([]*Type{contextualAwaitedType, c.createPromiseLikeType(contextualAwaitedType)})
			}
			// Regular function or Generator function
			return contextualReturnType
		}
	}
	return nil
}

func (c *Checker) getContextualIterationType(kind IterationTypeKind, functionDecl *ast.Node) *Type {
	isAsync := ast.GetFunctionFlags(functionDecl)&ast.FunctionFlagsAsync != 0
	contextualReturnType := c.getContextualReturnType(functionDecl, ContextFlagsNone)
	if contextualReturnType != nil {
		return c.getIterationTypeOfGeneratorFunctionReturnType(kind, contextualReturnType, isAsync)
	}
	return nil
}

func (c *Checker) getContextualReturnType(functionDecl *ast.Node, contextFlags ContextFlags) *Type {
	// If the containing function has a return type annotation, is a constructor, or is a get accessor whose
	// corresponding set accessor has a type annotation, return statements in the function are contextually typed
	returnType := c.getReturnTypeFromAnnotation(functionDecl)
	if returnType != nil {
		return returnType
	}
	// Otherwise, if the containing function is contextually typed by a function type with exactly one call signature
	// and that call signature is non-generic, return statements are contextually typed by the return type of the signature
	signature := c.getContextualSignatureForFunctionLikeDeclaration(functionDecl)
	if signature != nil && !c.isResolvingReturnTypeOfSignature(signature) {
		returnType := c.getReturnTypeOfSignature(signature)
		functionFlags := ast.GetFunctionFlags(functionDecl)
		if functionFlags&ast.FunctionFlagsGenerator != 0 {
			return c.filterType(returnType, func(t *Type) bool {
				return t.flags&(TypeFlagsAnyOrUnknown|TypeFlagsVoid|TypeFlagsInstantiableNonPrimitive) != 0 || c.checkGeneratorInstantiationAssignabilityToReturnType(t, functionFlags, nil /*errorNode*/)
			})
		}
		if functionFlags&ast.FunctionFlagsAsync != 0 {
			return c.filterType(returnType, func(t *Type) bool {
				return t.flags&(TypeFlagsAnyOrUnknown|TypeFlagsVoid|TypeFlagsInstantiableNonPrimitive) != 0 || c.getAwaitedTypeOfPromise(t) != nil
			})
		}
		return returnType
	}
	iife := ast.GetImmediatelyInvokedFunctionExpression(functionDecl)
	if iife != nil {
		return c.getContextualType(iife, contextFlags)
	}
	return nil
}

func (c *Checker) checkGeneratorInstantiationAssignabilityToReturnType(returnType *Type, functionFlags ast.FunctionFlags, errorNode *ast.Node) bool {
	// Naively, one could check that Generator<any, any, any> is assignable to the return type annotation.
	// However, that would not catch the error in the following case.
	//
	//    interface BadGenerator extends Iterable<number>, Iterator<string> { }
	//    function* g(): BadGenerator { } // Iterable and Iterator have different types!
	//
	generatorYieldType := core.OrElse(c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindYield, returnType, (functionFlags&ast.FunctionFlagsAsync) != 0), c.anyType)
	generatorReturnType := core.OrElse(c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, returnType, (functionFlags&ast.FunctionFlagsAsync) != 0), generatorYieldType)
	generatorNextType := core.OrElse(c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindNext, returnType, (functionFlags&ast.FunctionFlagsAsync) != 0), c.unknownType)
	generatorInstantiation := c.createGeneratorType(generatorYieldType, generatorReturnType, generatorNextType, functionFlags&ast.FunctionFlagsAsync != 0)
	return c.checkTypeAssignableTo(generatorInstantiation, returnType, errorNode, nil)
}

func (c *Checker) getContextualSignatureForFunctionLikeDeclaration(node *ast.Node) *Signature {
	// Only function expressions, arrow functions, and object literal methods are contextually typed.
	if ast.IsFunctionExpressionOrArrowFunction(node) || ast.IsObjectLiteralMethod(node) {
		return c.getContextualSignature(node)
	}
	return nil
}

func (c *Checker) getContextualTypeForYieldOperand(node *ast.Node, contextFlags ContextFlags) *Type {
	fn := ast.GetContainingFunction(node)
	if fn != nil {
		functionFlags := ast.GetFunctionFlags(fn)
		contextualReturnType := c.getContextualReturnType(fn, contextFlags)
		if contextualReturnType != nil {
			isAsyncGenerator := functionFlags&ast.FunctionFlagsAsync != 0
			isYieldStar := node.AsYieldExpression().AsteriskToken != nil
			if !isYieldStar && contextualReturnType.flags&TypeFlagsUnion != 0 {
				contextualReturnType = c.filterType(contextualReturnType, func(t *Type) bool {
					return c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, t, isAsyncGenerator) != nil
				})
			}
			if isYieldStar {
				iterationTypes := c.getIterationTypesOfGeneratorFunctionReturnType(contextualReturnType, isAsyncGenerator)
				yieldType := core.OrElse(iterationTypes.yieldType, c.silentNeverType)
				returnType := core.OrElse(c.getContextualType(node, contextFlags), c.silentNeverType)
				nextType := core.OrElse(iterationTypes.nextType, c.unknownType)
				generatorType := c.createGeneratorType(yieldType, returnType, nextType, false /*isAsyncGenerator*/)
				if isAsyncGenerator {
					asyncGeneratorType := c.createGeneratorType(yieldType, returnType, nextType, true /*isAsyncGenerator*/)
					return c.getUnionType([]*Type{generatorType, asyncGeneratorType})
				}
				return generatorType
			}
			return c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindYield, contextualReturnType, isAsyncGenerator)
		}
	}
	return nil
}

func (c *Checker) getContextualTypeForAwaitOperand(node *ast.Node, contextFlags ContextFlags) *Type {
	contextualType := c.getContextualType(node, contextFlags)
	if contextualType != nil {
		contextualAwaitedType := c.getAwaitedTypeNoAlias(contextualType)
		if contextualAwaitedType != nil {
			return c.getUnionType([]*Type{contextualAwaitedType, c.createPromiseLikeType(contextualAwaitedType)})
		}
	}
	return nil
}

// In a typed function call, an argument or substitution expression is contextually typed by the type of the corresponding parameter.
func (c *Checker) getContextualTypeForArgument(callTarget *ast.Node, arg *ast.Node) *Type {
	args := c.getEffectiveCallArguments(callTarget)
	argIndex := slices.Index(args, arg)
	// -1 for e.g. the expression of a CallExpression, or the tag of a TaggedTemplateExpression
	if argIndex == -1 {
		return nil
	}
	return c.getContextualTypeForArgumentAtIndex(callTarget, argIndex)
}

func (c *Checker) getContextualTypeForArgumentAtIndex(callTarget *ast.Node, argIndex int) *Type {
	if ast.IsImportCall(callTarget) {
		switch {
		case argIndex == 0:
			return c.stringType
		case argIndex == 1:
			return c.getGlobalImportCallOptionsType()
		default:
			return c.anyType
		}
	}
	// If we're already in the process of resolving the given signature, don't resolve again as
	// that could cause infinite recursion. Instead, return anySignature.
	var signature *Signature
	if c.signatureLinks.Get(callTarget).resolvedSignature == c.resolvingSignature {
		signature = c.resolvingSignature
	} else {
		signature = c.getResolvedSignature(callTarget, nil, CheckModeNormal)
	}
	if ast.IsJsxOpeningLikeElement(callTarget) && argIndex == 0 {
		return c.getEffectiveFirstArgumentForJsxSignature(signature, callTarget)
	}
	restIndex := len(signature.parameters) - 1
	if signatureHasRestParameter(signature) && argIndex >= restIndex {
		return c.getIndexedAccessTypeEx(c.getTypeOfSymbol(signature.parameters[restIndex]), c.getNumberLiteralType(jsnum.Number(argIndex-restIndex)), AccessFlagsContextual, nil, nil)
	}
	return c.getTypeAtPosition(signature, argIndex)
}

func (c *Checker) getContextualTypeForDecorator(decorator *ast.Node) *Type {
	signature := c.getDecoratorCallSignature(decorator)
	if signature != nil {
		return c.getOrCreateTypeFromSignature(signature)
	}
	return nil
}

func (c *Checker) getContextualTypeForBinaryOperand(node *ast.Node, contextFlags ContextFlags) *Type {
	binary := node.Parent.AsBinaryExpression()
	if t := binary.Type; t != nil {
		return c.getTypeFromTypeNode(t)
	}
	switch binary.OperatorToken.Kind {
	case ast.KindEqualsToken, ast.KindAmpersandAmpersandEqualsToken, ast.KindBarBarEqualsToken, ast.KindQuestionQuestionEqualsToken:
		// In an assignment expression, the right operand is contextually typed by the type of the left operand
		// unless it's an assignment declaration.
		if node == binary.Right {
			target := ast.GetLeftmostExpression(binary.Left, false)
			if !(ast.IsIdentifier(target) && c.getResolvedSymbol(target).Flags&ast.SymbolFlagsModuleExports != 0) {
				return c.getContextualTypeForAssignmentExpression(binary)
			}
		}
	case ast.KindBarBarToken, ast.KindQuestionQuestionToken:
		// When an || expression has a contextual type, the operands are contextually typed by that type, except
		// when that type originates in a binding pattern, the right operand is contextually typed by the type of
		// the left operand. When an || expression has no contextual type, the right operand is contextually typed
		// by the type of the left operand, except for the special case of Javascript declarations of the form
		// `namespace.prop = namespace.prop || {}`.
		t := c.getContextualType(binary.AsNode(), contextFlags)
		if node == binary.Right && (t == nil || c.patternForType[t] != nil) {
			return c.getTypeOfExpression(binary.Left)
		}
		return t
	case ast.KindAmpersandAmpersandToken, ast.KindCommaToken:
		if node == binary.Right {
			return c.getContextualType(binary.AsNode(), contextFlags)
		}
	}
	return nil
}

func (c *Checker) getContextualTypeForAssignmentExpression(binary *ast.BinaryExpression) *Type {
	left := binary.Left
	if ast.IsAccessExpression(left) {
		expr := left.Expression()
		switch expr.Kind {
		case ast.KindIdentifier:
			symbol := c.getExportSymbolOfValueSymbolIfExported(c.getResolvedSymbol(expr))
			if symbol.Flags&ast.SymbolFlagsModuleExports != 0 {
				// No contextual type for an expression of the form 'module.exports = expr'.
				return nil
			}
			if binary.Symbol != nil {
				// We have an assignment declaration (a binary expression with a symbol assigned by the binder) of the form
				// 'F.id = expr' or 'F[xxx] = expr'. If 'F' is declared as a variable with a type annotation, we can obtain a
				// contextual type from the annotated type without triggering a circularity. Otherwise, the assignment
				// declaration has no contextual type.
				if symbol.ValueDeclaration != nil && ast.IsVariableDeclaration(symbol.ValueDeclaration) {
					if typeNode := symbol.ValueDeclaration.Type(); typeNode != nil {
						if ast.IsPropertyAccessExpression(left) {
							return c.getTypeOfPropertyOfContextualType(c.getTypeFromTypeNode(typeNode), left.Name().Text())
						}
						nameType := c.checkExpressionCached(left.AsElementAccessExpression().ArgumentExpression)
						if isTypeUsableAsPropertyName(nameType) {
							return c.getTypeOfPropertyOfContextualTypeEx(c.getTypeFromTypeNode(typeNode), getPropertyNameFromType(nameType), nameType)
						}
						return c.getTypeOfExpression(left)
					}
				}
				return nil
			}
		case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression:
			if binary.Symbol != nil {
				return nil
			}
		case ast.KindThisKeyword:
			var symbol *ast.Symbol
			thisType := c.getTypeOfExpression(expr)
			if ast.IsPropertyAccessExpression(left) {
				name := left.Name()
				if ast.IsPrivateIdentifier(name) {
					if thisType.symbol != nil {
						symbol = c.getPropertyOfType(thisType, binder.GetSymbolNameForPrivateIdentifier(thisType.symbol, name.Text()))
					}
				} else {
					symbol = c.getPropertyOfType(thisType, name.Text())
				}
			} else {
				propType := c.checkExpressionCached(left.AsElementAccessExpression().ArgumentExpression)
				if isTypeUsableAsPropertyName(propType) {
					symbol = c.getPropertyOfType(thisType, getPropertyNameFromType(propType))
				}
			}
			if symbol != nil {
				if d := symbol.ValueDeclaration; d != nil && (ast.IsPropertyDeclaration(d) || ast.IsPropertySignatureDeclaration(d)) && d.Type() == nil && d.Initializer() == nil {
					// No contextual type for 'this.xxx = expr', where xxx is declared as a property with no type annotation or initializer.
					return nil
				}
			}
			if binary.Symbol != nil && binary.Symbol.ValueDeclaration != nil && binary.Symbol.ValueDeclaration.Type() == nil {
				// We have an assignment declaration 'this.xxx = expr' with no (synthetic) type annotation
				if !ast.IsObjectLiteralMethod(c.getThisContainer(expr, false, false)) {
					return nil
				}
				// and now for one single case of object literal methods
				name := ast.GetElementOrPropertyAccessName(left)
				if name == nil {
					return nil
				} else {
					// !!! contextual typing for `this` in object literals
					return nil
				}
			}
		}
	}
	return c.getTypeOfExpression(left)
}

func (c *Checker) getContextualTypeForObjectLiteralElement(element *ast.Node, contextFlags ContextFlags) *Type {
	if t := element.Type(); t != nil && !ast.IsObjectLiteralMethod(element) {
		return c.getTypeFromTypeNode(t)
	}
	objectLiteral := element.Parent
	t := c.getApparentTypeOfContextualType(objectLiteral, contextFlags)
	if t != nil {
		if c.hasBindableName(element) {
			// For a (non-symbol) computed property, there is no reason to look up the name
			// in the type. It will just be "__computed", which does not appear in any
			// SymbolTable.
			symbol := c.getSymbolOfDeclaration(element)
			return c.getTypeOfPropertyOfContextualTypeEx(t, symbol.Name, c.valueSymbolLinks.Get(symbol).nameType)
		}
		if ast.HasDynamicName(element) {
			name := ast.GetNameOfDeclaration(element)
			if name != nil && ast.IsComputedPropertyName(name) {
				exprType := c.checkExpression(name.Expression())
				if isTypeUsableAsPropertyName(exprType) {
					propType := c.getTypeOfPropertyOfContextualType(t, getPropertyNameFromType(exprType))
					if propType != nil {
						return propType
					}
				}
			}
		}
		if element.Name() != nil {
			nameType := c.getLiteralTypeFromPropertyName(element.Name())
			// We avoid calling getApplicableIndexInfo here because it performs potentially expensive intersection reduction.
			return c.mapTypeEx(t, func(t *Type) *Type {
				indexInfo := c.findApplicableIndexInfo(c.getIndexInfosOfStructuredType(t), nameType)
				if indexInfo == nil {
					return nil
				}
				return indexInfo.valueType
			}, true /*noReductions*/)
		}
	}
	return nil
}

// In an object literal contextually typed by a type T, the contextual type of a property assignment is the type of
// the matching property in T, if one exists. Otherwise, it is the type of the numeric index signature in T, if one
// exists. Otherwise, it is the type of the string index signature in T, if one exists.
func (c *Checker) getContextualTypeForObjectLiteralMethod(node *ast.Node, contextFlags ContextFlags) *Type {
	if node.Flags&ast.NodeFlagsInWithStatement != 0 {
		// We cannot answer semantic questions within a with block, do not proceed any further
		return nil
	}
	return c.getContextualTypeForObjectLiteralElement(node, contextFlags)
}

func (c *Checker) getContextualTypeForElementExpression(t *Type, index int, length int, firstSpreadIndex int, lastSpreadIndex int) *Type {
	if t == nil {
		return nil
	}
	return c.mapTypeEx(t, func(t *Type) *Type {
		if isTupleType(t) {
			// If index is before any spread element and within the fixed part of the contextual tuple type, return
			// the type of the contextual tuple element.
			if (firstSpreadIndex < 0 || index < firstSpreadIndex) && index < t.TargetTupleType().fixedLength {
				return c.removeMissingType(c.getTypeArguments(t)[index], t.TargetTupleType().elementInfos[index].flags&ElementFlagsOptional != 0)
			}
			// When the length is known and the index is after all spread elements we compute the offset from the element
			// to the end and the number of ending fixed elements in the contextual tuple type.
			offset := 0
			if length >= 0 && (lastSpreadIndex < 0 || index > lastSpreadIndex) {
				offset = length - index
			}
			fixedEndLength := 0
			if offset > 0 && t.TargetTupleType().combinedFlags&ElementFlagsVariable != 0 {
				fixedEndLength = getEndElementCount(t.TargetTupleType(), ElementFlagsFixed)
			}
			// If the offset is within the ending fixed part of the contextual tuple type, return the type of the contextual
			// tuple element.
			if offset > 0 && offset <= fixedEndLength {
				return c.getTypeArguments(t)[c.getTypeReferenceArity(t)-offset]
			}
			// Return a union of the possible contextual element types with no subtype reduction.
			tupleIndex := t.TargetTupleType().fixedLength
			if firstSpreadIndex >= 0 {
				tupleIndex = min(tupleIndex, firstSpreadIndex)
			}
			endSkipCount := fixedEndLength
			if length >= 0 && lastSpreadIndex >= 0 {
				endSkipCount = min(fixedEndLength, length-lastSpreadIndex)
			}
			return c.getElementTypeOfSliceOfTupleType(t, tupleIndex, endSkipCount, false /*writing*/, true /*noReductions*/)
		}
		// If element index is known and a contextual property with that name exists, return it. Otherwise return the
		// iterated or element type of the contextual type.
		if firstSpreadIndex < 0 || index < firstSpreadIndex {
			propType := c.getTypeOfPropertyOfContextualType(t, strconv.Itoa(index))
			if propType != nil {
				return propType
			}
		}
		return c.getIteratedTypeOrElementType(IterationUseElement, t, c.undefinedType, nil /*errorNode*/, false /*checkAssignability*/)
	}, true /*noReductions*/)
}

// In a contextually typed conditional expression, the true/false expressions are contextually typed by the same type.
func (c *Checker) getContextualTypeForConditionalOperand(node *ast.Node, contextFlags ContextFlags) *Type {
	conditional := node.Parent.AsConditionalExpression()
	if node == conditional.WhenTrue || node == conditional.WhenFalse {
		return c.getContextualType(node.Parent, contextFlags)
	}
	return nil
}

func (c *Checker) getContextualTypeForSubstitutionExpression(template *ast.Node, substitutionExpression *ast.Node) *Type {
	if ast.IsTaggedTemplateExpression(template.Parent) {
		return c.getContextualTypeForArgument(template.Parent, substitutionExpression)
	}
	return nil
}

func (c *Checker) getContextualImportAttributeType(node *ast.Node) *Type {
	return c.getTypeOfPropertyOfContextualType(c.getGlobalImportAttributesType(), node.Name().Text())
}

// Returns the effective arguments for an expression that works like a function invocation.
func (c *Checker) getEffectiveCallArguments(node *ast.Node) []*ast.Node {
	switch {
	case ast.IsJsxOpeningFragment(node):
		// This attributes Type does not include a children property yet, the same way a fragment created with <React.Fragment> does not at this stage
		return []*ast.Node{c.createSyntheticExpression(node, c.emptyFreshJsxObjectType, false, nil)}
	case ast.IsTaggedTemplateExpression(node):
		template := node.AsTaggedTemplateExpression().Template
		firstArg := c.createSyntheticExpression(template, c.getGlobalTemplateStringsArrayType(), false, nil)
		if !ast.IsTemplateExpression(template) {
			return []*ast.Node{firstArg}
		}
		spans := template.AsTemplateExpression().TemplateSpans.Nodes
		args := make([]*ast.Node, len(spans)+1)
		args[0] = firstArg
		for i, span := range spans {
			args[i+1] = span.Expression()
		}
		return args
	case ast.IsDecorator(node):
		return c.getEffectiveDecoratorArguments(node)
	case ast.IsBinaryExpression(node):
		// Handles instanceof operator
		return []*ast.Node{node.AsBinaryExpression().Left}
	case ast.IsJsxOpeningLikeElement(node):
		if len(node.Attributes().Properties()) != 0 || (ast.IsJsxOpeningElement(node) && len(node.Parent.Children().Nodes) != 0) {
			return []*ast.Node{node.Attributes()}
		}
		return nil
	default:
		args := node.Arguments()
		spreadIndex := c.getSpreadArgumentIndex(args)
		if spreadIndex >= 0 {
			// Create synthetic arguments from spreads of tuple types.
			effectiveArgs := slices.Clip(args[:spreadIndex])
			for i := spreadIndex; i < len(args); i++ {
				arg := args[i]
				var spreadType *Type
				// We can call checkExpressionCached because spread expressions never have a contextual type.
				if ast.IsSpreadElement(arg) {
					if len(c.flowLoopStack) != 0 {
						spreadType = c.checkExpression(arg.Expression())
					} else {
						spreadType = c.checkExpressionCached(arg.Expression())
					}
				}
				if spreadType != nil && isTupleType(spreadType) {
					for i, t := range c.getElementTypes(spreadType) {
						elementInfos := spreadType.TargetTupleType().elementInfos
						flags := elementInfos[i].flags
						syntheticType := t
						if flags&ElementFlagsRest != 0 {
							syntheticType = c.createArrayType(t)
						}
						syntheticArg := c.createSyntheticExpression(arg, syntheticType, flags&ElementFlagsVariable != 0, elementInfos[i].labeledDeclaration)
						effectiveArgs = append(effectiveArgs, syntheticArg)
					}
				} else {
					effectiveArgs = append(effectiveArgs, arg)
				}
			}
			return effectiveArgs
		}
		return args
	}
}

func (c *Checker) getSpreadArgumentIndex(args []*ast.Node) int {
	return core.FindIndex(args, isSpreadArgument)
}

func isSpreadArgument(arg *ast.Node) bool {
	return ast.IsSpreadElement(arg) || ast.IsSyntheticExpression(arg) && arg.AsSyntheticExpression().IsSpread
}

func (c *Checker) createSyntheticExpression(parent *ast.Node, t *Type, isSpread bool, tupleNameSource *ast.Node) *ast.Node {
	result := c.factory.NewSyntheticExpression(t, isSpread, tupleNameSource)
	result.Loc = parent.Loc
	result.Parent = parent
	return result
}

func (c *Checker) getSpreadIndices(node *ast.Node) (int, int) {
	links := c.arrayLiteralLinks.Get(node)
	if !links.indicesComputed {
		first, last := -1, -1
		for i, element := range node.Elements() {
			if ast.IsSpreadElement(element) {
				if first < 0 {
					first = i
				}
				last = i
			}
		}
		links.firstSpreadIndex, links.lastSpreadIndex = first, last
		links.indicesComputed = true
	}
	return links.firstSpreadIndex, links.lastSpreadIndex
}

// Returns the synthetic argument list for a decorator invocation.
func (c *Checker) getEffectiveDecoratorArguments(node *ast.Node) []*ast.Node {
	expr := node.Expression()
	signature := c.getDecoratorCallSignature(node)
	if signature != nil {
		args := make([]*ast.Node, len(signature.parameters))
		for i, param := range signature.parameters {
			args[i] = c.createSyntheticExpression(expr, c.getTypeOfSymbol(param), false, nil)
		}
		return args
	}
	panic("Decorator signature not found")
}

func (c *Checker) getDecoratorCallSignature(decorator *ast.Node) *Signature {
	if c.legacyDecorators {
		return c.getLegacyDecoratorCallSignature(decorator)
	}
	return c.getESDecoratorCallSignature(decorator)
}

func (c *Checker) getLegacyDecoratorCallSignature(decorator *ast.Node) *Signature {
	node := decorator.Parent
	links := c.signatureLinks.Get(node)
	if links.decoratorSignature == nil {
		links.decoratorSignature = c.anySignature
		switch node.Kind {
		case ast.KindClassDeclaration, ast.KindClassExpression:
			// For a class decorator, the `target` is the type of the class (e.g. the
			// "static" or "constructor" side of the class).
			targetType := c.getTypeOfSymbol(c.getSymbolOfDeclaration(node))
			targetParam := c.newParameter("target", targetType)
			links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam}, c.getUnionType([]*Type{targetType, c.voidType}))
		case ast.KindParameter:
			if !ast.IsConstructorDeclaration(node.Parent) && !(ast.IsMethodDeclaration(node.Parent) || ast.IsSetAccessorDeclaration(node.Parent) && ast.IsClassLike(node.Parent.Parent)) {
				break
			}
			if ast.GetThisParameter(node.Parent) == node {
				break
			}
			index := slices.Index(node.Parent.Parameters(), node) - core.IfElse(ast.GetThisParameter(node.Parent) != nil, 1, 0)
			debug.Assert(index >= 0)
			// A parameter declaration decorator will have three arguments (see `ParameterDecorator` in
			// core.d.ts).
			var targetType *Type
			var keyType *Type
			if ast.IsConstructorDeclaration(node.Parent) {
				targetType = c.getTypeOfSymbol(c.getSymbolOfDeclaration(node.Parent.Parent))
				keyType = c.undefinedType
			} else {
				targetType = c.getParentTypeOfClassElement(node.Parent)
				keyType = c.getClassElementPropertyKeyType(node.Parent)
			}
			indexType := c.getNumberLiteralType(jsnum.Number(index))
			targetParam := c.newParameter("target", targetType)
			keyParam := c.newParameter("propertyKey", keyType)
			indexParam := c.newParameter("parameterIndex", indexType)
			links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam, indexParam}, c.voidType)
		case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyDeclaration:
			if !ast.IsClassLike(node.Parent) {
				break
			}
			// A method or accessor declaration decorator will have either two or three arguments (see
			// `PropertyDecorator` and `MethodDecorator` in core.d.ts).
			targetType := c.getParentTypeOfClassElement(node)
			targetParam := c.newParameter("target", targetType)
			keyType := c.getClassElementPropertyKeyType(node)
			keyParam := c.newParameter("propertyKey", keyType)
			returnType := c.voidType
			if !ast.IsPropertyDeclaration(node) {
				returnType = c.newTypedPropertyDescriptorType(c.getTypeOfNode(node))
			}
			hasPropDesc := !ast.IsPropertyDeclaration(node) || ast.HasAccessorModifier(node)
			if hasPropDesc {
				descriptorType := c.newTypedPropertyDescriptorType(c.getTypeOfNode(node))
				descriptorParam := c.newParameter("descriptor", descriptorType)
				links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam, descriptorParam}, c.getUnionType([]*Type{returnType, c.voidType}))
			} else {
				links.decoratorSignature = c.newCallSignature(nil, nil, []*ast.Symbol{targetParam, keyParam}, c.getUnionType([]*Type{returnType, c.voidType}))
			}
		}
	}
	if links.decoratorSignature == c.anySignature {
		return nil
	}
	return links.decoratorSignature
}

func (c *Checker) getESDecoratorCallSignature(decorator *ast.Node) *Signature {
	// We are considering a future change that would allow the type of a decorator to affect the type of the
	// class and its members, such as a `@Stringify` decorator changing the type of a `number` field to `string`, or
	// a `@Callable` decorator adding a call signature to a `class`. The type arguments for the various context
	// types may eventually change to reflect such mutations.
	//
	// In some cases we describe such potential mutations as coming from a "prior decorator application". It is
	// important to note that, while decorators are *evaluated* left to right, they are *applied* right to left
	// to preserve f ৹ g -> f(g(x)) application order. In these cases, a "prior" decorator usually means the
	// next decorator following this one in document order.
	//
	// The "original type" of a class or member is the type it was declared as, or the type we infer from
	// initializers, before _any_ decorators are applied.
	//
	// The type of a class or member that is a result of a prior decorator application represents the
	// "current type", i.e., the type for the declaration at the time the decorator is _applied_.
	//
	// The type of a class or member that is the result of the application of *all* relevant decorators is the
	// "final type".
	//
	// Any decorator that allows mutation or replacement will also refer to an "input type" and an
	// "output type". The "input type" corresponds to the "current type" of the declaration, while the
	// "output type" will become either the "input type/current type" for a subsequent decorator application,
	// or the "final type" for the decorated declaration.
	//
	// It is important to understand decorator application order as it relates to how the "current", "input",
	// "output", and "final" types will be determined:
	//
	//  @E2 @E1 class SomeClass {
	//      @A2 @A1 static f() {}
	//      @B2 @B1 g() {}
	//      @C2 @C1 static x;
	//      @D2 @D1 y;
	//  }
	//
	// Per [the specification][1], decorators are applied in the following order:
	//
	// 1. For each static method (incl. get/set methods and `accessor` fields), in document order:
	//    a. Apply each decorator for that method, in reverse order (`A1`, `A2`).
	// 2. For each instance method (incl. get/set methods and `accessor` fields), in document order:
	//    a. Apply each decorator for that method, in reverse order (`B1`, `B2`).
	// 3. For each static field (excl. auto-accessors), in document order:
	//    a. Apply each decorator for that field, in reverse order (`C1`, `C2`).
	// 4. For each instance field (excl. auto-accessors), in document order:
	//    a. Apply each decorator for that field, in reverse order (`D1`, `D2`).
	// 5. Apply each decorator for the class, in reverse order (`E1`, `E2`).
	//
	// As a result, "current" types at each decorator application are as follows:
	// - For `A1`, the "current" types of the class and method are their "original" types.
	// - For `A2`, the "current type" of the method is the "output type" of `A1`, and the "current type" of the
	//   class is the type of `SomeClass` where `f` is the "output type" of `A1`. This becomes the "final type"
	//   of `f`.
	// - For `B1`, the "current type" of the method is its "original type", and the "current type" of the class
	//   is the type of `SomeClass` where `f` now has its "final type".
	// - etc.
	//
	// [1]: https://arai-a.github.io/ecma262-compare/?pr=2417&id=sec-runtime-semantics-classdefinitionevaluation
	//
	// This seems complicated at first glance, but is not unlike our existing inference for functions:
	//
	//  declare function pipe<Original, A1, A2, B1, B2, C1, C2, D1, D2, E1, E2>(
	//      original: Original,
	//      a1: (input: Original, context: Context<E2>) => A1,
	//      a2: (input: A1, context: Context<E2>) => A2,
	//      b1: (input: A2, context: Context<E2>) => B1,
	//      b2: (input: B1, context: Context<E2>) => B2,
	//      c1: (input: B2, context: Context<E2>) => C1,
	//      c2: (input: C1, context: Context<E2>) => C2,
	//      d1: (input: C2, context: Context<E2>) => D1,
	//      d2: (input: D1, context: Context<E2>) => D2,
	//      e1: (input: D2, context: Context<E2>) => E1,
	//      e2: (input: E1, context: Context<E2>) => E2,
	//  ): E2;

	// When a decorator is applied, it is passed two arguments: "target", which is a value representing the
	// thing being decorated (constructors for classes, functions for methods/accessors, `undefined` for fields,
	// and a `{ get, set }` object for auto-accessors), and "context", which is an object that provides
	// reflection information about the decorated element, as well as the ability to add additional "extra"
	// initializers. In most cases, the "target" argument corresponds to the "input type" in some way, and the
	// return value similarly corresponds to the "output type" (though if the "output type" is `void` or
	// `undefined` then the "output type" is the "input type").
	node := decorator.Parent
	links := c.signatureLinks.Get(node)
	if links.decoratorSignature == nil {
		links.decoratorSignature = c.anySignature
		switch node.Kind {
		case ast.KindClassDeclaration, ast.KindClassExpression:
			// Class decorators have a `context` of `ClassDecoratorContext<Class>`, where the `Class` type
			// argument will be the "final type" of the class after all decorators are applied.
			targetType := c.getTypeOfSymbol(c.getSymbolOfDeclaration(node))
			contextType := c.newClassDecoratorContextType(targetType)
			links.decoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, targetType)
		case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
			if !ast.IsClassLike(node.Parent) {
				break
			}
			// Method decorators have a `context` of `ClassMethodDecoratorContext<This, Value>`, where the
			// `Value` type argument corresponds to the "final type" of the method.
			//
			// Getter decorators have a `context` of `ClassGetterDecoratorContext<This, Value>`, where the
			// `Value` type argument corresponds to the "final type" of the value returned by the getter.
			//
			// Setter decorators have a `context` of `ClassSetterDecoratorContext<This, Value>`, where the
			// `Value` type argument corresponds to the "final type" of the parameter of the setter.
			//
			// In all three cases, the `This` type argument is the "final type" of either the class or
			// instance, depending on whether the member was `static`.
			var valueType *Type
			if ast.IsMethodDeclaration(node) {
				valueType = c.getOrCreateTypeFromSignature(c.getSignatureFromDeclaration(node))
			} else {
				valueType = c.getTypeOfNode(node)
			}
			var thisType *Type
			if ast.HasStaticModifier(node) {
				thisType = c.getTypeOfSymbol(c.getSymbolOfDeclaration(node.Parent))
			} else {
				thisType = c.getDeclaredTypeOfClassOrInterface(c.getSymbolOfDeclaration(node.Parent))
			}
			// We wrap the "input type", if necessary, to match the decoration target. For getters this is
			// something like `() => inputType`, for setters it's `(value: inputType) => void` and for
			// methods it is just the input type.
			var targetType *Type
			switch {
			case ast.IsGetAccessorDeclaration(node):
				targetType = c.newGetterFunctionType(valueType)
			case ast.IsSetAccessorDeclaration(node):
				targetType = c.newSetterFunctionType(valueType)
			default:
				targetType = valueType
			}
			contextType := c.newClassMemberDecoratorContextTypeForNode(node, thisType, valueType)
			links.decoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, targetType)
		case ast.KindPropertyDeclaration:
			if !ast.IsClassLike(node.Parent) {
				break
			}
			// Field decorators have a `context` of `ClassFieldDecoratorContext<This, Value>` and
			// auto-accessor decorators have a `context` of `ClassAccessorDecoratorContext<This, Value>. In
			// both cases, the `This` type argument is the "final type" of either the class or instance,
			// depending on whether the member was `static`, and the `Value` type argument corresponds to
			// the "final type" of the value stored in the field.
			valueType := c.getTypeOfNode(node)
			var thisType *Type
			if ast.HasStaticModifier(node) {
				thisType = c.getTypeOfSymbol(c.getSymbolOfDeclaration(node.Parent))
			} else {
				thisType = c.getDeclaredTypeOfClassOrInterface(c.getSymbolOfDeclaration(node.Parent))
			}
			// The `target` of an auto-accessor decorator is a `{ get, set }` object, representing the
			// runtime-generated getter and setter that are added to the class/prototype. The `target` of a
			// regular field decorator is always `undefined` as it isn't installed until it is initialized.
			var targetType *Type
			if ast.HasAccessorModifier(node) {
				targetType = c.newClassAccessorDecoratorTargetType(thisType, valueType)
			} else {
				targetType = c.undefinedType
			}
			// We wrap the "output type" depending on the declaration. For auto-accessors, we wrap the
			// "output type" in a `ClassAccessorDecoratorResult<This, In, Out>` type, which allows for
			// mutation of the runtime-generated getter and setter, as well as the injection of an
			// initializer mutator. For regular fields, we wrap the "output type" in an initializer mutator.
			var returnType *Type
			if ast.HasAccessorModifier(node) {
				returnType = c.newClassAccessorDecoratorResultType(thisType, valueType)
			} else {
				returnType = c.newClassFieldDecoratorInitializerMutatorType(thisType, valueType)
			}
			contextType := c.newClassMemberDecoratorContextTypeForNode(node, thisType, valueType)
			links.decoratorSignature = c.newESDecoratorCallSignature(targetType, contextType, returnType)
		}
	}
	if links.decoratorSignature == c.anySignature {
		return nil
	}
	return links.decoratorSignature
}

func (c *Checker) newClassDecoratorContextType(classType *Type) *Type {
	return c.tryCreateTypeReference(c.getGlobalClassDecoratorContextType(), []*Type{classType})
}

func (c *Checker) newClassMethodDecoratorContextType(classType *Type, valueType *Type) *Type {
	return c.tryCreateTypeReference(c.getGlobalClassMethodDecoratorContextType(), []*Type{classType, valueType})
}

func (c *Checker) newClassGetterDecoratorContextType(classType *Type, valueType *Type) *Type {
	return c.tryCreateTypeReference(c.getGlobalClassGetterDecoratorContextType(), []*Type{classType, valueType})
}

func (c *Checker) newClassSetterDecoratorContextType(classType *Type, valueType *Type) *Type {
	return c.tryCreateTypeReference(c.getGlobalClassSetterDecoratorContextType(), []*Type{classType, valueType})
}

func (c *Checker) newClassAccessorDecoratorContextType(thisType *Type, valueType *Type) *Type {
	return c.tryCreateTypeReference(c.getGlobalClassAccessorDecoratorContextType(), []*Type{thisType, valueType})
}

func (c *Checker) newClassFieldDecoratorContextType(thisType *Type, valueType *Type) *Type {
	return c.tryCreateTypeReference(c.getGlobalClassFieldDecoratorContextType(), []*Type{thisType, valueType})
}

// Gets a type like `{ name: "foo", private: false, static: true }` that is used to provided member-specific
// details that will be intersected with a decorator context type.
func (c *Checker) getClassMemberDecoratorContextOverrideType(nameType *Type, isPrivate bool, isStatic bool) *Type {
	kind := core.IfElse(
		isPrivate,
		core.IfElse(isStatic, CachedTypeKindDecoratorContextPrivateStatic, CachedTypeKindDecoratorContextPrivate),
		core.IfElse(isStatic, CachedTypeKindDecoratorContextStatic, CachedTypeKindDecoratorContext),
	)
	key := CachedTypeKey{kind: kind, typeId: nameType.id}
	if overrideType := c.cachedTypes[key]; overrideType != nil {
		return overrideType
	}
	members := make(ast.SymbolTable)
	members["name"] = c.newProperty("name", nameType)
	members["private"] = c.newProperty("private", core.IfElse(isPrivate, c.trueType, c.falseType))
	members["static"] = c.newProperty("static", core.IfElse(isStatic, c.trueType, c.falseType))
	overrideType := c.newAnonymousType(nil, members, nil, nil, nil)
	c.cachedTypes[key] = overrideType
	return overrideType
}

func (c *Checker) newClassMemberDecoratorContextTypeForNode(node *ast.Node, thisType *Type, valueType *Type) *Type {
	isStatic := ast.HasStaticModifier(node)
	isPrivate := ast.IsPrivateIdentifier(node.Name())
	var nameType *Type
	if isPrivate {
		nameType = c.getStringLiteralType(node.Name().Text())
	} else {
		nameType = c.getLiteralTypeFromPropertyName(node.Name())
	}
	var contextType *Type
	switch {
	case ast.IsMethodDeclaration(node):
		contextType = c.newClassMethodDecoratorContextType(thisType, valueType)
	case ast.IsGetAccessorDeclaration(node):
		contextType = c.newClassGetterDecoratorContextType(thisType, valueType)
	case ast.IsSetAccessorDeclaration(node):
		contextType = c.newClassSetterDecoratorContextType(thisType, valueType)
	case ast.IsAutoAccessorPropertyDeclaration(node):
		contextType = c.newClassAccessorDecoratorContextType(thisType, valueType)
	case ast.IsPropertyDeclaration(node):
		contextType = c.newClassFieldDecoratorContextType(thisType, valueType)
	default:
		panic("Unhandled case in createClassMemberDecoratorContextTypeForNode")
	}
	overrideType := c.getClassMemberDecoratorContextOverrideType(nameType, isPrivate, isStatic)
	return c.getIntersectionType([]*Type{contextType, overrideType})
}

func (c *Checker) newClassAccessorDecoratorTargetType(thisType *Type, valueType *Type) *Type {
	return c.tryCreateTypeReference(c.getGlobalClassAccessorDecoratorTargetType(), []*Type{thisType, valueType})
}

func (c *Checker) newClassAccessorDecoratorResultType(thisType *Type, valueType *Type) *Type {
	return c.tryCreateTypeReference(c.getGlobalClassAccessorDecoratorResultType(), []*Type{thisType, valueType})
}

func (c *Checker) newClassFieldDecoratorInitializerMutatorType(thisType *Type, valueType *Type) *Type {
	thisParam := c.newParameter("this", thisType)
	valueParam := c.newParameter("value", valueType)
	return c.newFunctionType(nil, thisParam, []*ast.Symbol{valueParam}, valueType)
}

// Creates a call signature for an ES Decorator. This method is used by the semantics of
// `getESDecoratorCallSignature`, which you should probably be using instead.
func (c *Checker) newESDecoratorCallSignature(targetType *Type, contextType *Type, nonOptionalReturnType *Type) *Signature {
	targetParam := c.newParameter("target", targetType)
	contextParam := c.newParameter("context", contextType)
	returnType := c.getUnionType([]*Type{nonOptionalReturnType, c.voidType})
	return c.newCallSignature(nil, nil /*thisParameter*/, []*ast.Symbol{targetParam, contextParam}, returnType)
}

// Creates a synthetic `FunctionType`
func (c *Checker) newFunctionType(typeParameters []*Type, thisParameter *ast.Symbol, parameters []*ast.Symbol, returnType *Type) *Type {
	signature := c.newCallSignature(typeParameters, thisParameter, parameters, returnType)
	return c.getOrCreateTypeFromSignature(signature)
}

func (c *Checker) newGetterFunctionType(t *Type) *Type {
	return c.newFunctionType(nil, nil /*thisParameter*/, nil, t)
}

func (c *Checker) newSetterFunctionType(t *Type) *Type {
	valueParam := c.newParameter("value", t)
	return c.newFunctionType(nil, nil /*thisParameter*/, []*ast.Symbol{valueParam}, c.voidType)
}

// Creates a synthetic `Signature` corresponding to a call signature.
func (c *Checker) newCallSignature(typeParameters []*Type, thisParameter *ast.Symbol, parameters []*ast.Symbol, returnType *Type) *Signature {
	decl := c.factory.NewFunctionTypeNode(nil, nil, c.factory.NewKeywordTypeNode(ast.KindAnyKeyword))
	return c.newSignature(SignatureFlagsNone, decl, typeParameters, thisParameter, parameters, returnType, nil, len(parameters))
}

func (c *Checker) newTypedPropertyDescriptorType(propertyType *Type) *Type {
	return c.createTypeFromGenericGlobalType(c.getGlobalTypedPropertyDescriptorType(), []*Type{propertyType})
}

func (c *Checker) getParentTypeOfClassElement(node *ast.Node) *Type {
	classSymbol := c.getSymbolOfNode(node.Parent)
	if ast.IsStatic(node) {
		return c.getTypeOfSymbol(classSymbol)
	}
	return c.getDeclaredTypeOfSymbol(classSymbol)
}

func (c *Checker) getClassElementPropertyKeyType(element *ast.Node) *Type {
	name := element.Name()
	switch name.Kind {
	case ast.KindIdentifier, ast.KindNumericLiteral, ast.KindStringLiteral:
		return c.getStringLiteralType(name.Text())
	case ast.KindComputedPropertyName:
		nameType := c.checkComputedPropertyName(name)
		if c.isTypeAssignableToKind(nameType, TypeFlagsESSymbolLike) {
			return nameType
		}
		return c.stringType
	}
	return c.errorType
}

func (c *Checker) getTypeOfPropertyOfContextualType(t *Type, name string) *Type {
	return c.getTypeOfPropertyOfContextualTypeEx(t, name, nil)
}

func (c *Checker) getTypeOfPropertyOfContextualTypeEx(t *Type, name string, nameType *Type) *Type {
	return c.mapTypeEx(t, func(t *Type) *Type {
		if t.flags&TypeFlagsIntersection != 0 {
			var types []*Type
			var indexInfoCandidates []*Type
			ignoreIndexInfos := false
			for _, constituentType := range t.Types() {
				if constituentType.flags&TypeFlagsObject == 0 {
					continue
				}
				if c.isGenericMappedType(constituentType) && c.getMappedTypeNameTypeKind(constituentType) != MappedTypeNameTypeKindRemapping {
					substitutedType := c.getIndexedMappedTypeSubstitutedTypeOfContextualType(constituentType, name, nameType)
					types = c.appendContextualPropertyTypeConstituent(types, substitutedType)
					continue
				}
				propertyType := c.getTypeOfConcretePropertyOfContextualType(constituentType, name)
				if propertyType == nil {
					if !ignoreIndexInfos {
						indexInfoCandidates = append(indexInfoCandidates, constituentType)
					}
					continue
				}
				ignoreIndexInfos = true
				indexInfoCandidates = nil
				types = c.appendContextualPropertyTypeConstituent(types, propertyType)
			}
			for _, candidate := range indexInfoCandidates {
				indexInfoType := c.getTypeFromIndexInfosOfContextualType(candidate, name, nameType)
				types = c.appendContextualPropertyTypeConstituent(types, indexInfoType)
			}
			if len(types) == 0 {
				return nil
			}
			if len(types) == 1 {
				return types[0]
			}
			return c.getIntersectionType(types)
		}
		if t.flags&TypeFlagsObject == 0 {
			return nil
		}
		if c.isGenericMappedType(t) && c.getMappedTypeNameTypeKind(t) != MappedTypeNameTypeKindRemapping {
			return c.getIndexedMappedTypeSubstitutedTypeOfContextualType(t, name, nameType)
		}
		result := c.getTypeOfConcretePropertyOfContextualType(t, name)
		if result != nil {
			return result
		}
		return c.getTypeFromIndexInfosOfContextualType(t, name, nameType)
	}, true /*noReductions*/)
}

func (c *Checker) getIndexedMappedTypeSubstitutedTypeOfContextualType(t *Type, name string, nameType *Type) *Type {
	propertyNameType := nameType
	if propertyNameType == nil {
		propertyNameType = c.getStringLiteralType(name)
	}
	constraint := c.getConstraintTypeFromMappedType(t)
	// special case for conditional types pretending to be negated types
	if t.AsMappedType().nameType != nil && c.isExcludedMappedPropertyName(t.AsMappedType().nameType, propertyNameType) || c.isExcludedMappedPropertyName(constraint, propertyNameType) {
		return nil
	}
	constraintOfConstraint := c.getBaseConstraintOrType(constraint)
	if !c.isTypeAssignableTo(propertyNameType, constraintOfConstraint) {
		return nil
	}
	return c.substituteIndexedMappedType(t, propertyNameType)
}

func (c *Checker) isExcludedMappedPropertyName(t *Type, propertyNameType *Type) bool {
	if t.flags&TypeFlagsConditional != 0 {
		return c.getReducedType(c.getTrueTypeFromConditionalType(t)).flags&TypeFlagsNever != 0 &&
			c.getActualTypeVariable(c.getFalseTypeFromConditionalType(t)) == c.getActualTypeVariable(t.AsConditionalType().checkType) &&
			c.isTypeAssignableTo(propertyNameType, t.AsConditionalType().extendsType)
	}
	if t.flags&TypeFlagsIntersection != 0 {
		return core.Some(t.Types(), func(t *Type) bool {
			return c.isExcludedMappedPropertyName(t, propertyNameType)
		})
	}
	return false
}

func (c *Checker) getTypeOfConcretePropertyOfContextualType(t *Type, name string) *Type {
	prop := c.getPropertyOfType(t, name)
	if prop == nil || c.isCircularMappedProperty(prop) {
		return nil
	}
	return c.removeMissingType(c.getTypeOfSymbol(prop), prop.Flags&ast.SymbolFlagsOptional != 0)
}

func (c *Checker) getTypeFromIndexInfosOfContextualType(t *Type, name string, nameType *Type) *Type {
	if isTupleType(t) && isNumericLiteralName(name) && jsnum.FromString(name) >= 0 {
		restType := c.getElementTypeOfSliceOfTupleType(t, t.TargetTupleType().fixedLength, 0 /*endSkipCount*/, false /*writing*/, true /*noReductions*/)
		if restType != nil {
			return restType
		}
	}
	if nameType == nil {
		nameType = c.getStringLiteralType(name)
	}
	indexInfo := c.findApplicableIndexInfo(c.getIndexInfosOfStructuredType(t), nameType)
	if indexInfo == nil {
		return nil
	}
	return indexInfo.valueType
}

func (c *Checker) isCircularMappedProperty(symbol *ast.Symbol) bool {
	if symbol.CheckFlags&ast.CheckFlagsMapped != 0 {
		links := c.valueSymbolLinks.Get(symbol)
		return links.resolvedType == nil && c.findResolutionCycleStartIndex(symbol, TypeSystemPropertyNameType) >= 0
	}
	return false
}

func (c *Checker) appendContextualPropertyTypeConstituent(types []*Type, t *Type) []*Type {
	// any doesn't provide any contextual information but could spoil the overall result by nullifying contextual information
	// provided by other intersection constituents so it gets replaced with `unknown` as `T & unknown` is just `T` and all
	// types computed based on the contextual information provided by other constituens are still assignable to any
	if t == nil {
		return types
	}
	if t.flags&TypeFlagsAny != 0 {
		return append(types, c.unknownType)
	}
	return append(types, t)
}

// Return the contextual type for a given expression node. During overload resolution, a contextual type may temporarily
// be "pushed" onto a node using the contextualType property.
func (c *Checker) getApparentTypeOfContextualType(node *ast.Node, contextFlags ContextFlags) *Type {
	var contextualType *Type
	if ast.IsObjectLiteralMethod(node) {
		contextualType = c.getContextualTypeForObjectLiteralMethod(node, contextFlags)
	} else {
		contextualType = c.getContextualType(node, contextFlags)
	}
	instantiatedType := c.instantiateContextualType(contextualType, node, contextFlags)
	if instantiatedType != nil && !(contextFlags&ContextFlagsNoConstraints != 0 && instantiatedType.flags&TypeFlagsTypeVariable != 0) {
		apparentType := c.mapTypeEx(instantiatedType, func(t *Type) *Type {
			if t.objectFlags&ObjectFlagsMapped != 0 {
				return t
			}
			return c.getApparentType(t)
		}, true)
		switch {
		case apparentType.flags&TypeFlagsUnion != 0 && ast.IsObjectLiteralExpression(node):
			return c.discriminateContextualTypeByObjectMembers(node, apparentType)
		case apparentType.flags&TypeFlagsUnion != 0 && ast.IsJsxAttributes(node):
			return c.discriminateContextualTypeByJSXAttributes(node, apparentType)
		default:
			return apparentType
		}
	}
	return nil
}

type ObjectLiteralDiscriminator struct {
	c       *Checker
	props   []*ast.Node
	members []*ast.Symbol
}

func (d *ObjectLiteralDiscriminator) len() int {
	return len(d.props) + len(d.members)
}

func (d *ObjectLiteralDiscriminator) name(index int) string {
	if index < len(d.props) {
		return d.props[index].Symbol().Name
	}
	return d.members[index-len(d.props)].Name
}

func (d *ObjectLiteralDiscriminator) matches(index int, t *Type) bool {
	var propType *Type
	if index < len(d.props) {
		prop := d.props[index]
		if ast.IsPropertyAssignment(prop) || ast.IsJsxAttribute(prop) {
			initializer := prop.Initializer()
			if initializer != nil {
				propType = d.c.getContextFreeTypeOfExpression(prop.Initializer())
			} else {
				propType = d.c.trueType // JsxAttribute without initializer is always true
			}
		} else {
			propType = d.c.getContextFreeTypeOfExpression(prop.Name())
		}
	} else {
		propType = d.c.undefinedType
	}
	for _, s := range propType.Distributed() {
		if d.c.isTypeAssignableTo(s, t) {
			return true
		}
	}
	return false
}

func (c *Checker) discriminateContextualTypeByObjectMembers(node *ast.Node, contextualType *Type) *Type {
	key := DiscriminatedContextualTypeKey{nodeId: ast.GetNodeId(node), typeId: contextualType.id}
	if discriminated := c.discriminatedContextualTypes[key]; discriminated != nil {
		return discriminated
	}
	discriminated := c.getMatchingUnionConstituentForObjectLiteral(contextualType, node)
	if discriminated == nil {
		discriminantProperties := core.Filter(node.Properties(), func(p *ast.Node) bool {
			symbol := p.Symbol()
			if symbol == nil {
				return false
			}
			if ast.IsPropertyAssignment(p) {
				return c.isPossiblyDiscriminantValue(p.Initializer()) && c.isDiscriminantProperty(contextualType, symbol.Name)
			}
			if ast.IsShorthandPropertyAssignment(p) {
				return c.isDiscriminantProperty(contextualType, symbol.Name)
			}
			return false
		})
		discriminantMembers := core.Filter(c.getPropertiesOfType(contextualType), func(s *ast.Symbol) bool {
			return s.Flags&ast.SymbolFlagsOptional != 0 && node.Symbol().Members[s.Name] == nil && c.isDiscriminantProperty(contextualType, s.Name)
		})
		discriminator := &ObjectLiteralDiscriminator{c: c, props: discriminantProperties, members: discriminantMembers}
		discriminated = c.discriminateTypeByDiscriminableItems(contextualType, discriminator)
	}
	c.discriminatedContextualTypes[key] = discriminated
	return discriminated
}

func (c *Checker) getMatchingUnionConstituentForObjectLiteral(unionType *Type, node *ast.Node) *Type {
	keyPropertyName := c.getKeyPropertyName(unionType)
	if keyPropertyName != "" {
		propNode := core.Find(node.Properties(), func(p *ast.Node) bool {
			return p.Symbol() != nil && ast.IsPropertyAssignment(p) && p.Symbol().Name == keyPropertyName && c.isPossiblyDiscriminantValue(p.Initializer())
		})
		if propNode != nil {
			propType := c.getContextFreeTypeOfExpression(propNode.Initializer())
			return c.getConstituentTypeForKeyType(unionType, propType)
		}
	}
	return nil
}

// Return true if the given expression is possibly a discriminant value. We limit the kinds of
// expressions we check to those that don't depend on their contextual type in order not to cause
// recursive (and possibly infinite) invocations of getContextualType.
func (c *Checker) isPossiblyDiscriminantValue(node *ast.Node) bool {
	switch node.Kind {
	case ast.KindStringLiteral, ast.KindNumericLiteral, ast.KindBigIntLiteral, ast.KindNoSubstitutionTemplateLiteral, ast.KindTemplateExpression,
		ast.KindTrueKeyword, ast.KindFalseKeyword, ast.KindNullKeyword, ast.KindIdentifier, ast.KindUndefinedKeyword:
		return true
	case ast.KindPropertyAccessExpression, ast.KindParenthesizedExpression:
		return c.isPossiblyDiscriminantValue(node.Expression())
	case ast.KindJsxExpression:
		return node.Expression() == nil || c.isPossiblyDiscriminantValue(node.Expression())
	}
	return false
}

// If the given contextual type contains instantiable types and if a mapper representing
// return type inferences is available, instantiate those types using that mapper.
func (c *Checker) instantiateContextualType(contextualType *Type, node *ast.Node, contextFlags ContextFlags) *Type {
	if contextualType != nil && c.maybeTypeOfKind(contextualType, TypeFlagsInstantiable) {
		inferenceContext := c.getInferenceContext(node)
		// If no inferences have been made, and none of the type parameters for which we are inferring
		// specify default types, nothing is gained from instantiating as type parameters would just be
		// replaced with their constraints similar to the apparent type.
		if inferenceContext != nil {
			if contextFlags&ContextFlagsSignature != 0 && core.Some(inferenceContext.inferences, hasInferenceCandidatesOrDefault) {
				// For contextual signatures we incorporate all inferences made so far, e.g. from return
				// types as well as arguments to the left in a function call.
				t := c.instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper)
				if t.flags&TypeFlagsAnyOrUnknown == 0 {
					return t
				}
			}
			if inferenceContext.returnMapper != nil {
				// For other purposes (e.g. determining whether to produce literal types) we only
				// incorporate inferences made from the return type in a function call. We remove
				// the 'boolean' type from the contextual type such that contextually typed boolean
				// literals actually end up widening to 'boolean' (see #48363).
				t := c.instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper)
				if t.flags&TypeFlagsAnyOrUnknown == 0 {
					if t.flags&TypeFlagsUnion != 0 && containsType(t.Types(), c.regularFalseType) && containsType(t.Types(), c.regularTrueType) {
						return c.filterType(t, func(t *Type) bool {
							return t != c.regularFalseType && t != c.regularTrueType
						})
					}
					return t
				}
			}
		}
	}
	return contextualType
}

// This function is similar to instantiateType, except that (a) it only instantiates types that
// are classified as instantiable (i.e. it doesn't instantiate object types), and (b) it performs
// no reductions on instantiated union types.
func (c *Checker) instantiateInstantiableTypes(t *Type, mapper *TypeMapper) *Type {
	if t.flags&TypeFlagsInstantiable != 0 {
		return c.instantiateType(t, mapper)
	}
	if t.flags&TypeFlagsUnion != 0 {
		return c.getUnionTypeEx(core.Map(t.Types(), func(t *Type) *Type {
			return c.instantiateInstantiableTypes(t, mapper)
		}), UnionReductionNone, nil, nil)
	}
	if t.flags&TypeFlagsIntersection != 0 {
		return c.getIntersectionType(core.Map(t.Types(), func(t *Type) *Type {
			return c.instantiateInstantiableTypes(t, mapper)
		}))
	}
	return t
}

func (c *Checker) pushCachedContextualType(node *ast.Node) {
	c.pushContextualType(node, c.getContextualType(node, ContextFlagsNone), true /*isCache*/)
}

func (c *Checker) pushContextualType(node *ast.Node, t *Type, isCache bool) {
	c.contextualInfos = append(c.contextualInfos, ContextualInfo{node, t, isCache})
}

func (c *Checker) popContextualType() {
	lastIndex := len(c.contextualInfos) - 1
	c.contextualInfos[lastIndex] = ContextualInfo{}
	c.contextualInfos = c.contextualInfos[:lastIndex]
}

func (c *Checker) findContextualNode(node *ast.Node, includeCaches bool) int {
	for i, info := range c.contextualInfos {
		if node == info.node && (includeCaches || !info.isCache) {
			return i
		}
	}
	return -1
}

// Returns true if the given expression contains (at any level of nesting) a function or arrow expression
// that is subject to contextual typing.
func (c *Checker) isContextSensitive(node *ast.Node) bool {
	switch node.Kind {
	case ast.KindFunctionExpression, ast.KindArrowFunction, ast.KindMethodDeclaration, ast.KindFunctionDeclaration:
		return c.isContextSensitiveFunctionLikeDeclaration(node)
	case ast.KindObjectLiteralExpression:
		return core.Some(node.Properties(), c.isContextSensitive)
	case ast.KindArrayLiteralExpression:
		return core.Some(node.Elements(), c.isContextSensitive)
	case ast.KindConditionalExpression:
		return c.isContextSensitive(node.AsConditionalExpression().WhenTrue) || c.isContextSensitive(node.AsConditionalExpression().WhenFalse)
	case ast.KindBinaryExpression:
		binary := node.AsBinaryExpression()
		return ast.NodeKindIs(binary.OperatorToken, ast.KindBarBarToken, ast.KindQuestionQuestionToken) && (c.isContextSensitive(binary.Left) || c.isContextSensitive(binary.Right))
	case ast.KindPropertyAssignment:
		return c.isContextSensitive(node.Initializer())
	case ast.KindParenthesizedExpression:
		return c.isContextSensitive(node.Expression())
	case ast.KindJsxAttributes:
		return core.Some(node.Properties(), c.isContextSensitive) || ast.IsJsxOpeningElement(node.Parent) && core.Some(node.Parent.Parent.Children().Nodes, c.isContextSensitive)
	case ast.KindJsxAttribute:
		// If there is no initializer, JSX attribute has a boolean value of true which is not context sensitive.
		initializer := node.Initializer()
		return initializer != nil && c.isContextSensitive(initializer)
	case ast.KindJsxExpression:
		// It is possible to that node.expression is undefined (e.g <div x={} />)
		expression := node.Expression()
		return expression != nil && c.isContextSensitive(expression)
	case ast.KindYieldExpression:
		expression := node.Expression()
		return expression != nil && c.isContextSensitive(expression)
	}
	return false
}

func (c *Checker) isContextSensitiveFunctionLikeDeclaration(node *ast.Node) bool {
	return ast.HasContextSensitiveParameters(node) || c.hasContextSensitiveReturnExpression(node) || c.hasContextSensitiveYieldExpression(node)
}

func (c *Checker) hasContextSensitiveReturnExpression(node *ast.Node) bool {
	if node.TypeParameters() != nil || node.Type() != nil {
		return false
	}
	body := node.Body()
	if body == nil {
		return false
	}
	if !ast.IsBlock(body) {
		return c.isContextSensitive(body)
	}
	return ast.ForEachReturnStatement(body, func(statement *ast.Node) bool {
		return statement.Expression() != nil && c.isContextSensitive(statement.Expression())
	})
}

func (c *Checker) hasContextSensitiveYieldExpression(node *ast.Node) bool {
	return ast.GetFunctionFlags(node)&ast.FunctionFlagsGenerator != 0 && node.Body() != nil && forEachYieldExpression(node.Body(), c.isContextSensitive)
}

func (c *Checker) pushInferenceContext(node *ast.Node, context *InferenceContext) {
	c.inferenceContextInfos = append(c.inferenceContextInfos, InferenceContextInfo{node, context})
}

func (c *Checker) popInferenceContext() {
	lastIndex := len(c.inferenceContextInfos) - 1
	c.inferenceContextInfos[lastIndex] = InferenceContextInfo{}
	c.inferenceContextInfos = c.inferenceContextInfos[:lastIndex]
}

func (c *Checker) getInferenceContext(node *ast.Node) *InferenceContext {
	for i := len(c.inferenceContextInfos) - 1; i >= 0; i-- {
		if isNodeDescendantOf(node, c.inferenceContextInfos[i].node) {
			return c.inferenceContextInfos[i].context
		}
	}
	return nil
}

func (c *Checker) getTypeFacts(t *Type, mask TypeFacts) TypeFacts {
	return c.getTypeFactsWorker(t, mask) & mask
}

func (c *Checker) hasTypeFacts(t *Type, mask TypeFacts) bool {
	return c.getTypeFacts(t, mask) != 0
}

func (c *Checker) getTypeFactsWorker(t *Type, callerOnlyNeeds TypeFacts) TypeFacts {
	if t.flags&(TypeFlagsIntersection|TypeFlagsInstantiable) != 0 {
		t = c.getBaseConstraintOfType(t)
		if t == nil {
			t = c.unknownType
		}
	}
	flags := t.flags
	switch {
	case flags&(TypeFlagsString|TypeFlagsStringMapping) != 0:
		if c.strictNullChecks {
			return TypeFactsStringStrictFacts
		}
		return TypeFactsStringFacts
	case flags&(TypeFlagsStringLiteral|TypeFlagsTemplateLiteral) != 0:
		isEmpty := flags&TypeFlagsStringLiteral != 0 && getStringLiteralValue(t) == ""
		if c.strictNullChecks {
			if isEmpty {
				return TypeFactsEmptyStringStrictFacts
			}
			return TypeFactsNonEmptyStringStrictFacts
		}
		if isEmpty {
			return TypeFactsEmptyStringFacts
		}
		return TypeFactsNonEmptyStringFacts
	case flags&(TypeFlagsNumber|TypeFlagsEnum) != 0:
		if c.strictNullChecks {
			return TypeFactsNumberStrictFacts
		}
		return TypeFactsNumberFacts
	case flags&TypeFlagsNumberLiteral != 0:
		isZero := getNumberLiteralValue(t) == 0
		if c.strictNullChecks {
			if isZero {
				return TypeFactsZeroNumberStrictFacts
			}
			return TypeFactsNonZeroNumberStrictFacts
		}
		if isZero {
			return TypeFactsZeroNumberFacts
		}
		return TypeFactsNonZeroNumberFacts
	case flags&TypeFlagsBigInt != 0:
		if c.strictNullChecks {
			return TypeFactsBigIntStrictFacts
		}
		return TypeFactsBigIntFacts
	case flags&TypeFlagsBigIntLiteral != 0:
		isZero := isZeroBigInt(t)
		if c.strictNullChecks {
			if isZero {
				return TypeFactsZeroBigIntStrictFacts
			}
			return TypeFactsNonZeroBigIntStrictFacts
		}
		if isZero {
			return TypeFactsZeroBigIntFacts
		}
		return TypeFactsNonZeroBigIntFacts
	case flags&TypeFlagsBoolean != 0:
		if c.strictNullChecks {
			return TypeFactsBooleanStrictFacts
		}
		return TypeFactsBooleanFacts
	case flags&TypeFlagsBooleanLike != 0:
		isFalse := t == c.falseType || t == c.regularFalseType
		if c.strictNullChecks {
			if isFalse {
				return TypeFactsFalseStrictFacts
			}
			return TypeFactsTrueStrictFacts
		}
		if isFalse {
			return TypeFactsFalseFacts
		}
		return TypeFactsTrueFacts
	case flags&TypeFlagsObject != 0:
		var possibleFacts TypeFacts
		if c.strictNullChecks {
			possibleFacts = TypeFactsEmptyObjectStrictFacts | TypeFactsFunctionStrictFacts | TypeFactsObjectStrictFacts
		} else {
			possibleFacts = TypeFactsEmptyObjectFacts | TypeFactsFunctionFacts | TypeFactsObjectFacts
		}
		if (callerOnlyNeeds & possibleFacts) == 0 {
			// If the caller doesn't care about any of the facts that we could possibly produce,
			// return zero so we can skip resolving members.
			return TypeFactsNone
		}
		switch {
		case t.objectFlags&ObjectFlagsAnonymous != 0 && c.isEmptyObjectType(t):
			if c.strictNullChecks {
				return TypeFactsEmptyObjectStrictFacts
			}
			return TypeFactsEmptyObjectFacts
		case c.isFunctionObjectType(t):
			if c.strictNullChecks {
				return TypeFactsFunctionStrictFacts
			}
			return TypeFactsFunctionFacts
		case c.strictNullChecks:
			return TypeFactsObjectStrictFacts
		}
		return TypeFactsObjectFacts
	case flags&TypeFlagsVoid != 0:
		return TypeFactsVoidFacts
	case flags&TypeFlagsUndefined != 0:
		return TypeFactsUndefinedFacts
	case flags&TypeFlagsNull != 0:
		return TypeFactsNullFacts
	case flags&TypeFlagsESSymbolLike != 0:
		if c.strictNullChecks {
			return TypeFactsSymbolStrictFacts
		} else {
			return TypeFactsSymbolFacts
		}
	case flags&TypeFlagsNonPrimitive != 0:
		if c.strictNullChecks {
			return TypeFactsObjectStrictFacts
		} else {
			return TypeFactsObjectFacts
		}
	case flags&TypeFlagsNever != 0:
		return TypeFactsNone
	case flags&TypeFlagsUnion != 0:
		var facts TypeFacts
		for _, t := range t.Types() {
			facts |= c.getTypeFactsWorker(t, callerOnlyNeeds)
		}
		return facts
	case flags&TypeFlagsIntersection != 0:
		return c.getIntersectionTypeFacts(t, callerOnlyNeeds)
	}
	return TypeFactsUnknownFacts
}

func (c *Checker) getIntersectionTypeFacts(t *Type, callerOnlyNeeds TypeFacts) TypeFacts {
	// When an intersection contains a primitive type we ignore object type constituents as they are
	// presumably type tags. For example, in string & { __kind__: "name" } we ignore the object type.
	ignoreObjects := c.maybeTypeOfKind(t, TypeFlagsPrimitive)
	// When computing the type facts of an intersection type, certain type facts are computed as `and`
	// and others are computed as `or`.
	oredFacts := TypeFactsNone
	andedFacts := TypeFactsAll
	for _, t := range t.Types() {
		if !(ignoreObjects && t.flags&TypeFlagsObject != 0) {
			f := c.getTypeFactsWorker(t, callerOnlyNeeds)
			oredFacts |= f
			andedFacts &= f
		}
	}
	return oredFacts&TypeFactsOrFactsMask | andedFacts&TypeFactsAndFactsMask
}

func isZeroBigInt(t *Type) bool {
	return getBigIntLiteralValue(t) == jsnum.PseudoBigInt{}
}

func (c *Checker) isFunctionObjectType(t *Type) bool {
	if t.objectFlags&ObjectFlagsEvolvingArray != 0 {
		return false
	}
	// We do a quick check for a "bind" property before performing the more expensive subtype
	// check. This gives us a quicker out in the common case where an object type is not a function.
	resolved := c.resolveStructuredTypeMembers(t)
	return len(resolved.signatures) != 0 || resolved.members["bind"] != nil && c.isTypeSubtypeOf(t, c.globalFunctionType)
}

func (c *Checker) getTypeWithFacts(t *Type, include TypeFacts) *Type {
	return c.filterType(t, func(t *Type) bool {
		return c.hasTypeFacts(t, include)
	})
}

// This function is similar to getTypeWithFacts, except that in strictNullChecks mode it replaces type
// unknown with the union {} | null | undefined (and reduces that accordingly), and it intersects remaining
// instantiable types with {}, {} | null, or {} | undefined in order to remove null and/or undefined.
func (c *Checker) getAdjustedTypeWithFacts(t *Type, facts TypeFacts) *Type {
	reduced := c.recombineUnknownType(c.getTypeWithFacts(core.IfElse(c.strictNullChecks && t.flags&TypeFlagsUnknown != 0, c.unknownUnionType, t), facts))
	if c.strictNullChecks {
		switch facts {
		case TypeFactsNEUndefined:
			return c.removeNullableByIntersection(reduced, TypeFactsEQUndefined, TypeFactsEQNull, TypeFactsIsNull, c.nullType)
		case TypeFactsNENull:
			return c.removeNullableByIntersection(reduced, TypeFactsEQNull, TypeFactsEQUndefined, TypeFactsIsUndefined, c.undefinedType)
		case TypeFactsNEUndefinedOrNull, TypeFactsTruthy:
			return c.mapType(reduced, func(t *Type) *Type {
				if c.hasTypeFacts(t, TypeFactsEQUndefinedOrNull) {
					return c.getGlobalNonNullableTypeInstantiation(t)
				}
				return t
			})
		}
	}
	return reduced
}

func (c *Checker) removeNullableByIntersection(t *Type, targetFacts TypeFacts, otherFacts TypeFacts, otherIncludesFacts TypeFacts, otherType *Type) *Type {
	facts := c.getTypeFacts(t, TypeFactsEQUndefined|TypeFactsEQNull|TypeFactsIsUndefined|TypeFactsIsNull)
	// Simply return the type if it never compares equal to the target nullable.
	if facts&targetFacts == 0 {
		return t
	}
	// By default we intersect with a union of {} and the opposite nullable.
	emptyAndOtherUnion := c.getUnionType([]*Type{c.emptyObjectType, otherType})
	// For each constituent type that can compare equal to the target nullable, intersect with the above union
	// if the type doesn't already include the opposite nullable and the constituent can compare equal to the
	// opposite nullable; otherwise, just intersect with {}.
	return c.mapType(t, func(t *Type) *Type {
		if c.hasTypeFacts(t, targetFacts) {
			if facts&otherIncludesFacts == 0 && c.hasTypeFacts(t, otherFacts) {
				return c.getIntersectionType([]*Type{t, emptyAndOtherUnion})
			}
			return c.getIntersectionType([]*Type{t, c.emptyObjectType})
		}
		return t
	})
}

func (c *Checker) recombineUnknownType(t *Type) *Type {
	if t == c.unknownUnionType {
		return c.unknownType
	}
	return t
}

func (c *Checker) getGlobalNonNullableTypeInstantiation(t *Type) *Type {
	alias := c.getGlobalNonNullableTypeAliasOrNil()
	if alias != nil {
		return c.getTypeAliasInstantiation(alias, []*Type{t}, nil)
	}
	return c.getIntersectionType([]*Type{t, c.emptyObjectType})
}

func (c *Checker) convertAutoToAny(t *Type) *Type {
	switch {
	case t == c.autoType:
		return c.anyType
	case t == c.autoArrayType:
		return c.anyArrayType
	}
	return t
}

// Gets the "awaited type" of a type.
// @param type The type to await.
// @param withAlias When `true`, wraps the "awaited type" in `Awaited<T>` if needed.
// @remarks The "awaited type" of an expression is its "promised type" if the expression is a
// Promise-like type; otherwise, it is the type of the expression. This is used to reflect
// The runtime behavior of the `await` keyword.
func (c *Checker) checkAwaitedType(t *Type, withAlias bool, errorNode *ast.Node, diagnosticMessage *diagnostics.Message) *Type {
	var awaitedType *Type
	if withAlias {
		awaitedType = c.getAwaitedTypeEx(t, errorNode, diagnosticMessage)
	} else {
		awaitedType = c.getAwaitedTypeNoAliasEx(t, errorNode, diagnosticMessage)
	}
	if awaitedType != nil {
		return awaitedType
	}
	return c.errorType
}

// Gets the "awaited type" of a type.
//
// The "awaited type" of an expression is its "promised type" if the expression is a
// Promise-like type; otherwise, it is the type of the expression. If the "promised
// type" is itself a Promise-like, the "promised type" is recursively unwrapped until a
// non-promise type is found.
//
// This is used to reflect the runtime behavior of the `await` keyword.
func (c *Checker) getAwaitedType(t *Type) *Type {
	return c.getAwaitedTypeEx(t, nil, nil)
}

func (c *Checker) getAwaitedTypeEx(t *Type, errorNode *ast.Node, diagnosticMessage *diagnostics.Message, args ...any) *Type {
	awaitedType := c.getAwaitedTypeNoAliasEx(t, errorNode, diagnosticMessage, args...)
	if awaitedType != nil {
		return c.createAwaitedTypeIfNeeded(awaitedType)
	}
	return nil
}

// Gets the "awaited type" of a type without introducing an `Awaited<T>` wrapper.
func (c *Checker) getAwaitedTypeNoAlias(t *Type) *Type {
	return c.getAwaitedTypeNoAliasEx(t, nil, nil)
}

func (c *Checker) getAwaitedTypeNoAliasEx(t *Type, errorNode *ast.Node, diagnosticMessage *diagnostics.Message, args ...any) *Type {
	if IsTypeAny(t) {
		return t
	}
	// If this is already an `Awaited<T>`, just return it. This avoids `Awaited<Awaited<T>>` in higher-order
	if c.isAwaitedTypeInstantiation(t) {
		return t
	}
	// If we've already cached an awaited type, return a possible `Awaited<T>` for it.
	key := CachedTypeKey{kind: CachedTypeKindAwaitedType, typeId: t.id}
	if awaitedType := c.cachedTypes[key]; awaitedType != nil {
		return awaitedType
	}
	// For a union, get a union of the awaited types of each constituent.
	if t.flags&TypeFlagsUnion != 0 {
		if slices.Contains(c.awaitedTypeStack, t) {
			if errorNode != nil {
				c.error(errorNode, diagnostics.Type_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method)
			}
			return nil
		}
		c.awaitedTypeStack = append(c.awaitedTypeStack, t)
		mapped := c.mapType(t, func(t *Type) *Type { return c.getAwaitedTypeNoAliasEx(t, errorNode, diagnosticMessage, args...) })
		c.awaitedTypeStack = c.awaitedTypeStack[:len(c.awaitedTypeStack)-1]
		c.cachedTypes[key] = mapped
		return mapped
	}
	// If `type` is generic and should be wrapped in `Awaited<T>`, return it.
	if c.isAwaitedTypeNeeded(t) {
		c.cachedTypes[key] = t
		return t
	}
	var thisTypeForError *Type
	promisedType := c.getPromisedTypeOfPromiseEx(t, nil /*errorNode*/, &thisTypeForError)
	if promisedType != nil {
		if t == promisedType || slices.Contains(c.awaitedTypeStack, promisedType) {
			// Verify that we don't have a bad actor in the form of a promise whose
			// promised type is the same as the promise type, or a mutually recursive
			// promise. If so, we return undefined as we cannot guess the shape. If this
			// were the actual case in the JavaScript, this Promise would never resolve.
			//
			// An example of a bad actor with a singly-recursive promise type might
			// be:
			//
			//  interface BadPromise {
			//      then(
			//          onfulfilled: (value: BadPromise) => any,
			//          onrejected: (error: any) => any): BadPromise;
			//  }
			//
			// The above interface will pass the PromiseLike check, and return a
			// promised type of `BadPromise`. Since this is a self reference, we
			// don't want to keep recursing ad infinitum.
			//
			// An example of a bad actor in the form of a mutually-recursive
			// promise type might be:
			//
			//  interface BadPromiseA {
			//      then(
			//          onfulfilled: (value: BadPromiseB) => any,
			//          onrejected: (error: any) => any): BadPromiseB;
			//  }
			//
			//  interface BadPromiseB {
			//      then(
			//          onfulfilled: (value: BadPromiseA) => any,
			//          onrejected: (error: any) => any): BadPromiseA;
			//  }
			//
			if errorNode != nil {
				c.error(errorNode, diagnostics.Type_is_referenced_directly_or_indirectly_in_the_fulfillment_callback_of_its_own_then_method)
			}
			return nil
		}
		// Keep track of the type we're about to unwrap to avoid bad recursive promise types.
		// See the comments above for more information.
		c.awaitedTypeStack = append(c.awaitedTypeStack, t)
		awaitedType := c.getAwaitedTypeNoAliasEx(promisedType, errorNode, diagnosticMessage, args...)
		c.awaitedTypeStack = c.awaitedTypeStack[:len(c.awaitedTypeStack)-1]
		if awaitedType == nil {
			return nil
		}
		c.cachedTypes[key] = awaitedType
		return awaitedType
	}
	// The type was not a promise, so it could not be unwrapped any further.
	// As long as the type does not have a callable "then" property, it is
	// safe to return the type; otherwise, an error is reported and we return
	// undefined.
	//
	// An example of a non-promise "thenable" might be:
	//
	//  await { then(): void {} }
	//
	// The "thenable" does not match the minimal definition for a promise. When
	// a Promise/A+-compatible or ES6 promise tries to adopt this value, the promise
	// will never settle. We treat this as an error to help flag an early indicator
	// of a runtime problem. If the user wants to return this value from an async
	// function, they would need to wrap it in some other value. If they want it to
	// be treated as a promise, they can cast to <any>.
	if c.isThenableType(t) {
		if errorNode != nil {
			var diagnostic *ast.Diagnostic
			if thisTypeForError != nil {
				diagnostic = NewDiagnosticForNode(errorNode, diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1, c.TypeToString(t), c.TypeToString(thisTypeForError))
			}
			c.addDiagnostic(NewDiagnosticChainForNode(diagnostic, errorNode, diagnosticMessage, args...))
		}
		return nil
	}
	c.cachedTypes[key] = t
	return t
}

func (c *Checker) isAwaitedTypeInstantiation(t *Type) bool {
	if t.flags&TypeFlagsConditional != 0 {
		awaitedSymbol := c.getGlobalAwaitedSymbolOrNil()
		return awaitedSymbol != nil && t.alias != nil && t.alias.symbol == awaitedSymbol && len(t.alias.typeArguments) == 1
	}
	return false
}

func (c *Checker) isAwaitedTypeNeeded(t *Type) bool {
	// If this is already an `Awaited<T>`, we shouldn't wrap it. This helps to avoid `Awaited<Awaited<T>>` in higher-order.
	if IsTypeAny(t) || c.isAwaitedTypeInstantiation(t) {
		return false
	}
	// We only need `Awaited<T>` if `T` contains possibly non-primitive types.
	if c.isGenericObjectType(t) {
		baseConstraint := c.getBaseConstraintOfType(t)
		// We only need `Awaited<T>` if `T` is a type variable that has no base constraint, or the base constraint of `T` is `any`, `unknown`, `{}`, `object`,
		// or is promise-like.
		if baseConstraint != nil {
			return baseConstraint.flags&TypeFlagsAnyOrUnknown != 0 || c.isEmptyObjectType(baseConstraint) || someType(baseConstraint, c.isThenableType)
		}
		return c.maybeTypeOfKind(t, TypeFlagsTypeVariable)
	}
	return false
}

func (c *Checker) createAwaitedTypeIfNeeded(t *Type) *Type {
	// We wrap type `T` in `Awaited<T>` based on the following conditions:
	// - `T` is not already an `Awaited<U>`, and
	// - `T` is generic, and
	// - One of the following applies:
	//   - `T` has no base constraint, or
	//   - The base constraint of `T` is `any`, `unknown`, `object`, or `{}`, or
	//   - The base constraint of `T` is an object type with a callable `then` method.
	if c.isAwaitedTypeNeeded(t) {
		awaitedType := c.tryCreateAwaitedType(t)
		if awaitedType != nil {
			return awaitedType
		}
	}
	return t
}

func (c *Checker) tryCreateAwaitedType(t *Type) *Type {
	// Nothing to do if `Awaited<T>` doesn't exist
	awaitedSymbol := c.getGlobalAwaitedSymbol()
	if awaitedSymbol != nil {
		// Unwrap unions that may contain `Awaited<T>`, otherwise its possible to manufacture an `Awaited<Awaited<T> | U>` where
		// an `Awaited<T | U>` would suffice.
		return c.getTypeAliasInstantiation(awaitedSymbol, []*Type{c.unwrapAwaitedType(t)}, nil)
	}
	return nil
}

// For a generic `Awaited<T>`, gets `T`.
func (c *Checker) unwrapAwaitedType(t *Type) *Type {
	switch {
	case t.flags&TypeFlagsUnion != 0:
		return c.mapType(t, c.unwrapAwaitedType)
	case c.isAwaitedTypeInstantiation(t):
		return t.alias.typeArguments[0]
	}
	return t
}

func (c *Checker) isThenableType(t *Type) bool {
	if c.allTypesAssignableToKind(c.getBaseConstraintOrType(t), TypeFlagsPrimitive|TypeFlagsNever) {
		// primitive types cannot be considered "thenable" since they are not objects.
		return false
	}
	thenFunction := c.getTypeOfPropertyOfType(t, "then")
	return thenFunction != nil && len(c.getSignaturesOfType(c.getTypeWithFacts(thenFunction, TypeFactsNEUndefinedOrNull), SignatureKindCall)) != 0
}

func (c *Checker) getAwaitedTypeOfPromise(t *Type) *Type {
	return c.getAwaitedTypeOfPromiseEx(t, nil, nil)
}

func (c *Checker) getAwaitedTypeOfPromiseEx(t *Type, errorNode *ast.Node, diagnosticMessage *diagnostics.Message, args ...any) *Type {
	promisedType := c.getPromisedTypeOfPromiseEx(t, errorNode, nil)
	if promisedType != nil {
		return c.getAwaitedTypeEx(promisedType, errorNode, diagnosticMessage, args...)
	}
	return nil
}

// Check if a parameter or catch variable (or their bindings elements) is assigned anywhere
func (c *Checker) isSomeSymbolAssigned(rootDeclaration *ast.Node) bool {
	return c.isSomeSymbolAssignedWorker(rootDeclaration.Name())
}

func (c *Checker) isSomeSymbolAssignedWorker(node *ast.Node) bool {
	if node.Kind == ast.KindIdentifier {
		return c.isSymbolAssigned(c.getSymbolOfDeclaration(node.Parent))
	}
	return core.Some(node.Elements(), func(e *ast.Node) bool {
		return e.Name() != nil && c.isSomeSymbolAssignedWorker(e.Name())
	})
}

func (c *Checker) getTargetType(t *Type) *Type {
	if t.objectFlags&ObjectFlagsReference != 0 {
		return t.AsTypeReference().target
	}
	return t
}

func (c *Checker) getNarrowableTypeForReference(t *Type, reference *ast.Node, checkMode CheckMode) *Type {
	if c.isNoInferType(t) {
		t = t.AsSubstitutionType().baseType
	}
	// When the type of a reference is or contains an instantiable type with a union type constraint, and
	// when the reference is in a constraint position (where it is known we'll obtain the apparent type) or
	// has a contextual type containing no top-level instantiables (meaning constraints will determine
	// assignability), we substitute constraints for all instantiables in the type of the reference to give
	// control flow analysis an opportunity to narrow it further. For example, for a reference of a type
	// parameter type 'T extends string | undefined' with a contextual type 'string', we substitute
	// 'string | undefined' to give control flow analysis the opportunity to narrow to type 'string'.
	substituteConstraints := checkMode&CheckModeInferential == 0 && someType(t, c.isGenericTypeWithUnionConstraint) && (c.isConstraintPosition(t, reference) || c.hasContextualTypeWithNoGenericTypes(reference, checkMode))
	if substituteConstraints {
		return c.mapType(t, c.getBaseConstraintOrType)
	}
	return t
}

func (c *Checker) isConstraintPosition(t *Type, node *ast.Node) bool {
	parent := node.Parent
	// In an element access obj[x], we consider obj to be in a constraint position, except when obj is of
	// a generic type without a nullable constraint and x is a generic type. This is because when both obj
	// and x are of generic types T and K, we want the resulting type to be T[K].
	return ast.IsPropertyAccessExpression(parent) || ast.IsQualifiedName(parent) ||
		(ast.IsCallExpression(parent) || ast.IsNewExpression(parent)) && parent.Expression() == node ||
		ast.IsElementAccessExpression(parent) && parent.Expression() == node && !(someType(t, c.isGenericTypeWithoutNullableConstraint) && c.isGenericIndexType(c.getTypeOfExpression(parent.AsElementAccessExpression().ArgumentExpression)))
}

func (c *Checker) isGenericTypeWithUnionConstraint(t *Type) bool {
	if t.flags&TypeFlagsIntersection != 0 {
		return core.Some(t.AsIntersectionType().types, c.isGenericTypeWithUnionConstraint)
	}
	return t.flags&TypeFlagsInstantiable != 0 && c.getBaseConstraintOrType(t).flags&(TypeFlagsNullable|TypeFlagsUnion) != 0
}

func (c *Checker) isGenericTypeWithoutNullableConstraint(t *Type) bool {
	if t.flags&TypeFlagsIntersection != 0 {
		return core.Some(t.AsIntersectionType().types, c.isGenericTypeWithoutNullableConstraint)
	}
	return t.flags&TypeFlagsInstantiable != 0 && !c.maybeTypeOfKind(c.getBaseConstraintOrType(t), TypeFlagsNullable)
}

func (c *Checker) hasContextualTypeWithNoGenericTypes(node *ast.Node, checkMode CheckMode) bool {
	// Computing the contextual type for a child of a JSX element involves resolving the type of the
	// element's tag name, so we exclude that here to avoid circularities.
	// If check mode has `CheckMode.RestBindingElement`, we skip binding pattern contextual types,
	// as we want the type of a rest element to be generic when possible.
	if (ast.IsIdentifier(node) || ast.IsPropertyAccessExpression(node) || ast.IsElementAccessExpression(node)) &&
		!((ast.IsJsxOpeningElement(node.Parent) || ast.IsJsxSelfClosingElement(node.Parent)) && node.Parent.TagName() == node) {
		contextualType := c.getContextualType(node, core.IfElse(checkMode&CheckModeRestBindingElement != 0, ContextFlagsSkipBindingPatterns, ContextFlagsNone))
		if contextualType != nil {
			return !c.isGenericType(contextualType)
		}
	}
	return false
}

func (c *Checker) getNonUndefinedType(t *Type) *Type {
	typeOrConstraint := t
	if someType(t, c.isGenericTypeWithUndefinedConstraint) {
		typeOrConstraint = c.mapType(t, func(t *Type) *Type {
			if t.flags&TypeFlagsInstantiable != 0 {
				return c.getBaseConstraintOrType(t)
			}
			return t
		})
	}
	return c.getTypeWithFacts(typeOrConstraint, TypeFactsNEUndefined)
}

func (c *Checker) isGenericTypeWithUndefinedConstraint(t *Type) bool {
	if t.flags&TypeFlagsInstantiable != 0 {
		constraint := c.getBaseConstraintOfType(t)
		if constraint != nil {
			return c.maybeTypeOfKind(constraint, TypeFlagsUndefined)
		}
	}
	return false
}

func (c *Checker) getActualTypeVariable(t *Type) *Type {
	if t.flags&TypeFlagsSubstitution != 0 {
		return c.getActualTypeVariable(t.AsSubstitutionType().baseType)
	}
	if t.flags&TypeFlagsIndexedAccess != 0 && (t.AsIndexedAccessType().objectType.flags&TypeFlagsSubstitution != 0 || t.AsIndexedAccessType().indexType.flags&TypeFlagsSubstitution != 0) {
		return c.getIndexedAccessType(c.getActualTypeVariable(t.AsIndexedAccessType().objectType), c.getActualTypeVariable(t.AsIndexedAccessType().indexType))
	}
	return t
}

func (c *Checker) GetSymbolAtLocation(node *ast.Node) *ast.Symbol {
	// !!!
	// const node = getParseTreeNode(nodeIn);

	// set ignoreErrors: true because any lookups invoked by the API shouldn't cause any new errors
	return c.getSymbolAtLocation(ast.GetReparsedNodeForNode(node), true /*ignoreErrors*/)
}

// Returns the symbol associated with a given AST node. Do *not* use this function in the checker itself! It should
// be used only by the language service and external tools. The semantics of the function are deliberately "fuzzy"
// and aim to just return *some* symbol for the node. To obtain the symbol associated with a node for type checking
// purposes, use appropriate function for the context, e.g. `getResolvedSymbol` for an expression identifier,
// `getSymbolOfDeclaration` for a declaration, etc.
func (c *Checker) getSymbolAtLocation(node *ast.Node, ignoreErrors bool) *ast.Symbol {
	if ast.IsSourceFile(node) {
		if ast.IsExternalOrCommonJSModule(node.AsSourceFile()) {
			return c.getMergedSymbol(node.Symbol())
		}
		return nil
	}
	parent := node.Parent
	grandParent := parent.Parent

	if node.Flags&ast.NodeFlagsInWithStatement != 0 {
		// We cannot answer semantic questions within a with block, do not proceed any further
		return nil
	}

	if ast.IsDeclarationNameOrImportPropertyName(node) {
		// This is a declaration, call getSymbolOfNode
		parentSymbol := c.getSymbolOfDeclaration(parent)
		if ast.IsImportOrExportSpecifier(parent) && parent.PropertyName() == node {
			return c.getImmediateAliasedSymbol(parentSymbol)
		}
		return parentSymbol
	} else if ast.IsLiteralComputedPropertyDeclarationName(node) {
		return c.getSymbolOfDeclaration(grandParent)
	}

	if ast.IsIdentifier(node) {
		if isInRightSideOfImportOrExportAssignment(node) {
			return c.getSymbolOfNameOrPropertyAccessExpression(node)
		} else if ast.IsBindingElement(parent) && ast.IsObjectBindingPattern(grandParent) && node == parent.PropertyName() {
			typeOfPattern := c.getTypeOfNode(grandParent)
			if propertyDeclaration := c.getPropertyOfType(typeOfPattern, node.Text()); propertyDeclaration != nil {
				return propertyDeclaration
			}
		} else if ast.IsMetaProperty(parent) && parent.Name() == node {
			metaProp := parent.AsMetaProperty()
			if metaProp.KeywordToken == ast.KindNewKeyword && node.Text() == "target" {
				// `target` in `new.target`
				return c.checkNewTargetMetaProperty(parent).symbol
			}
			// The `meta` in `import.meta` could be given `getTypeOfNode(parent).symbol` (the `ImportMeta` interface symbol), but
			// we have a fake expression type made for other reasons already, whose transient `meta`
			// member should more exactly be the kind of (declarationless) symbol we want.
			// (See #44364 and #45031 for relevant implementation PRs)
			if metaProp.KeywordToken == ast.KindImportKeyword && node.Text() == "meta" {
				return c.getGlobalImportMetaExpressionType().AsObjectType().members["meta"]
			}
			// no other meta properties are valid syntax, thus no others should have symbols
			return nil
		} else if ast.IsJSDocParameterTag(parent) && parent.Name() == node {
			if fn := ast.GetNodeAtPosition(ast.GetSourceFileOfNode(node), node.Pos(), false); fn != nil && ast.IsFunctionLike(fn) {
				for _, param := range fn.Parameters() {
					if ast.IsIdentifier(param.Name()) && param.Name().Text() == node.Text() {
						return c.getSymbolOfNode(param)
					}
				}
			}
		}
	}

	switch node.Kind {
	case ast.KindIdentifier, ast.KindPrivateIdentifier, ast.KindPropertyAccessExpression, ast.KindQualifiedName:
		if !ast.IsThisInTypeQuery(node) {
			return c.getSymbolOfNameOrPropertyAccessExpression(node)
		}
		fallthrough
	case ast.KindThisKeyword:
		container := c.getThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/)
		if ast.IsFunctionLike(container) {
			sig := c.getSignatureFromDeclaration(container)
			if sig.thisParameter != nil {
				return sig.thisParameter
			}
		}
		if ast.IsInExpressionContext(node) {
			return c.checkExpression(node).symbol
		}
		fallthrough
	case ast.KindThisType:
		return c.getTypeFromThisTypeNode(node).symbol
	case ast.KindSuperKeyword:
		return c.checkExpression(node).symbol
	case ast.KindConstructorKeyword:
		// constructor keyword for an overload, should take us to the definition if it exist
		constructorDeclaration := parent
		if constructorDeclaration != nil && constructorDeclaration.Kind == ast.KindConstructor {
			return constructorDeclaration.Parent.Symbol()
		}
		return nil
	case ast.KindStringLiteral, ast.KindNoSubstitutionTemplateLiteral:
		// 1). import x = require("./mo/*gotToDefinitionHere*/d")
		// 2). External module name in an import declaration
		// 3). Require in Javascript
		// 4). type A = import("./f/*gotToDefinitionHere*/oo")
		if (ast.IsExternalModuleImportEqualsDeclaration(grandParent) && ast.GetExternalModuleImportEqualsDeclarationExpression(grandParent) == node) ||
			((parent.Kind == ast.KindImportDeclaration || parent.Kind == ast.KindJSImportDeclaration || parent.Kind == ast.KindExportDeclaration) && ast.GetExternalModuleName(parent) == node) ||
			ast.IsVariableDeclarationInitializedToRequire(grandParent) || ast.IsImportCall(parent) ||
			(ast.IsLiteralTypeNode(parent) && ast.IsLiteralImportTypeNode(grandParent) && grandParent.AsImportTypeNode().Argument == parent) {
			return c.resolveExternalModuleName(node, node, ignoreErrors)
		}
		if ast.IsCallExpression(parent) && ast.IsBindableObjectDefinePropertyCall(parent) && parent.Arguments()[1] == node {
			return c.getSymbolOfDeclaration(parent)
		}
		fallthrough
	case ast.KindNumericLiteral:
		// index access
		var objectType *Type
		if ast.IsElementAccessExpression(parent) {
			if parent.AsElementAccessExpression().ArgumentExpression == node {
				objectType = c.getTypeOfExpression(parent.Expression())
			}
		} else if ast.IsLiteralTypeNode(parent) && ast.IsIndexedAccessTypeNode(grandParent) {
			objectType = c.getTypeFromTypeNode(grandParent.AsIndexedAccessTypeNode().ObjectType)
		}

		if objectType != nil {
			return c.getPropertyOfType(objectType, node.Text())
		}
		return nil
	case ast.KindDefaultKeyword, ast.KindFunctionKeyword, ast.KindEqualsGreaterThanToken, ast.KindClassKeyword:
		return c.getSymbolOfNode(node.Parent)
	case ast.KindImportType:
		if ast.IsLiteralImportTypeNode(node) {
			return c.getSymbolAtLocation(node.AsImportTypeNode().Argument.AsLiteralTypeNode().Literal, ignoreErrors)
		}
		return nil
	case ast.KindExportKeyword:
		if ast.IsExportAssignment(parent) {
			if parent.Symbol() == nil {
				panic("Symbol should be defined")
			}
			return parent.Symbol()
		}
		return nil
	case ast.KindImportKeyword:
		if ast.IsMetaProperty(node.Parent) && node.Parent.Text() == "defer" {
			return nil
		}
		fallthrough
	case ast.KindNewKeyword:
		if ast.IsMetaProperty(parent) {
			return c.checkMetaPropertyKeyword(parent).symbol
		}
		return nil
	case ast.KindInstanceOfKeyword:
		if ast.IsBinaryExpression(parent) {
			t := c.getTypeOfExpression(parent.AsBinaryExpression().Right)
			hasInstanceMethodType := c.getSymbolHasInstanceMethodOfObjectType(t)
			if hasInstanceMethodType != nil && hasInstanceMethodType.symbol != nil {
				return hasInstanceMethodType.symbol
			}
			return t.symbol
		}
		return nil
	case ast.KindMetaProperty:
		return c.checkExpression(node).symbol
	case ast.KindJsxNamespacedName:
		if ast.IsJsxTagName(node) && isJsxIntrinsicTagName(node) {
			symbol := c.getIntrinsicTagSymbol(node.Parent)
			if symbol == c.unknownSymbol {
				return nil
			}
			return symbol
		}
		fallthrough
	default:
		return nil
	}
}

func (c *Checker) getIndexSignaturesAtLocation(node *ast.Node) []*ast.Node {
	var signatures []*ast.Node
	if ast.IsIdentifier(node) && ast.IsPropertyAccessExpression(node.Parent) && node.Parent.Name() == node {
		keyType := c.getLiteralTypeFromPropertyName(node)
		objectType := c.getTypeOfExpression(node.Parent.Expression())
		for _, t := range objectType.Distributed() {
			for _, info := range c.getApplicableIndexInfos(t, keyType) {
				if info.declaration != nil {
					signatures = core.AppendIfUnique(signatures, info.declaration)
				}
			}
		}
	}
	return signatures
}

func (c *Checker) getSymbolOfNameOrPropertyAccessExpression(name *ast.Node) *ast.Symbol {
	if ast.IsDeclarationName(name) {
		return c.getSymbolOfNode(name.Parent)
	}
	if name.Parent.Kind == ast.KindExportAssignment && ast.IsEntityNameExpression(name) {
		// Even an entity name expression that doesn't resolve as an entityname may still typecheck as a property access expression
		success := c.resolveEntityName(
			name,
			/*all meanings*/ ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias,
			true /*ignoreErrors*/, false /*dontResolveAlias*/, nil, /*location*/
		)
		if success != nil && success != c.unknownSymbol {
			return success
		}
	} else if ast.IsEntityName(name) && isInRightSideOfImportOrExportAssignment(name) {
		// Since we already checked for ExportAssignment, this really could only be an Import
		importEqualsDeclaration := ast.FindAncestorKind(name, ast.KindImportEqualsDeclaration)
		if importEqualsDeclaration == nil {
			panic("ImportEqualsDeclaration should be defined")
		}
		return c.getSymbolOfPartOfRightHandSideOfImportEquals(name)
	}

	if ast.IsEntityName(name) {
		possibleImportNode := isImportTypeQualifierPart(name)
		if possibleImportNode != nil {
			c.getTypeFromTypeNode(possibleImportNode)
			sym := c.getResolvedSymbolOrNil(name)
			return core.IfElse(sym == c.unknownSymbol, nil, sym)
		}
	}

	for ast.IsRightSideOfQualifiedNameOrPropertyAccess(name) {
		name = name.Parent
	}

	if isInNameOfExpressionWithTypeArguments(name) {
		var meaning ast.SymbolFlags
		if name.Parent.Kind == ast.KindExpressionWithTypeArguments {
			// An 'ExpressionWithTypeArguments' may appear in type space (interface Foo extends Bar<T>),
			// value space (return foo<T>), or both(class Foo extends Bar<T>); ensure the meaning matches.
			meaning = core.IfElse(ast.IsPartOfTypeNode(name), ast.SymbolFlagsType, ast.SymbolFlagsValue)

			// In a class 'extends' clause we are also looking for a value.
			if ast.IsExpressionWithTypeArgumentsInClassExtendsClause(name.Parent) {
				meaning = meaning | ast.SymbolFlagsValue
			}
		} else {
			meaning = ast.SymbolFlagsNamespace
		}

		meaning = meaning | ast.SymbolFlagsAlias
		var entityNameSymbol *ast.Symbol
		if ast.IsEntityNameExpression(name) {
			entityNameSymbol = c.resolveEntityName(name, meaning, true /*ignoreErrors*/, false /*dontResolveAlias*/, nil /*location*/)
		}
		if entityNameSymbol != nil {
			return entityNameSymbol
		}
	}

	if ast.IsExpressionNode(name) {
		if ast.NodeIsMissing(name) {
			// Missing entity name.
			return nil
		}
		isJSDoc := ast.IsJSDocNameReferenceContext(name)
		if ast.IsIdentifier(name) {
			if ast.IsJsxTagName(name) && isJsxIntrinsicTagName(name) {
				symbol := c.getIntrinsicTagSymbol(name.Parent)
				return core.IfElse(symbol == c.unknownSymbol, nil, symbol)
			}
			meaning := core.IfElse(isJSDoc, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, ast.SymbolFlagsValue)
			var location *ast.Node
			if isJSDoc {
				location = ast.GetHostSignatureFromJSDoc(name)
			}
			result := c.resolveEntityName(name, meaning, true /*ignoreErrors*/, true /*dontResolveAlias*/, location)
			if result == nil && isJSDoc {
				if container := ast.FindAncestor(name, ast.IsClassOrInterfaceLike); container != nil {
					symbol := c.getSymbolOfDeclaration(container)
					// Handle unqualified references to class static members and class or interface instance members
					if result = c.getMergedSymbol(c.getSymbol(c.getExportsOfSymbol(symbol), name.Text(), meaning)); result == nil {
						result = c.getPropertyOfType(c.getDeclaredTypeOfSymbol(symbol), name.Text())
					}
				}
			}
			return result
		} else if ast.IsPrivateIdentifier(name) {
			return c.getSymbolForPrivateIdentifierExpression(name)
		} else if ast.IsPropertyAccessExpression(name) || ast.IsQualifiedName(name) {
			links := c.symbolNodeLinks.Get(name)
			if links.resolvedSymbol != nil {
				return links.resolvedSymbol
			}
			if ast.IsPropertyAccessExpression(name) {
				c.checkPropertyAccessExpression(name, CheckModeNormal, false /*writeOnly*/)
				if links.resolvedSymbol == nil && !ast.IsPrivateIdentifier(name.Name()) {
					links.resolvedSymbol = c.getApplicableIndexSymbol(
						c.checkExpressionCached(name.Expression()),
						c.getLiteralTypeFromPropertyName(name.Name()),
					)
				}
			} else {
				c.checkQualifiedName(name, CheckModeNormal)
			}
			if links.resolvedSymbol == nil && isJSDoc && ast.IsQualifiedName(name) {
				return c.resolveJSDocMemberName(name)
			}
			return links.resolvedSymbol
		}
	} else if ast.IsEntityName(name) && isTypeReferenceIdentifier(name) {
		meaning := core.IfElse(name.Parent.Kind == ast.KindTypeReference, ast.SymbolFlagsType, ast.SymbolFlagsNamespace)
		symbol := c.resolveEntityName(name, meaning, true /*ignoreErrors*/, true /*dontResolveAlias*/, nil /*location*/)
		if symbol != nil && symbol != c.unknownSymbol {
			return symbol
		}
		return c.getUnresolvedSymbolForEntityName(name)
	}

	if name.Parent.Kind == ast.KindTypePredicate {
		return c.resolveEntityName(
			name,
			ast.SymbolFlagsFunctionScopedVariable, /*meaning*/
			true,                                  /*ignoreErrors*/
			false,                                 /*dontResolveAlias*/
			nil,                                   /*location*/
		)
	}
	return nil
}

func (c *Checker) isThisPropertyAndThisTyped(node *ast.Node) bool {
	if node.Expression().Kind == ast.KindThisKeyword {
		container := c.getThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/)
		if ast.IsFunctionLike(container) {
			containingLiteral := getContainingObjectLiteral(container)
			if containingLiteral != nil {
				contextualType := c.getApparentTypeOfContextualType(containingLiteral, ContextFlagsNone)
				t := c.getThisTypeOfObjectLiteralFromContextualType(containingLiteral, contextualType)
				return t != nil && !IsTypeAny(t)
			}
		}
	}
	return false
}

func (c *Checker) getTypeOfNode(node *ast.Node) *Type {
	if ast.IsSourceFile(node) && !ast.IsExternalOrCommonJSModule(node.AsSourceFile()) {
		return c.errorType
	}

	if node.Flags&ast.NodeFlagsInWithStatement != 0 {
		// We cannot answer semantic questions within a with block, do not proceed any further
		return c.errorType
	}

	classDecl, isImplements := ast.TryGetClassImplementingOrExtendingExpressionWithTypeArguments(node)
	var classType *Type
	if classDecl != nil {
		classType = c.getDeclaredTypeOfClassOrInterface(c.getSymbolOfDeclaration(classDecl))
	}

	if ast.IsPartOfTypeNode(node) {
		typeFromTypeNode := c.getTypeFromTypeNode(node)
		if classType != nil {
			return c.getTypeWithThisArgument(
				typeFromTypeNode,
				classType.AsInterfaceType().thisType,
				false, /*needApparentType*/
			)
		}
		return typeFromTypeNode
	}

	if ast.IsExpressionNode(node) {
		return c.getRegularTypeOfExpression(node)
	}

	if classType != nil && !isImplements {
		// A SyntaxKind.ExpressionWithTypeArguments is considered a type node, except when it occurs in the
		// extends clause of a class. We handle that case here.
		baseType := core.FirstOrNil(c.getBaseTypes(classType))
		if baseType != nil {
			return c.getTypeWithThisArgument(baseType, classType.AsInterfaceType().thisType, false /*needApparentType*/)
		}
		return c.errorType
	}

	if ast.IsTypeDeclaration(node) {
		// In this case, we call getSymbolOfDeclaration instead of getSymbolAtLocation because it is a declaration
		symbol := c.getSymbolOfDeclaration(node)
		return c.getDeclaredTypeOfSymbol(symbol)
	}

	if ast.IsTypeDeclarationName(node) {
		symbol := c.getSymbolAtLocation(node, false /*ignoreErrors*/)
		if symbol != nil {
			return c.getDeclaredTypeOfSymbol(symbol)
		}
		return c.errorType
	}

	if ast.IsBindingElement(node) {
		t := c.getTypeForVariableLikeDeclaration(node, true /*includeOptionality*/, CheckModeNormal)
		if t != nil {
			return t
		}
		return c.errorType
	}

	if ast.IsDeclaration(node) {
		// In this case, we call getSymbolOfDeclaration instead of getSymbolLAtocation because it is a declaration
		symbol := c.getSymbolOfDeclaration(node)
		if symbol != nil {
			return c.getTypeOfSymbol(symbol)
		}
		return c.errorType
	}

	if ast.IsDeclarationNameOrImportPropertyName(node) {
		symbol := c.getSymbolAtLocation(node, false /*ignoreErrors*/)
		if symbol != nil {
			return c.getTypeOfSymbol(symbol)
		}
		return c.errorType
	}

	if ast.IsBindingPattern(node) {
		t := c.getTypeForVariableLikeDeclaration(node.Parent, true /*includeOptionality*/, CheckModeNormal)
		if t != nil {
			return t
		}
		return c.errorType
	}

	if isInRightSideOfImportOrExportAssignment(node) {
		symbol := c.getSymbolAtLocation(node, false /*ignoreErrors*/)
		if symbol != nil {
			declaredType := c.getDeclaredTypeOfSymbol(symbol)
			if !c.isErrorType(declaredType) {
				return declaredType
			}
			return c.getTypeOfSymbol(symbol)
		}
	}

	if ast.IsMetaProperty(node.Parent) && node.Parent.AsMetaProperty().KeywordToken == node.Kind {
		return c.checkMetaPropertyKeyword(node.Parent)
	}

	if ast.IsImportAttributes(node) {
		return c.getGlobalImportAttributesType()
	}

	return c.errorType
}

func (c *Checker) getThisTypeOfObjectLiteralFromContextualType(containingLiteral *ast.Node, contextualType *Type) *Type {
	literal := containingLiteral
	t := contextualType
	for t != nil {
		thisType := c.getThisTypeFromContextualType(t)
		if thisType != nil {
			return thisType
		}
		if literal.Parent.Kind != ast.KindPropertyAssignment {
			break
		}
		literal = literal.Parent.Parent
		t = c.getApparentTypeOfContextualType(literal, ContextFlagsNone)
	}
	return nil
}

func (c *Checker) getThisTypeFromContextualType(t *Type) *Type {
	return c.mapType(t, func(t *Type) *Type {
		if t.flags&TypeFlagsIntersection != 0 {
			for _, t := range t.AsIntersectionType().types {
				typeArg := c.getThisTypeArgument(t)
				if typeArg != nil {
					return typeArg
				}
			}
			return nil
		} else {
			return c.getThisTypeArgument(t)
		}
	})
}

func (c *Checker) getThisTypeArgument(t *Type) *Type {
	if t.objectFlags&ObjectFlagsReference != 0 && t.AsTypeReference().target == c.globalThisType {
		return c.getTypeArguments(t)[0]
	}
	return nil
}

func (c *Checker) getApplicableIndexInfos(t *Type, keyType *Type) []*IndexInfo {
	return core.Filter(c.getIndexInfosOfType(t), func(info *IndexInfo) bool { return c.isApplicableIndexType(keyType, info.keyType) })
}

func (c *Checker) getApplicableIndexSymbol(t *Type, keyType *Type) *ast.Symbol {
	if info := c.getApplicableIndexInfo(t, keyType); info != nil && info != c.anyBaseTypeIndexInfo {
		if info.indexSymbol == nil {
			var declarations []*ast.Node
			if info.declaration != nil {
				declarations = []*ast.Node{info.declaration}
			} else {
				for _, info := range c.getIndexInfosOfType(t) {
					if info.declaration != nil && c.isApplicableIndexType(keyType, info.keyType) {
						declarations = append(declarations, info.declaration)
					}
				}
			}
			if len(declarations) != 0 {
				symbol := c.newSymbol(ast.SymbolFlagsProperty, ast.InternalSymbolNameIndex)
				symbol.CheckFlags |= ast.CheckFlagsIndexSymbol
				symbol.Declarations = declarations
				symbol.ValueDeclaration = declarations[0]
				symbol.Parent = t.symbol
				links := c.valueSymbolLinks.Get(symbol)
				links.resolvedType = info.valueType
				info.indexSymbol = symbol
			}
		}
		return info.indexSymbol
	}
	return nil
}

func (c *Checker) getRegularTypeOfExpression(expr *ast.Node) *Type {
	if ast.IsRightSideOfQualifiedNameOrPropertyAccess(expr) {
		expr = expr.Parent
	}
	return c.getRegularTypeOfLiteralType(c.getTypeOfExpression(expr))
}

func (c *Checker) containsArgumentsReference(node *ast.Node) bool {
	if node.Body() == nil {
		return false
	}

	if containsArguments, ok := c.cachedArgumentsReferenced[node]; ok {
		return containsArguments
	}

	var visit func(node *ast.Node) bool
	visit = func(node *ast.Node) bool {
		if node == nil {
			return false
		}
		switch node.Kind {
		case ast.KindIdentifier:
			return node.Text() == c.argumentsSymbol.Name && c.IsArgumentsSymbol(c.getResolvedSymbol(node))
		case ast.KindPropertyDeclaration, ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
			if ast.IsComputedPropertyName(node.Name()) {
				return visit(node.Name())
			}
		case ast.KindPropertyAccessExpression, ast.KindElementAccessExpression:
			return visit(node.Expression())
		case ast.KindPropertyAssignment:
			return visit(node.Initializer())
		}
		if nodeStartsNewLexicalEnvironment(node) || ast.IsPartOfTypeNode(node) {
			return false
		}
		return node.ForEachChild(visit)
	}

	containsArguments := visit(node.Body())
	c.cachedArgumentsReferenced[node] = containsArguments
	return containsArguments
}

func (c *Checker) GetTypeAtLocation(node *ast.Node) *Type {
	return c.getTypeOfNode(ast.GetReparsedNodeForNode(node))
}

func (c *Checker) GetEmitResolver() *EmitResolver {
	c.emitResolverOnce.Do(func() {
		c.emitResolver = newEmitResolver(c)
	})

	return c.emitResolver
}

func (c *Checker) GetAliasedSymbol(symbol *ast.Symbol) *ast.Symbol {
	return c.resolveAlias(symbol)
}
