30926 lines
1.3 MiB
30926 lines
1.3 MiB
package checker
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"iter"
|
|
"maps"
|
|
"math"
|
|
"slices"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"unicode/utf8"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/evaluator"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/module"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/modulespecifiers"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
)
|
|
|
|
// 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
|
|
)
|
|
|
|
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 string
|
|
}
|
|
|
|
// CachedSignatureKey
|
|
|
|
type CachedSignatureKey struct {
|
|
sig *Signature
|
|
key string // Type list key or one of the strings below
|
|
}
|
|
|
|
const (
|
|
SignatureKeyErased string = "-"
|
|
SignatureKeyCanonical string = "*"
|
|
SignatureKeyBase string = "#"
|
|
SignatureKeyInner string = "<"
|
|
SignatureKeyOuter string = ">"
|
|
SignatureKeyImplementation string = "+"
|
|
)
|
|
|
|
// 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
|
|
}
|
|
|
|
// FlowLoopKey
|
|
|
|
type FlowLoopKey struct {
|
|
flowNode *ast.FlowNode
|
|
refKey string
|
|
}
|
|
|
|
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
|
|
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
|
|
}
|
|
|
|
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]
|
|
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
|
|
IsSourceFromProjectReference(path tspath.Path) 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
|
|
TotalInstantiationCount uint32
|
|
instantiationCount uint32
|
|
instantiationDepth uint32
|
|
inlineLevel int
|
|
currentNode *ast.Node
|
|
varianceTypeParameter *Type
|
|
languageVersion core.ScriptTarget
|
|
moduleKind core.ModuleKind
|
|
moduleResolutionKind core.ModuleResolutionKind
|
|
isInferencePartiallyBlocked bool
|
|
legacyDecorators bool
|
|
emitStandardClassFields bool
|
|
allowSyntheticDefaultImports bool
|
|
strictNullChecks bool
|
|
strictFunctionTypes bool
|
|
strictBindCallApply bool
|
|
strictPropertyInitialization bool
|
|
strictBuiltinIteratorReturn bool
|
|
noImplicitAny bool
|
|
noImplicitThis bool
|
|
useUnknownInCatchVariables bool
|
|
exactOptionalPropertyTypes bool
|
|
canCollectSymbolAliasAccessibilityData bool
|
|
wasCanceled bool
|
|
arrayVariances []VarianceFlags
|
|
globals ast.SymbolTable
|
|
evaluate evaluator.Evaluator
|
|
stringLiteralTypes map[string]*Type
|
|
numberLiteralTypes map[jsnum.Number]*Type
|
|
bigintLiteralTypes map[jsnum.PseudoBigInt]*Type
|
|
enumLiteralTypes map[EnumLiteralKey]*Type
|
|
indexedAccessTypes map[string]*Type
|
|
templateLiteralTypes map[string]*Type
|
|
stringMappingTypes map[StringMappingKey]*Type
|
|
uniqueESSymbolTypes map[*ast.Symbol]*Type
|
|
thisExpandoKinds map[*ast.Symbol]thisAssignmentDeclarationKind
|
|
thisExpandoLocations map[*ast.Symbol]*ast.Node
|
|
subtypeReductionCache map[string][]*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
|
|
resolvingSymbol *ast.Symbol
|
|
unresolvedSymbols map[string]*ast.Symbol
|
|
errorTypes map[string]*Type
|
|
globalThisSymbol *ast.Symbol
|
|
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[string]*Type
|
|
unionTypes map[string]*Type
|
|
unionOfUnionTypes map[UnionOfUnionKey]*Type
|
|
intersectionTypes map[string]*Type
|
|
diagnostics ast.DiagnosticsCollection
|
|
suggestionDiagnostics ast.DiagnosticsCollection
|
|
symbolPool core.Pool[ast.Symbol]
|
|
signaturePool core.Pool[Signature]
|
|
indexInfoPool core.Pool[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]
|
|
indexSymbolLinks core.LinkStore[*ast.Symbol, IndexSymbolLinks]
|
|
ReverseMappedSymbolLinks core.LinkStore[*ast.Symbol, ReverseMappedSymbolLinks]
|
|
markedAssignmentSymbolLinks core.LinkStore[*ast.Symbol, MarkedAssignmentSymbolLinks]
|
|
symbolContainerLinks core.LinkStore[*ast.Symbol, ContainingSymbolLinks]
|
|
sourceFileLinks core.LinkStore[*ast.SourceFile, SourceFileLinks]
|
|
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
|
|
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[string]*Type
|
|
ambientModulesOnce sync.Once
|
|
ambientModules []*ast.Symbol
|
|
}
|
|
|
|
func NewChecker(program Program) *Checker {
|
|
program.BindSourceFiles()
|
|
|
|
c := &Checker{}
|
|
c.id = nextCheckerID.Add(1)
|
|
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.UseDefineForClassFields.IsFalse() && c.compilerOptions.GetEmitScriptTarget() >= core.ScriptTargetES2022
|
|
c.allowSyntheticDefaultImports = c.compilerOptions.GetAllowSyntheticDefaultImports()
|
|
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.indexedAccessTypes = make(map[string]*Type)
|
|
c.templateLiteralTypes = make(map[string]*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[string][]*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.resolvingSymbol = c.newSymbol(ast.SymbolFlagsNone, ast.InternalSymbolNameResolving)
|
|
c.unresolvedSymbols = make(map[string]*ast.Symbol)
|
|
c.errorTypes = make(map[string]*Type)
|
|
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[string]*Type)
|
|
c.unionTypes = make(map[string]*Type)
|
|
c.unionOfUnionTypes = make(map[UnionOfUnionKey]*Type)
|
|
c.intersectionTypes = make(map[string]*Type)
|
|
c.diagnostics = ast.DiagnosticsCollection{}
|
|
c.suggestionDiagnostics = ast.DiagnosticsCollection{}
|
|
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, "any")
|
|
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[string]*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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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
|
|
augmentations := make([][]*ast.Node, 0, len(c.files))
|
|
for _, file := range c.files {
|
|
if !ast.IsExternalOrCommonJSModule(file) {
|
|
c.mergeSymbolTable(c.globals, file.Locals, false, nil)
|
|
}
|
|
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.Symbol.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*/)
|
|
// 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) 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.diagnostics.Add(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,
|
|
GetModuleSymbol: c.getModuleSymbol,
|
|
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,
|
|
GetModuleSymbol: c.getModuleSymbol,
|
|
Lookup: c.getSuggestionForSymbolNameLookup,
|
|
SymbolReferenced: c.symbolReferenced,
|
|
SetRequiresScopeChangeCache: c.setRequiresScopeChangeCache,
|
|
GetRequiresScopeChangeCache: c.getRequiresScopeChangeCache,
|
|
}
|
|
}
|
|
|
|
func (c *Checker) getModuleSymbol(sourceFile *ast.Node) *ast.Symbol {
|
|
result := c.newSymbol(ast.SymbolFlagsModuleExports|ast.SymbolFlagsFunctionScopedVariable, ast.InternalSymbolNameModuleExports)
|
|
result.ValueDeclaration = sourceFile
|
|
return result
|
|
}
|
|
|
|
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
|
|
}
|
|
// Report missing lib first
|
|
suggestedLib := c.getSuggestedLibForNonExistentName(name)
|
|
if suggestedLib != "" {
|
|
c.error(errorLocation, nameNotFoundMessage, name, 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)
|
|
message := core.IfElse(meaning == ast.SymbolFlagsNamespace, diagnostics.Cannot_find_namespace_0_Did_you_mean_1, diagnostics.Cannot_find_name_0_Did_you_mean_1)
|
|
diagnostic := NewDiagnosticForNode(errorLocation, message, name, suggestionName)
|
|
if suggestion.ValueDeclaration != nil {
|
|
diagnostic.AddRelatedInfo(NewDiagnosticForNode(suggestion.ValueDeclaration, diagnostics.X_0_is_declared_here, suggestionName))
|
|
}
|
|
c.diagnostics.Add(diagnostic)
|
|
return
|
|
}
|
|
// And then fall back to unspecified "not found"
|
|
c.error(errorLocation, nameNotFoundMessage, name)
|
|
}
|
|
|
|
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 ast.IsComputedPropertyName(node) || ast.IsPropertySignatureDeclaration(node) {
|
|
node = node.Parent
|
|
}
|
|
if ast.IsTypeLiteralNode(node) && len(node.AsTypeLiteralNode().Members.Nodes) == 1 {
|
|
t := c.getDeclaredTypeOfSymbol(symbol)
|
|
return t.flags&TypeFlagsUnion != 0 && c.allTypesAssignableToKind(t, TypeFlagsStringOrNumberLiteral)
|
|
}
|
|
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) getPrimitiveAliasSymbols() {
|
|
var symbols []*ast.Symbol
|
|
for _, name := range []string{"string", "number", "boolean", "object", "bigint", "symbol"} {
|
|
symbols = append(symbols, c.newSymbol(ast.SymbolFlagsTypeAlias, name))
|
|
}
|
|
}
|
|
|
|
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*/)
|
|
}
|
|
|
|
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
|
|
}
|
|
allSymbols := slices.Collect(maps.Values(symbols))
|
|
if meaning&ast.SymbolFlagsGlobalLookup != 0 {
|
|
for _, s := range []string{"stringString", "numberNumber", "booleanBoolean", "objectObject", "bigintBigInt", "symbolSymbol"} {
|
|
if _, ok := symbols[s[len(s)/2:]]; ok {
|
|
allSymbols = append(allSymbols, c.newSymbol(ast.SymbolFlagsTypeAlias, s[:len(s)/2]))
|
|
}
|
|
}
|
|
}
|
|
c.sortSymbols(allSymbols)
|
|
return c.getSpellingSuggestionForName(name, allSymbols, 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 []*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)
|
|
}
|
|
|
|
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.Symbol().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.AsPropertyDeclaration().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.IsParameter(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.IsParameter(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 || (getFunctionFlags(n)&FunctionFlagsAsyncGenerator != 0)) {
|
|
return false
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// stopAtAnyPropertyDeclaration 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
|
|
if stopAtAnyPropertyDeclaration && (ast.IsPropertyDeclaration(declaration) && node.Parent == declaration.Parent || ast.IsParameterPropertyDeclaration(declaration, declaration.Parent) && node.Parent == declaration.Parent.Parent) {
|
|
return true
|
|
}
|
|
case ast.KindBlock:
|
|
switch node.Parent.Kind {
|
|
case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (c *Checker) getTypeOnlyAliasDeclaration(symbol *ast.Symbol) *ast.Node {
|
|
return c.getTypeOnlyAliasDeclarationEx(symbol, ast.SymbolFlagsNone)
|
|
}
|
|
|
|
func (c *Checker) getTypeOnlyAliasDeclarationEx(symbol *ast.Symbol, include ast.SymbolFlags) *ast.Node {
|
|
if symbol.Flags&ast.SymbolFlagsAlias == 0 {
|
|
return nil
|
|
}
|
|
links := c.aliasSymbolLinks.Get(symbol)
|
|
if !links.typeOnlyDeclarationResolved {
|
|
// We need to set a WIP value here to prevent reentrancy during `getImmediateAliasedSymbol` which, paradoxically, can depend on this
|
|
links.typeOnlyDeclarationResolved = true
|
|
resolved := c.resolveSymbol(symbol)
|
|
// While usually the alias will have been marked during the pass by the full typecheck, we may still need to calculate the alias declaration now
|
|
var immediateTarget *ast.Symbol
|
|
if c.getDeclarationOfAliasSymbol(symbol) != nil {
|
|
immediateTarget = c.getImmediateAliasedSymbol(symbol)
|
|
}
|
|
c.markSymbolOfAliasDeclarationIfTypeOnly(core.FirstOrNil(symbol.Declarations), immediateTarget, resolved, true /*overwriteEmpty*/, nil, "")
|
|
}
|
|
if include == ast.SymbolFlagsNone {
|
|
return links.typeOnlyDeclaration
|
|
}
|
|
if links.typeOnlyDeclaration != nil {
|
|
var resolved *ast.Symbol
|
|
if links.typeOnlyDeclaration.Kind == ast.KindExportDeclaration {
|
|
name := links.typeOnlyExportStarName
|
|
if name == "" {
|
|
name = symbol.Name
|
|
}
|
|
resolved = c.resolveSymbol(c.getExportsOfModule(links.typeOnlyDeclaration.Symbol().Parent)[name])
|
|
} else {
|
|
resolved = c.resolveAlias(links.typeOnlyDeclaration.Symbol())
|
|
}
|
|
if c.getSymbolFlags(resolved)&include != 0 {
|
|
return links.typeOnlyDeclaration
|
|
}
|
|
}
|
|
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, true /*dontRecursivelyResolve*/)
|
|
}
|
|
|
|
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) {
|
|
if SkipTypeChecking(sourceFile, c.compilerOptions, c.program, false) {
|
|
return
|
|
}
|
|
c.checkSourceFile(ctx, sourceFile)
|
|
}
|
|
|
|
func (c *Checker) checkSourceFile(ctx context.Context, sourceFile *ast.SourceFile) {
|
|
c.checkNotCanceled()
|
|
links := c.sourceFileLinks.Get(sourceFile)
|
|
if !links.typeChecked {
|
|
c.ctx = ctx
|
|
// 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 ctx.Err() == nil {
|
|
// This relies on the results of other lazy diagnostics, so must be computed after them
|
|
if !sourceFile.IsDeclarationFile && (c.compilerOptions.NoUnusedLocals.IsTrue() || c.compilerOptions.NoUnusedParameters.IsTrue()) {
|
|
c.checkUnusedIdentifiers(links.identifierCheckNodes)
|
|
}
|
|
if !sourceFile.IsDeclarationFile {
|
|
c.checkUnusedRenamedBindingElements()
|
|
}
|
|
} else {
|
|
c.wasCanceled = true
|
|
}
|
|
c.ctx = nil
|
|
links.typeChecked = true
|
|
}
|
|
}
|
|
|
|
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
|
|
c.currentNode = node
|
|
c.instantiationCount = 0
|
|
c.checkSourceElementWorker(node)
|
|
c.currentNode = saveCurrentNode
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *Checker) checkSourceElementWorker(node *ast.Node) {
|
|
if node.Flags&ast.NodeFlagsHasJSDoc != 0 {
|
|
for _, jsdoc := range node.JSDoc(nil) {
|
|
c.checkJSDocComments(jsdoc)
|
|
if tags := jsdoc.AsJSDoc().Tags; tags != nil {
|
|
for _, tag := range tags.Nodes {
|
|
c.checkJSDocComments(tag)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
kind := node.Kind
|
|
if kind >= ast.KindFirstStatement && kind <= ast.KindLastStatement {
|
|
flowNode := node.FlowNodeData().FlowNode
|
|
if flowNode != nil && !c.isReachableFlowNode(flowNode) {
|
|
c.errorOrSuggestion(c.compilerOptions.AllowUnreachableCode == core.TSFalse, node, diagnostics.Unreachable_code_detected)
|
|
}
|
|
}
|
|
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, ast.KindJSExportAssignment:
|
|
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)
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
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.AsVoidExpression().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*/, nil); 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.AsTypeParameter().Expression; expr != nil {
|
|
c.grammarErrorOnFirstToken(expr, diagnostics.Type_expected)
|
|
}
|
|
tpNode := node.AsTypeParameter()
|
|
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 {
|
|
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) 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.compilerOptions.ErasableSyntaxOnly.IsTrue() {
|
|
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)
|
|
// 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)
|
|
}
|
|
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 {
|
|
functionFlags := getFunctionFlags(node)
|
|
if (functionFlags & (FunctionFlagsInvalid | FunctionFlagsGenerator)) == 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 & FunctionFlagsAsyncGenerator) == 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)
|
|
}
|
|
}
|
|
|
|
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)
|
|
firstDeclaration := ast.GetDeclarationOfKind(symbol, node.Kind)
|
|
// Only type check the symbol once
|
|
if node == firstDeclaration {
|
|
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().AsBlock().Statements.Nodes {
|
|
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())
|
|
}
|
|
|
|
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) && d.Flags&ast.NodeFlagsDeprecated != 0 }) {
|
|
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(getTagNameOfNode(node))
|
|
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.diagnostics.Add(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.AsBindingPattern().Elements.Nodes {
|
|
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
|
|
checkProperty := func(symbol *ast.Symbol, 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
|
|
}
|
|
if state := names[symbol.Name]; state != 2 {
|
|
if state == 1 {
|
|
c.reportDuplicateMemberErrors(node, symbol.Name, true, isStatic, diagnostics.Duplicate_identifier_0)
|
|
}
|
|
names[symbol.Name] = state + 1
|
|
}
|
|
}
|
|
}
|
|
for _, member := range node.Members() {
|
|
if ast.IsConstructorDeclaration(member) {
|
|
for _, param := range member.Parameters() {
|
|
if ast.IsParameterPropertyDeclaration(param, member) && !ast.IsBindingPattern(param.Name()) {
|
|
checkProperty(c.getSymbolOfDeclaration(param), 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)))
|
|
}
|
|
// When a property has multiple declarations, check that only one of those declarations is in this object
|
|
// type declaration (multiple merged object types are permitted to each declare the same property).
|
|
if ast.IsPropertyDeclaration(member) && !ast.HasAccessorModifier(member) || ast.IsPropertySignatureDeclaration(member) {
|
|
checkProperty(symbol, 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.AsTupleTypeNode().Elements.Nodes
|
|
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)
|
|
}
|
|
typeParameterNode := node.AsInferTypeNode().TypeParameter
|
|
c.checkSourceElement(typeParameterNode)
|
|
symbol := c.getSymbolOfDeclaration(typeParameterNode)
|
|
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(), core.IfElse(node.Token == ast.KindWithKeyword,
|
|
diagnostics.Type_import_attributes_should_have_exactly_one_key_resolution_mode_with_value_import_or_require,
|
|
diagnostics.Type_import_assertions_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(), core.IfElse(node.Token == ast.KindWithKeyword,
|
|
diagnostics.X_resolution_mode_is_the_only_valid_key_for_type_import_attributes,
|
|
diagnostics.X_resolution_mode_is_the_only_valid_key_for_type_import_assertions))
|
|
}
|
|
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.AsTypeParameter().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 := 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,
|
|
// if the javascript file comes before the typescript file and both contain same name functions,
|
|
// checkFunctionOrConstructorSymbol wouldn't be called if we didnt ignore javascript function.
|
|
firstDeclaration := core.Find(localSymbol.Declarations, func(declaration *ast.Node) bool {
|
|
return declaration.Kind == node.Kind && declaration.Flags&ast.NodeFlagsJavaScriptFile == 0
|
|
})
|
|
// Only type check the symbol once
|
|
if node == firstDeclaration {
|
|
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 || core.Some(node.Parameters(), func(p *ast.Node) bool { return p.Type() != nil }) {
|
|
c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_may_not_occur_with_a_param_or_returns_tag)
|
|
}
|
|
}
|
|
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&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) {
|
|
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(ast.GetNameOfDeclaration(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 := 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, 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.AsDoStatement().Statement)
|
|
c.checkTruthinessExpression(node.Expression(), CheckModeNormal)
|
|
}
|
|
|
|
func (c *Checker) checkWhileStatement(node *ast.Node) {
|
|
c.checkGrammarStatementInAmbientContext(node)
|
|
c.checkTruthinessExpression(node.Expression(), CheckModeNormal)
|
|
c.checkSourceElement(node.AsWhileStatement().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)
|
|
}
|
|
} // 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 := 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 := 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&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.GetRangeOfTokenAtPosition(sourceFile, node.Pos()).Pos()
|
|
end := node.AsWithStatement().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.AsCaseOrDefaultClause().Statements.Nodes)
|
|
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.AsLabeledStatement().Label.Text() == labelText {
|
|
c.grammarErrorOnNode(labelNode, diagnostics.Duplicate_label_0, labelText)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
c.checkSourceElement(labeledStatement.Statement)
|
|
}
|
|
|
|
func (c *Checker) checkThrowStatement(node *ast.Node) {
|
|
throwExpr := node.AsThrowStatement().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*/)
|
|
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.checkTypeForDuplicateIndexSignatures(node)
|
|
c.checkPropertyInitialization(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.AsTypeParameter().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.AsTypeParameter().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
|
|
}
|
|
declaredProp := member.Symbol()
|
|
if 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.diagnostics.Add(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.UseDefineForClassFields.IsTrue() {
|
|
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.AsPropertyDeclaration().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 !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, hasAbstractModifier)
|
|
if !baseHasAbstract {
|
|
message := core.IfElse(ast.IsParameter(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 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, 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 with computed names here. Static properties cannot be in conflict with indexers,
|
|
// and properties with literal names were already checked.
|
|
if !ast.IsStatic(member) && !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.diagnostics.Add(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) checkTypeForDuplicateIndexSignatures(node *ast.Node) {
|
|
if ast.IsInterfaceDeclaration(node) {
|
|
// in case of merging interface declaration it is possible that we'll enter this check procedure several times for every declaration
|
|
// to prevent this run check only for the first declaration of a given kind
|
|
if symbol := c.getSymbolOfDeclaration(node); len(symbol.Declarations) != 0 && symbol.Declarations[0] != node {
|
|
return
|
|
}
|
|
}
|
|
// 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) && !hasAbstractModifier(node) && !isExclamationToken(node.AsPropertyDeclaration().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
|
|
firstInterfaceDecl := ast.GetDeclarationOfKind(symbol, ast.KindInterfaceDeclaration)
|
|
if node == firstInterfaceDecl {
|
|
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.checkTypeForDuplicateIndexSignatures(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.diagnostics.Add(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.compilerOptions.ErasableSyntaxOnly.IsTrue() && 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)
|
|
firstDeclaration := ast.GetDeclarationOfKind(enumSymbol, node.Kind)
|
|
if node == firstDeclaration {
|
|
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 {
|
|
tokenRange := getNonModifierTokenRangeOfNode(node)
|
|
c.suggestionDiagnostics.Add(ast.NewDiagnostic(ast.GetSourceFileOfNode(node), tokenRange, 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.compilerOptions.ErasableSyntaxOnly.IsTrue() {
|
|
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().AsModuleBlock().Statements.Nodes {
|
|
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.KindJSExportAssignment, 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.AsBindingPattern().Elements.Nodes {
|
|
// mark individual names in binding pattern
|
|
c.checkModuleAugmentationElement(el)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Checker) checkImportDeclaration(node *ast.Node) {
|
|
// 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.AsImportDeclaration().ImportClause
|
|
moduleSpecifier := node.AsImportDeclaration().ModuleSpecifier
|
|
if importClause != nil && !c.checkGrammarImportClause(importClause.AsImportClause()) {
|
|
if importClause.Name() != nil {
|
|
c.checkImportBinding(importClause)
|
|
}
|
|
namedBindings := importClause.AsImportClause().NamedBindings
|
|
if namedBindings != nil {
|
|
if ast.IsNamespaceImport(namedBindings) {
|
|
c.checkImportBinding(namedBindings)
|
|
} else {
|
|
resolvedModule = c.resolveExternalModuleName(node, node.AsImportDeclaration().ModuleSpecifier, false)
|
|
if resolvedModule != nil {
|
|
for _, binding := range namedBindings.AsNamedImports().Elements.Nodes {
|
|
c.checkImportBinding(binding)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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.IsTrue() && importClause == nil {
|
|
c.resolveExternalModuleName(node, moduleSpecifier, false)
|
|
}
|
|
}
|
|
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 {
|
|
diagnostic := core.IfElse(attributes.AsImportAttributes().Token == ast.KindWithKeyword,
|
|
diagnostics.Import_attribute_values_must_be_string_literal_expressions,
|
|
diagnostics.Import_assertion_values_must_be_string_literal_expressions)
|
|
hasError := false
|
|
for _, attr := range attributes.AsImportAttributes().Attributes.Nodes {
|
|
if !ast.IsStringLiteral(attr.AsImportAttribute().Value) {
|
|
hasError = true
|
|
c.error(attr.AsImportAttribute().Value, diagnostic)
|
|
}
|
|
}
|
|
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*/)
|
|
}
|
|
}
|
|
|
|
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 {
|
|
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)
|
|
isImportAttributes := node.AsImportAttributes().Token == ast.KindWithKeyword
|
|
if isTypeOnly && override != core.ResolutionModeNone {
|
|
return // Other grammar checks do not apply to type-only imports with resolution mode assertions
|
|
}
|
|
|
|
if !c.moduleKind.SupportsImportAttributes() {
|
|
if isImportAttributes {
|
|
c.grammarErrorOnNode(node, diagnostics.Import_attributes_are_only_supported_when_the_module_option_is_set_to_esnext_node18_node20_nodenext_or_preserve)
|
|
} else {
|
|
c.grammarErrorOnNode(node, diagnostics.Import_assertions_are_only_supported_when_the_module_option_is_set_to_esnext_node18_node20_nodenext_or_preserve)
|
|
}
|
|
return
|
|
}
|
|
|
|
if core.ModuleKindNode20 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext && !isImportAttributes {
|
|
c.grammarErrorOnNode(node, diagnostics.Import_assertions_have_been_replaced_by_import_attributes_Use_with_instead_of_assert)
|
|
return
|
|
}
|
|
|
|
if moduleSpecifier := getModuleSpecifierFromNode(declaration); moduleSpecifier != nil {
|
|
if c.getEmitSyntaxForModuleSpecifierExpression(moduleSpecifier) == core.ModuleKindCommonJS {
|
|
if isImportAttributes {
|
|
c.grammarErrorOnNode(node, diagnostics.Import_attributes_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls)
|
|
} else {
|
|
c.grammarErrorOnNode(node, diagnostics.Import_assertions_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
if isTypeOnly {
|
|
c.grammarErrorOnNode(node, core.IfElse(isImportAttributes,
|
|
diagnostics.Import_attributes_cannot_be_used_with_type_only_imports_or_exports,
|
|
diagnostics.Import_assertions_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.compilerOptions.ErasableSyntaxOnly.IsTrue() && 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.AsImportEqualsDeclaration().IsTypeOnly {
|
|
c.grammarErrorOnNode(node, diagnostics.An_import_alias_cannot_use_import_type)
|
|
}
|
|
} else {
|
|
if core.ModuleKindES2015 <= c.moduleKind && c.moduleKind <= core.ModuleKindESNext && !node.AsImportEqualsDeclaration().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.Node) {
|
|
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.AsNamedExports().Elements.Nodes {
|
|
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*/)
|
|
}
|
|
}
|
|
}
|
|
c.checkImportAttributes(node)
|
|
}
|
|
|
|
func (c *Checker) checkExportSpecifier(node *ast.Node) {
|
|
c.checkAliasSymbol(node)
|
|
hasModuleSpecifier := node.Parent.Parent.AsExportDeclaration().ModuleSpecifier != nil
|
|
c.checkModuleExportName(node.AsExportSpecifier().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*/)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Checker) checkExportAssignment(node *ast.Node) {
|
|
isExportEquals := ast.IsJSExportAssignment(node) || 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.compilerOptions.ErasableSyntaxOnly.IsTrue() && 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]
|
|
if exportEqualsSymbol != nil && c.hasExportedMembers(moduleSymbol) {
|
|
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 {
|
|
for _, declaration := range symbol.Declarations {
|
|
if isNotOverload(declaration) {
|
|
c.error(declaration, diagnostics.Cannot_redeclare_exported_variable_0, id)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
links.exportsChecked = true
|
|
}
|
|
}
|
|
|
|
func (c *Checker) hasExportedMembers(moduleSymbol *ast.Symbol) bool {
|
|
for id := range moduleSymbol.Exports {
|
|
if id != ast.InternalSymbolNameExportEquals {
|
|
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) {
|
|
c.checkSourceElements(node.AsVariableDeclarationList().Declarations.Nodes)
|
|
}
|
|
|
|
func (c *Checker) checkVariableDeclaration(node *ast.Node) {
|
|
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
|
|
}
|
|
// 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.AsBindingPattern().Elements.Nodes)
|
|
}
|
|
// 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.AsBindingPattern().Elements.Nodes, 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) || !nodeCanBeDecorated(c.legacyDecorators, node, node.Parent, node.Parent.Parent) {
|
|
return
|
|
}
|
|
firstDecorator := core.Find(node.ModifierNodes(), ast.IsDecorator)
|
|
if firstDecorator == nil {
|
|
return
|
|
}
|
|
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
|
|
}
|
|
uplevelIteration := c.languageVersion >= core.ScriptTargetES2015
|
|
downlevelIteration := !uplevelIteration && c.compilerOptions.DownlevelIteration == core.TSTrue
|
|
possibleOutOfBounds := c.compilerOptions.NoUncheckedIndexedAccess == core.TSTrue && use&IterationUsePossiblyOutOfBounds != 0
|
|
// Get the iterated type of an `Iterable<T>` or `IterableIterator<T>` only in ES2015
|
|
// or higher, when inside of an async generator or for-await-if, or when
|
|
// downlevelIteration is requested.
|
|
if uplevelIteration || downlevelIteration || allowAsyncIterables {
|
|
// We only report errors for an invalid iterable type in ES2015 or higher.
|
|
iterationTypes := c.getIterationTypesOfIterable(inputType, use, core.IfElse(uplevelIteration, 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 || uplevelIteration {
|
|
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, downlevelIteration)
|
|
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 {
|
|
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 {
|
|
return c.combineIterationTypes(core.Map(t.Types(), func(t *Type) IterationTypes { return c.getIterationTypesOfIterableWorker(t, use, errorNode, noCache) }))
|
|
}
|
|
if use&IterationUseAllowsAsyncIterablesFlag != 0 {
|
|
iterationTypes := c.getIterationTypesOfIterableFast(t, c.asyncIterationTypesResolver)
|
|
if iterationTypes.hasTypes() {
|
|
if use&IterationUseForOfFlag != 0 {
|
|
return c.getAsyncFromSyncIterationTypes(iterationTypes, errorNode)
|
|
}
|
|
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
|
|
}
|
|
}
|
|
var diags []*ast.Diagnostic
|
|
if use&IterationUseAllowsAsyncIterablesFlag != 0 {
|
|
iterationTypes := c.getIterationTypesOfIterableSlow(t, c.asyncIterationTypesResolver, errorNode, &diags)
|
|
if iterationTypes.hasTypes() {
|
|
return iterationTypes
|
|
}
|
|
}
|
|
if use&IterationUseAllowsSyncIterablesFlag != 0 {
|
|
iterationTypes := c.getIterationTypesOfIterableSlow(t, c.syncIterationTypesResolver, errorNode, &diags)
|
|
if iterationTypes.hasTypes() {
|
|
if use&IterationUseAllowsAsyncIterablesFlag != 0 {
|
|
return c.getAsyncFromSyncIterationTypes(iterationTypes, errorNode)
|
|
}
|
|
return iterationTypes
|
|
}
|
|
}
|
|
if errorNode != nil {
|
|
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, downlevelIteration bool) (*diagnostics.Message, bool) {
|
|
if downlevelIteration {
|
|
if allowsStrings {
|
|
return diagnostics.Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator, true
|
|
}
|
|
return diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator, true
|
|
}
|
|
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))
|
|
// 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) && target.Flags&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 {
|
|
debug.Assert(node.Kind != ast.KindVariableDeclaration)
|
|
specifierText := "..."
|
|
if importDeclaration := ast.FindAncestor(node, ast.IsImportOrImportEqualsDeclaration); importDeclaration != nil {
|
|
if moduleSpecifier := TryGetModuleSpecifierFromDeclaration(importDeclaration); moduleSpecifier != nil {
|
|
specifierText = moduleSpecifier.Text()
|
|
}
|
|
}
|
|
identifierText := symbol.Name
|
|
if ast.IsIdentifier(errorNode) {
|
|
identifierText = errorNode.Text()
|
|
}
|
|
importText := "import(\"" + specifierText + "\")." + 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
|
|
}
|
|
targetFlags := c.getSymbolFlags(target)
|
|
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.AssertIsDefined(node.Name(), "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.IsParameter(left) && ast.IsVariableDeclaration(right) || ast.IsVariableDeclaration(left) && ast.IsParameter(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.AsTypeAliasDeclaration().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.KindJSExportAssignment, ast.KindBinaryExpression:
|
|
var expression *ast.Node
|
|
if ast.IsExportAssignment(node) || ast.IsJSExportAssignment(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) {
|
|
return DeclarationSpacesExportValue
|
|
}
|
|
node = expression
|
|
// 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.KindCommonJSExport:
|
|
return DeclarationSpacesExportValue
|
|
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.AsTypeParameter().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.IsParameter(location), UnusedKindParameter, UnusedKindLocal), diagnostic)
|
|
}
|
|
|
|
func (c *Checker) reportUnused(location *ast.Node, kind UnusedKind, diagnostic *ast.Diagnostic) {
|
|
if location.Flags&(ast.NodeFlagsAmbient|ast.NodeFlagsThisNodeOrAnySubNodesHasError) == 0 &&
|
|
(kind == UnusedKindLocal && c.compilerOptions.NoUnusedLocals.IsTrue() ||
|
|
(kind == UnusedKindParameter && c.compilerOptions.NoUnusedParameters.IsTrue())) {
|
|
c.diagnostics.Add(diagnostic)
|
|
}
|
|
}
|
|
|
|
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:
|
|
// 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) {
|
|
continue
|
|
}
|
|
for _, declaration := range local.Declarations {
|
|
switch {
|
|
case ast.IsVariableDeclaration(declaration) || ast.IsParameter(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.AsBindingPattern().Elements.Nodes
|
|
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().AsBindingPattern().Elements.Nodes, 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.AsBindingPattern().Elements.Nodes)
|
|
if node != lastElement && hasDotDotDotToken(lastElement) {
|
|
return false
|
|
}
|
|
}
|
|
if (ast.IsParameter(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.AsNamedImports().Elements.Nodes)
|
|
}
|
|
}
|
|
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.diagnostics.Add(diagnostic)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Checker) checkExpressionStatement(node *ast.Node) {
|
|
// Grammar checking
|
|
c.checkGrammarStatementInAmbientContext(node)
|
|
c.checkExpression(node.AsExpressionStatement().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 {
|
|
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)
|
|
}
|
|
// !!!
|
|
// nodeCheckFlag := NodeCheckFlagsNone
|
|
// if ast.IsStatic(container) || isCallExpression {
|
|
// nodeCheckFlag = NodeCheckFlagsSuperStatic
|
|
// if !isCallExpression && c.languageVersion >= core.ScriptTargetES2015 && 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`.
|
|
// forEachEnclosingBlockScopeContainer(node.Parent, func(current *ast.Node) {
|
|
// if !isSourceFile(current) || isExternalOrCommonJSModule(current) {
|
|
// c.getNodeLinks(current).flags |= NodeCheckFlagsContainsSuperPropertyInStaticInitializer
|
|
// }
|
|
// })
|
|
// }
|
|
// } else {
|
|
// nodeCheckFlag = NodeCheckFlagsSuperInstance
|
|
// }
|
|
// c.getNodeLinks(node).flags |= nodeCheckFlag
|
|
// // Due to how we emit async functions, we need to specialize the emit for an async method that contains a `super` reference.
|
|
// // This is due to the fact that we emit the body of an async function inside of a generator function. As generator
|
|
// // functions cannot reference `super`, we emit a helper inside of the method body, but outside of the generator. This helper
|
|
// // uses an arrow function, which is permitted to reference `super`.
|
|
// //
|
|
// // There are two primary ways we can access `super` from within an async method. The first is getting the value of a property
|
|
// // or indexed access on super, either as part of a right-hand-side expression or call expression. The second is when setting the value
|
|
// // of a property or indexed access, either as part of an assignment expression or destructuring assignment.
|
|
// //
|
|
// // The simplest case is reading a value, in which case we will emit something like the following:
|
|
// //
|
|
// // // ts
|
|
// // ...
|
|
// // async asyncMethod() {
|
|
// // let x = await super.asyncMethod();
|
|
// // return x;
|
|
// // }
|
|
// // ...
|
|
// //
|
|
// // // js
|
|
// // ...
|
|
// // asyncMethod() {
|
|
// // const _super = Object.create(null, {
|
|
// // asyncMethod: { get: () => super.asyncMethod },
|
|
// // });
|
|
// // return __awaiter(this, arguments, Promise, function *() {
|
|
// // let x = yield _super.asyncMethod.call(this);
|
|
// // return x;
|
|
// // });
|
|
// // }
|
|
// // ...
|
|
// //
|
|
// // The more complex case is when we wish to assign a value, especially as part of a destructuring assignment. As both cases
|
|
// // are legal in ES6, but also likely less frequent, we only emit setters if there is an assignment:
|
|
// //
|
|
// // // ts
|
|
// // ...
|
|
// // async asyncMethod(ar: Promise<any[]>) {
|
|
// // [super.a, super.b] = await ar;
|
|
// // }
|
|
// // ...
|
|
// //
|
|
// // // js
|
|
// // ...
|
|
// // asyncMethod(ar) {
|
|
// // const _super = Object.create(null, {
|
|
// // a: { get: () => super.a, set: (v) => super.a = v },
|
|
// // b: { get: () => super.b, set: (v) => super.b = v }
|
|
// // };
|
|
// // return __awaiter(this, arguments, Promise, function *() {
|
|
// // [_super.a, _super.b] = yield ar;
|
|
// // });
|
|
// // }
|
|
// // ...
|
|
// //
|
|
// // Creating an object that has getter and setters instead of just an accessor function is required for destructuring assignments
|
|
// // as a call expression cannot be used as the target of a destructuring assignment while a property access can.
|
|
// //
|
|
// // For element access expressions (`super[x]`), we emit a generic helper that forwards the element access in both situations.
|
|
// if container.Kind == ast.KindMethodDeclaration && inAsyncFunction {
|
|
// if isSuperProperty(node.Parent) && isAssignmentTarget(node.Parent) {
|
|
// c.getNodeLinks(container).flags |= NodeCheckFlagsMethodWithSuperPropertyAssignmentInAsync
|
|
// } else {
|
|
// c.getNodeLinks(container).flags |= NodeCheckFlagsMethodWithSuperPropertyAccessInAsync
|
|
// }
|
|
// }
|
|
// if needToCaptureLexicalThis {
|
|
// // call expressions are allowed only in constructors so they should always capture correct 'this'
|
|
// // super property access expressions can also appear in arrow functions -
|
|
// // in this case they should also use correct lexical this
|
|
// c.captureLexicalThis(node.Parent, container)
|
|
// }
|
|
if container.Parent.Kind == ast.KindObjectLiteralExpression {
|
|
if c.languageVersion < core.ScriptTargetES2015 {
|
|
c.error(node, diagnostics.X_super_is_only_allowed_in_members_of_object_literal_expressions_when_option_target_is_ES2015_or_higher)
|
|
return c.errorType
|
|
}
|
|
// 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 {
|
|
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.IsParameter(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.AsArrayLiteralExpression().Elements.Nodes
|
|
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.AsSpreadElement().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) {
|
|
variable := initializer.AsVariableDeclarationList().Declarations.Nodes[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)
|
|
}
|
|
}
|
|
// resolveExternalModuleName will return undefined if the moduleReferenceExpression is not a string literal
|
|
moduleSymbol := c.resolveExternalModuleName(node, specifier, false /*ignoreErrors*/)
|
|
if moduleSymbol != nil {
|
|
esModuleSymbol := c.resolveESModuleSymbol(moduleSymbol, specifier, true /*dontResolveAlias*/, false /*suppressInteropError*/)
|
|
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.AsCallExpression().Arguments.Nodes[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.AsCallExpression().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 && sig.declaration.Flags&ast.NodeFlagsDeprecated != 0 {
|
|
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)
|
|
}
|
|
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 {
|
|
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.diagnostics.Add(diag)
|
|
c.invocationErrorRecovery(apparentType, SignatureKindCall, diag)
|
|
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 /*inferenceContext*/, 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 {
|
|
// If we are *inside the body of candidate*, we need to create a clone of `candidate` with differing type parameter identities,
|
|
// so our inference results for this call doesn't pollute expression types referencing the outer type parameter!
|
|
var candidateParameterContext *ast.Node
|
|
typeParamDeclaration := core.FirstOrNil(candidate.typeParameters[0].symbol.Declarations)
|
|
if typeParamDeclaration != nil {
|
|
candidateParameterContext = typeParamDeclaration.Parent
|
|
} else if candidate.declaration != nil && ast.IsConstructorDeclaration(candidate.declaration) {
|
|
candidateParameterContext = candidate.declaration.Parent
|
|
} else {
|
|
candidateParameterContext = candidate.declaration
|
|
}
|
|
if candidateParameterContext != nil && ast.FindAncestor(s.node, func(a *ast.Node) bool { return a == candidateParameterContext }) != nil {
|
|
candidate = c.getImplementationSignature(candidate)
|
|
}
|
|
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, InferenceFlagsNone /*flags*/, nil)
|
|
// The resulting type arguments are instantiated with the inference context mapper, as the inferred types may still contain references to the inference context's
|
|
// type variables via contextual projection. These are kept generic until all inferences are locked in, so the dependencies expressed can pass constraint checks.
|
|
typeArgumentTypes = c.instantiateTypes(c.inferTypeArguments(s.node, candidate, s.args, s.argCheckMode|CheckModeSkipGenericFunctions, inferenceContext), inferenceContext.nonFixingMapper)
|
|
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*/, inferenceContext, 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.instantiateTypes(c.inferTypeArguments(s.node, candidate, s.args, s.argCheckMode, inferenceContext), inferenceContext.mapper)
|
|
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*/, inferenceContext, 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) getImplementationSignature(signature *Signature) *Signature {
|
|
key := CachedSignatureKey{sig: signature, key: SignatureKeyImplementation}
|
|
if cached := c.cachedSignatures[key]; cached != nil {
|
|
return cached
|
|
}
|
|
result := c.instantiateSignature(signature, newTypeMapper(nil, nil))
|
|
c.cachedSignatures[key] = result
|
|
return result
|
|
}
|
|
|
|
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.diagnostics.Add(diagnostic)
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
return typeArgumentTypes
|
|
}
|
|
|
|
func (c *Checker) isSignatureApplicable(node *ast.Node, args []*ast.Node, signature *Signature, relation *Relation, checkMode CheckMode, reportErrors bool, inferenceContext *InferenceContext, 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) && 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 regularArgType *Type
|
|
if checkMode&CheckModeSkipContextSensitive != 0 {
|
|
regularArgType = c.getRegularTypeOfObjectLiteral(argType)
|
|
} else {
|
|
regularArgType = argType
|
|
}
|
|
// If this was inferred under a given inference context, we may need to instantiate the expression type to finish resolving
|
|
// the type variables in the expression.
|
|
var checkArgType *Type
|
|
if inferenceContext != nil {
|
|
checkArgType = c.instantiateType(regularArgType, inferenceContext.nonFixingMapper)
|
|
} else {
|
|
checkArgType = regularArgType
|
|
}
|
|
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, 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*/, nil /*inferenceContext*/, &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.diagnostics.Add(diagnostic)
|
|
}
|
|
case s.candidateForArgumentArityError != nil:
|
|
c.diagnostics.Add(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.diagnostics.Add(c.getTypeArgumentArityError(s.node, signatures, s.typeArguments, headMessage))
|
|
} else {
|
|
c.diagnostics.Add(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)
|
|
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
|
|
}
|
|
errorNode := getErrorNodeForCallNode(node)
|
|
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:
|
|
sourceFile := ast.GetSourceFileOfNode(node)
|
|
pos := scanner.SkipTrivia(sourceFile.Text(), args[maxCount].Pos())
|
|
end := args[len(args)-1].End()
|
|
if end == pos {
|
|
end++
|
|
}
|
|
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.IsParameter(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)
|
|
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(ast.GetSourceFileOfNode(node), node.TypeArgumentList().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(ast.GetSourceFileOfNode(node), node.TypeArgumentList().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(ast.GetSourceFileOfNode(node), node.TypeArgumentList().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:
|
|
c.checkExpression(node.AsJsxOpeningElement().Attributes)
|
|
case ast.KindJsxSelfClosingElement:
|
|
c.checkExpression(node.AsJsxSelfClosingElement().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.diagnostics.Add(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.valueSymbolLinks.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)
|
|
return c.getTypeOfSymbol(c.getSymbolOfDeclaration(node))
|
|
}
|
|
|
|
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 && !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)
|
|
}
|
|
if node.Type() != nil || core.Some(node.Parameters(), func(p *ast.Node) bool { return p.Type() != nil }) {
|
|
c.error(node.FunctionLikeData().FullSignature, diagnostics.A_JSDoc_type_tag_may_not_occur_with_a_param_or_returns_tag)
|
|
}
|
|
}
|
|
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 := 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.AsParameterDeclaration().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.AsBindingPattern().Elements.Nodes {
|
|
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) {
|
|
c.checkCollisionWithRequireExportsInGeneratedCode(node, name)
|
|
switch {
|
|
case name == nil:
|
|
return
|
|
case ast.IsClassLike(node):
|
|
c.checkTypeNameIsReserved(name, diagnostics.Class_name_cannot_be_0)
|
|
case 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) 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.IsParameter(root) && ast.NodeIsMissing(root.Parent.Body()) {
|
|
// just an overload - no codegen impact
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
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.diagnostics.Add(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 {
|
|
c.checkSourceElement(node.Type())
|
|
return c.checkSatisfiesExpressionWorker(node.Expression(), node.Type(), CheckModeNormal)
|
|
}
|
|
|
|
func (c *Checker) checkSatisfiesExpressionWorker(expression *ast.Node, target *ast.Node, checkMode CheckMode) *Type {
|
|
exprType := c.checkExpressionEx(expression, checkMode)
|
|
targetType := c.getTypeFromTypeNode(target)
|
|
if c.isErrorType(targetType) {
|
|
return targetType
|
|
}
|
|
errorNode := ast.FindAncestor(target.Parent, func(n *ast.Node) bool { return ast.IsSatisfiesExpression(n) })
|
|
c.checkTypeAssignableToAndOptionallyElaborate(exprType, targetType, errorNode, 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.AsCallExpression().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 := getFunctionFlags(fn)
|
|
if functionFlags&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 & FunctionFlagsAsync) != 0
|
|
// 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 {
|
|
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) && 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.IsParameter(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) {
|
|
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 && 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 {
|
|
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))
|
|
}
|
|
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
|
|
initialType := propType
|
|
if c.strictNullChecks && prop != nil {
|
|
declaration := prop.ValueDeclaration
|
|
if declaration != nil && 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
|
|
initialType = c.getOptionalType(propType, false /*isProperty*/)
|
|
}
|
|
}
|
|
}
|
|
flowType := c.getFlowTypeOfReferenceEx(node, propType, initialType, 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, scanner.DeclarationNameToString(typeClass.Name()))
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *Checker) reportNonexistentProperty(propNode *ast.Node, containingType *Type) {
|
|
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)
|
|
diagnostic = NewDiagnosticChainForNode(diagnostic, propNode, diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, 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.diagnostics.Add(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(), 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.UseDefineForClassFields.IsTrue() || !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.AsPropertyDeclaration().PostfixToken)
|
|
}
|
|
|
|
func (c *Checker) isPropertyDeclaredInAncestorClass(prop *ast.Symbol) bool {
|
|
if prop.Parent.Flags&ast.SymbolFlagsClass == 0 {
|
|
return false
|
|
}
|
|
classType := c.getDeclaredTypeOfSymbol(prop.Parent)
|
|
for {
|
|
baseTypes := c.getBaseTypes(classType)
|
|
if len(baseTypes) == 0 {
|
|
return false
|
|
}
|
|
classType = baseTypes[0]
|
|
superProperty := c.getPropertyOfType(classType, prop.Name)
|
|
if superProperty != nil && superProperty.ValueDeclaration != nil {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 c.languageVersion < core.ScriptTargetES2015 {
|
|
if c.symbolHasNonMethodDeclaration(prop) {
|
|
if errorNode != nil {
|
|
c.error(errorNode, diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword)
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
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)) {
|
|
declaringClassDeclaration := ast.GetClassLikeDeclarationOfSymbol(c.getParentOfSymbol(prop))
|
|
if declaringClassDeclaration != nil && 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), declaringClassDeclaration.Name().Text())
|
|
}
|
|
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.AsParameterDeclaration().Type != nil {
|
|
thisType = c.getTypeFromTypeNode(thisParameter.AsParameterDeclaration().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)
|
|
}
|
|
}
|
|
}
|
|
if c.noImplicitThis {
|
|
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) {
|
|
return c.getWidenedType(c.checkExpressionCached(target.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 {
|
|
if container == nil {
|
|
container = c.getThisContainer(node, false /*includeArrowFunctions*/, false /*includeClassComputedPropertyName*/)
|
|
}
|
|
if ast.IsFunctionLike(container) && (!c.isInParameterInitializerBeforeContainingFunction(node) || ast.GetThisParameter(container) != nil) {
|
|
thisType := c.getThisTypeOfDeclaration(container)
|
|
if thisType == nil && ast.IsInJSFile(container) {
|
|
if sig := c.getSignatureOfFullSignatureType(container); sig != nil {
|
|
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.IsParameter(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) getThisTypeOfDeclaration(declaration *ast.Node) *Type {
|
|
return c.getThisTypeOfSignature(c.getSignatureFromDeclaration(declaration))
|
|
}
|
|
|
|
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.compilerOptions.ErasableSyntaxOnly.IsTrue() {
|
|
c.diagnostics.Add(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_assertions_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) {
|
|
exprType := c.getRegularTypeOfObjectLiteral(c.getBaseTypeOfLiteralType(c.assertionLinks.Get(node).exprType))
|
|
targetType := c.getTypeFromTypeNode(node.Type())
|
|
if !c.isErrorType(targetType) {
|
|
widenedType := c.getWidenedType(exprType)
|
|
if !c.isTypeComparableTo(targetType, widenedType) {
|
|
errNode := node
|
|
if node.Flags&ast.NodeFlagsReparsed != 0 {
|
|
errNode = node.Type()
|
|
}
|
|
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) {
|
|
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.AsObjectLiteralExpression().Properties
|
|
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.AsObjectLiteralExpression().Properties.Nodes
|
|
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
|
|
}
|
|
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.AsArrayLiteralExpression().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.Nodes {
|
|
t := possiblyOutOfBoundsType
|
|
if elements.Nodes[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.AsArrayLiteralExpression().Elements
|
|
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) {
|
|
// getters can be a subtype of setters, so to check for assignability we use the setter's type instead
|
|
if 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)
|
|
c.checkNullishCoalesceOperandRight(right)
|
|
}
|
|
|
|
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) checkNullishCoalesceOperandRight(right *ast.Node) {
|
|
binaryExpression := right.Parent
|
|
if binaryExpression.Parent != nil && ast.IsBinaryExpression(binaryExpression.Parent) && binaryExpression.Parent.AsBinaryExpression().OperatorToken.Kind == ast.KindQuestionQuestionToken {
|
|
rightTarget := ast.SkipOuterExpressions(right, ast.OEKAll)
|
|
nullishSemantics := c.getSyntacticNullishnessSemantics(rightTarget)
|
|
switch nullishSemantics {
|
|
case PredicateSemanticsAlways:
|
|
c.error(rightTarget, diagnostics.This_expression_is_always_nullish)
|
|
case PredicateSemanticsNever:
|
|
c.error(rightTarget, diagnostics.This_expression_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.KindEqualsToken,
|
|
ast.KindQuestionQuestionToken,
|
|
ast.KindQuestionQuestionEqualsToken,
|
|
ast.KindBarBarToken,
|
|
ast.KindBarBarEqualsToken,
|
|
ast.KindAmpersandAmpersandToken,
|
|
ast.KindAmpersandAmpersandEqualsToken:
|
|
return PredicateSemanticsSometimes
|
|
case ast.KindCommaToken:
|
|
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) {
|
|
// 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 {
|
|
c.reportNonexistentProperty(left, rightType)
|
|
}
|
|
} 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 {
|
|
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.AsObjectLiteralExpression().Properties.Nodes {
|
|
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 patternWithComputedProperties {
|
|
result.objectFlags |= ObjectFlagsObjectLiteralPatternWithComputedProperties
|
|
}
|
|
if inDestructuringPattern {
|
|
c.patternForType[result] = node
|
|
}
|
|
return result
|
|
}
|
|
// expando object literals have empty properties but filled exports -- skip straight to type creation
|
|
if len(node.AsObjectLiteralExpression().Properties.Nodes) == 0 && node.Symbol() != nil && len(node.Symbol().Exports) > 0 {
|
|
propertiesTable = node.Symbol().Exports
|
|
return createObjectLiteralType()
|
|
}
|
|
for _, memberDecl := range node.AsObjectLiteralExpression().Properties.Nodes {
|
|
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 {
|
|
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 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 {
|
|
// 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.
|
|
if ast.IsBindingElement(declaration) && declaration.Initializer() == nil && !hasDotDotDotToken(declaration) && len(declaration.Parent.AsBindingPattern().Elements.Nodes) >= 2 {
|
|
parent := declaration.Parent.Parent
|
|
rootDeclaration := ast.GetRootDeclaration(parent)
|
|
if ast.IsVariableDeclaration(rootDeclaration) && c.getCombinedNodeFlagsCached(rootDeclaration)&ast.NodeFlagsConstant != 0 || ast.IsParameter(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)
|
|
}
|
|
links.flags &^= NodeCheckFlagsInCheckIdentifier
|
|
if parentTypeConstraint != nil && parentTypeConstraint.flags&TypeFlagsUnion != 0 && !(ast.IsParameter(rootDeclaration) && c.isSomeSymbolAssigned(rootDeclaration)) {
|
|
pattern := declaration.Parent
|
|
narrowedType := c.getFlowTypeOfReferenceEx(pattern, parentTypeConstraint, parentTypeConstraint, nil /*flowContainer*/, getFlowNodeOfNode(location))
|
|
if narrowedType.flags&TypeFlagsNever != 0 {
|
|
return c.neverType
|
|
}
|
|
// 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].
|
|
return c.getBindingElementTypeFromParentType(declaration, narrowedType, true /*noTupleBoundsCheck*/)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// 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.
|
|
if ast.IsParameter(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))
|
|
return c.getIndexedAccessType(narrowedType, c.getNumberLiteralType(jsnum.Number(index)))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return t
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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.AsIdentifier().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) getCannotFindNameDiagnosticForName(node *ast.Node) *diagnostics.Message {
|
|
switch node.AsIdentifier().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.Types != nil,
|
|
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,
|
|
diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_jQuery_Try_npm_i_save_dev_types_Slashjquery)
|
|
case "describe", "suite", "it", "test":
|
|
return core.IfElse(c.compilerOptions.Types != nil,
|
|
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,
|
|
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)
|
|
case "process", "require", "Buffer", "module":
|
|
return core.IfElse(c.compilerOptions.Types != nil,
|
|
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,
|
|
diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_node_Try_npm_i_save_dev_types_Slashnode)
|
|
case "Bun":
|
|
return core.IfElse(c.compilerOptions.Types != nil,
|
|
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,
|
|
diagnostics.Cannot_find_name_0_Do_you_need_to_install_type_definitions_for_Bun_Try_npm_i_save_dev_types_Slashbun)
|
|
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()
|
|
if sourceFile != nil {
|
|
c.CheckSourceFile(ctx, sourceFile)
|
|
if c.wasCanceled {
|
|
return nil
|
|
}
|
|
return collection.GetDiagnosticsForFile(sourceFile.FileName())
|
|
}
|
|
for _, file := range c.files {
|
|
c.CheckSourceFile(ctx, file)
|
|
if c.wasCanceled {
|
|
return nil
|
|
}
|
|
}
|
|
return collection.GetDiagnostics()
|
|
}
|
|
|
|
func (c *Checker) GetDiagnosticsWithoutCheck(sourceFile *ast.SourceFile) []*ast.Diagnostic {
|
|
c.checkNotCanceled()
|
|
return c.diagnostics.GetDiagnosticsForFile(sourceFile.FileName())
|
|
}
|
|
|
|
func (c *Checker) GetGlobalDiagnostics() []*ast.Diagnostic {
|
|
c.checkNotCanceled()
|
|
return c.diagnostics.GetGlobalDiagnostics()
|
|
}
|
|
|
|
func (c *Checker) error(location *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic {
|
|
diagnostic := NewDiagnosticForNode(location, message, args...)
|
|
c.diagnostics.Add(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.diagnostics.Add(diagnostic)
|
|
} else {
|
|
suggestion := *diagnostic
|
|
suggestion.SetCategory(diagnostics.CategorySuggestion)
|
|
c.suggestionDiagnostics.Add(&suggestion)
|
|
}
|
|
}
|
|
|
|
func (c *Checker) IsDeprecatedDeclaration(declaration *ast.Node) bool {
|
|
return c.getCombinedNodeFlagsCached(declaration)&ast.NodeFlagsDeprecated != 0
|
|
}
|
|
|
|
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 := getJSDocDeprecatedTag(declaration)
|
|
if deprecatedTag != nil {
|
|
diagnostic.AddRelatedInfo(NewDiagnosticForNode(deprecatedTag, diagnostics.The_declaration_was_marked_as_deprecated_here))
|
|
break
|
|
}
|
|
}
|
|
c.suggestionDiagnostics.Add(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.symbolPool.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
|
|
}
|
|
target.Flags |= source.Flags
|
|
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))
|
|
symbolName := c.symbolToString(source)
|
|
// !!!
|
|
// Collect top-level duplicate identifier errors into one mapping, so we can then merge their diagnostics if there are a bunch
|
|
// if sourceSymbolFile != nil && targetSymbolFile != nil && c.amalgamatedDuplicates && !isEitherEnum && sourceSymbolFile != targetSymbolFile {
|
|
// var firstFile SourceFile
|
|
// if comparePaths(sourceSymbolFile.path, targetSymbolFile.path) == ComparisonLessThan {
|
|
// firstFile = sourceSymbolFile
|
|
// } else {
|
|
// firstFile = targetSymbolFile
|
|
// }
|
|
// var secondFile SourceFile
|
|
// if firstFile == sourceSymbolFile {
|
|
// secondFile = targetSymbolFile
|
|
// } else {
|
|
// secondFile = sourceSymbolFile
|
|
// }
|
|
// filesDuplicates := getOrUpdate(c.amalgamatedDuplicates, __TEMPLATE__(firstFile.path, "|", secondFile.path), func() DuplicateInfoForFiles {
|
|
// return (map[any]any{ /* TODO(TS-TO-GO): was object literal */
|
|
// "firstFile": firstFile,
|
|
// "secondFile": secondFile,
|
|
// "conflictingSymbols": NewMap(),
|
|
// })
|
|
// })
|
|
// conflictingSymbolInfo := getOrUpdate(filesDuplicates.conflictingSymbols, symbolName, func() DuplicateInfoForSymbol {
|
|
// return (map[any]any{ /* TODO(TS-TO-GO): was object literal */
|
|
// "isBlockScoped": isEitherBlockScoped,
|
|
// "firstFileLocations": []never{},
|
|
// "secondFileLocations": []never{},
|
|
// })
|
|
// })
|
|
// if !isSourcePlainJS {
|
|
// addDuplicateLocations(conflictingSymbolInfo.firstFileLocations, source)
|
|
// }
|
|
// if !isTargetPlainJS {
|
|
// addDuplicateLocations(conflictingSymbolInfo.secondFileLocations, target)
|
|
// }
|
|
// } else {
|
|
c.addDuplicateDeclarationErrorsForSymbols(source, message, symbolName, target)
|
|
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.diagnostics.Add(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
|
|
}
|
|
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, dontResolveAlias bool) *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, false /*dontResolveAlias*/)
|
|
if resolved != nil && core.ModuleKindNode20 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext {
|
|
moduleExports := c.getExportOfModule(resolved, ast.InternalSymbolNameModuleExports, node, dontResolveAlias)
|
|
if moduleExports != nil {
|
|
return moduleExports
|
|
}
|
|
}
|
|
c.markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, false /*overwriteEmpty*/, nil, "")
|
|
return resolved
|
|
}
|
|
resolved := c.getSymbolOfPartOfRightHandSideOfImportEquals(node.AsImportEqualsDeclaration().ModuleReference, dontResolveAlias)
|
|
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, dontResolveAlias bool) *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*/, 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*/, dontResolveAlias, nil /*location*/)
|
|
}
|
|
|
|
func (c *Checker) checkAndReportErrorForResolvingImportAliasToTypeOnlySymbol(node *ast.Node, resolved *ast.Symbol) {
|
|
decl := node.AsImportEqualsDeclaration()
|
|
if c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "") && !decl.IsTypeOnly {
|
|
typeOnlyDeclaration := c.getTypeOnlyAliasDeclaration(c.getSymbolOfDeclaration(node))
|
|
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 = getNameFromImportDeclaration(typeOnlyDeclaration).Text()
|
|
}
|
|
c.error(decl.ModuleReference, message).AddRelatedInfo(createDiagnosticForNode(typeOnlyDeclaration, relatedMessage, name))
|
|
}
|
|
}
|
|
|
|
func (c *Checker) getTargetOfImportClause(node *ast.Node, dontResolveAlias bool) *ast.Symbol {
|
|
moduleSymbol := c.resolveExternalModuleName(node, getModuleSpecifierFromNode(node.Parent), false /*ignoreErrors*/)
|
|
if moduleSymbol != nil {
|
|
return c.getTargetOfModuleDefault(moduleSymbol, node, 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. If `esModuleInterop` is 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
|
|
//
|
|
// But without `esModuleInterop`, it will be broken:
|
|
//
|
|
// const dep_1 = require("./dep.mjs"); // the `module.exports` export value (could be primitive)
|
|
// dep_1.default; // `default` property access on the `module.exports` export value
|
|
//
|
|
// We could try to resolve the 'default' property in the latter case, but it's a mistake to run in this
|
|
// environment without `esModuleInterop`, so just error.
|
|
if !c.compilerOptions.GetESModuleInterop() {
|
|
c.error(node.Name(), diagnostics.Module_0_can_only_be_default_imported_using_the_1_flag, c.symbolToString(moduleSymbol), "esModuleInterop")
|
|
return nil
|
|
}
|
|
c.markSymbolOfAliasDeclarationIfTypeOnly(node, exportModuleDotExportsSymbol /*finalTarget*/, nil /*overwriteEmpty*/, false, 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 hasExportAssignmentSymbol(moduleSymbol) && !c.allowSyntheticDefaultImports {
|
|
compilerOptionName := core.IfElse(c.moduleKind >= core.ModuleKindES2015, "allowSyntheticDefaultImports", "esModuleInterop")
|
|
exportEqualsSymbol := moduleSymbol.Exports[ast.InternalSymbolNameExportEquals]
|
|
exportAssignment := exportEqualsSymbol.ValueDeclaration
|
|
err := c.error(node.Name(), diagnostics.Module_0_can_only_be_default_imported_using_the_1_flag, c.symbolToString(moduleSymbol), compilerOptionName)
|
|
if exportAssignment != nil {
|
|
err.AddRelatedInfo(createDiagnosticForNode(exportAssignment, diagnostics.This_module_is_declared_with_export_and_can_only_be_used_with_a_default_import_when_using_the_0_flag, compilerOptionName))
|
|
}
|
|
} else 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, moduleSymbol, resolved /*overwriteEmpty*/, false, nil, "")
|
|
return resolved
|
|
}
|
|
c.markSymbolOfAliasDeclarationIfTypeOnly(node, exportDefaultSymbol /*finalTarget*/, nil /*overwriteEmpty*/, false, 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.AsExportDeclaration().ModuleSpecifier != nil) {
|
|
return false
|
|
}
|
|
resolvedExternalModuleName := c.resolveExternalModuleName(decl, decl.AsExportDeclaration().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, exportSymbol, resolved, false /*overwriteEmpty*/, nil, "")
|
|
return resolved
|
|
}
|
|
|
|
func (c *Checker) getTargetOfNamespaceImport(node *ast.Node, dontResolveAlias bool) *ast.Symbol {
|
|
moduleSpecifier := c.getModuleSpecifierForImportOrExport(node)
|
|
immediate := c.resolveExternalModuleName(node, moduleSpecifier, false /*ignoreErrors*/)
|
|
resolved := c.resolveESModuleSymbol(immediate, moduleSpecifier, dontResolveAlias /*suppressInteropError*/, false)
|
|
c.markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, false /*overwriteEmpty*/, nil, "")
|
|
return resolved
|
|
}
|
|
|
|
func (c *Checker) getTargetOfNamespaceExport(node *ast.Node, dontResolveAlias bool) *ast.Symbol {
|
|
moduleSpecifier := c.getModuleSpecifierForImportOrExport(node)
|
|
if moduleSpecifier != nil {
|
|
immediate := c.resolveExternalModuleName(node, moduleSpecifier, false /*ignoreErrors*/)
|
|
resolved := c.resolveESModuleSymbol(immediate, moduleSpecifier, dontResolveAlias /*suppressInteropError*/, false)
|
|
c.markSymbolOfAliasDeclarationIfTypeOnly(node, immediate, resolved, false /*overwriteEmpty*/, nil, "")
|
|
return resolved
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Checker) getTargetOfImportSpecifier(node *ast.Node, dontResolveAlias bool) *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, dontResolveAlias)
|
|
}
|
|
}
|
|
}
|
|
root := node.Parent.Parent.Parent // ImportDeclaration
|
|
if ast.IsBindingElement(node) {
|
|
root = ast.GetRootDeclaration(node)
|
|
}
|
|
resolved := c.getExternalModuleMember(root, node, dontResolveAlias)
|
|
c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, 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()
|
|
suppressInteropError := node.Kind == ast.KindVariableDeclaration || nameText == ast.InternalSymbolNameDefault && c.allowSyntheticDefaultImports
|
|
targetSymbol := c.resolveESModuleSymbol(moduleSymbol, moduleSpecifier /*dontResolveAlias*/, false, suppressInteropError)
|
|
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)
|
|
symbolFromModule := c.getExportOfModule(targetSymbol, 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.AsVariableDeclaration().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 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, exportSymbol, resolved /*overwriteEmpty*/, false, exportStarDeclaration, nameText)
|
|
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
|
|
}
|
|
}
|
|
if !c.allowSyntheticDefaultImports {
|
|
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 !ast.IsExternalModule(file.AsSourceFile()) && 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) {
|
|
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(node, name, declarationName, moduleSymbol, moduleName)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Checker) reportNonExportedMember(node *ast.Node, 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(node, 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(node *ast.Node, name *ast.Node, declarationName string, moduleName string) {
|
|
if c.moduleKind >= core.ModuleKindES2015 {
|
|
message := core.IfElse(c.compilerOptions.GetESModuleInterop(),
|
|
diagnostics.X_0_can_only_be_imported_by_using_a_default_import,
|
|
diagnostics.X_0_can_only_be_imported_by_turning_on_the_esModuleInterop_flag_and_using_a_default_import)
|
|
c.error(name, message, declarationName)
|
|
} else {
|
|
message := core.IfElse(c.compilerOptions.GetESModuleInterop(),
|
|
diagnostics.X_0_can_only_be_imported_by_using_import_1_require_2_or_a_default_import,
|
|
diagnostics.X_0_can_only_be_imported_by_using_import_1_require_2_or_by_turning_on_the_esModuleInterop_flag_and_using_a_default_import)
|
|
c.error(name, message, 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.AsExportDeclaration().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 /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "")
|
|
return resolved
|
|
}
|
|
|
|
func (c *Checker) getTargetOfExportAssignment(node *ast.Node, dontResolveAlias bool) *ast.Symbol {
|
|
resolved := c.getTargetOfAliasLikeExpression(node.AsExportAssignment().Expression, dontResolveAlias)
|
|
c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "")
|
|
return resolved
|
|
}
|
|
|
|
func (c *Checker) getTargetOfBinaryExpression(node *ast.Node, dontResolveAlias bool) *ast.Symbol {
|
|
resolved := c.getTargetOfAliasLikeExpression(node.AsBinaryExpression().Right, dontResolveAlias)
|
|
c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "")
|
|
return resolved
|
|
}
|
|
|
|
func (c *Checker) getTargetOfAliasLikeExpression(expression *ast.Node, dontResolveAlias bool) *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*/, dontResolveAlias, nil /*location*/)
|
|
if aliasLike != nil {
|
|
return aliasLike
|
|
}
|
|
c.checkExpressionCached(expression)
|
|
return c.getResolvedSymbolOrNil(expression)
|
|
}
|
|
|
|
func (c *Checker) getTargetOfNamespaceExportDeclaration(node *ast.Node, dontResolveAlias bool) *ast.Symbol {
|
|
if ast.CanHaveSymbol(node.Parent) {
|
|
resolved := c.resolveExternalModuleSymbol(node.Parent.Symbol(), dontResolveAlias)
|
|
c.markSymbolOfAliasDeclarationIfTypeOnly(node, nil /*immediateTarget*/, resolved, false /*overwriteEmpty*/, nil, "")
|
|
return resolved
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Checker) getTargetOfAccessExpression(node *ast.Node, dontRecursivelyResolve bool) *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, dontRecursivelyResolve)
|
|
}
|
|
}
|
|
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.AsExternalModuleReference().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.AsImportDeclaration().ModuleSpecifier
|
|
case ast.KindExportDeclaration:
|
|
return node.AsExportDeclaration().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, immediateTarget *ast.Symbol, finalTarget *ast.Symbol, overwriteEmpty bool, exportStarDeclaration *ast.Node, exportStarName string) 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)
|
|
if ast.IsTypeOnlyImportOrExportDeclaration(aliasDeclaration) {
|
|
links := c.aliasSymbolLinks.Get(sourceSymbol)
|
|
links.typeOnlyDeclaration = aliasDeclaration
|
|
return true
|
|
}
|
|
if exportStarDeclaration != nil {
|
|
links := c.aliasSymbolLinks.Get(sourceSymbol)
|
|
links.typeOnlyDeclaration = exportStarDeclaration
|
|
if sourceSymbol.Name != exportStarName {
|
|
links.typeOnlyExportStarName = exportStarName
|
|
}
|
|
return true
|
|
}
|
|
links := c.aliasSymbolLinks.Get(sourceSymbol)
|
|
return c.markSymbolOfAliasDeclarationIfTypeOnlyWorker(links, immediateTarget, overwriteEmpty) || c.markSymbolOfAliasDeclarationIfTypeOnlyWorker(links, finalTarget, overwriteEmpty)
|
|
}
|
|
|
|
func (c *Checker) markSymbolOfAliasDeclarationIfTypeOnlyWorker(aliasDeclarationLinks *AliasSymbolLinks, target *ast.Symbol, overwriteEmpty bool) bool {
|
|
if target != nil && (!aliasDeclarationLinks.typeOnlyDeclarationResolved || overwriteEmpty && aliasDeclarationLinks.typeOnlyDeclaration == nil) {
|
|
exportSymbol := core.OrElse(target.Exports[ast.InternalSymbolNameExportEquals], target)
|
|
aliasDeclarationLinks.typeOnlyDeclarationResolved = true
|
|
if typeOnly := core.Find(exportSymbol.Declarations, ast.IsTypeOnlyImportOrExportDeclaration); typeOnly != nil {
|
|
aliasDeclarationLinks.typeOnlyDeclaration = typeOnly
|
|
} else {
|
|
aliasDeclarationLinks.typeOnlyDeclaration = c.aliasSymbolLinks.Get(exportSymbol).typeOnlyDeclaration
|
|
}
|
|
}
|
|
return aliasDeclarationLinks.typeOnlyDeclaration != nil
|
|
}
|
|
|
|
func (c *Checker) resolveExternalModuleName(location *ast.Node, moduleReferenceExpression *ast.Node, ignoreErrors bool) *ast.Symbol {
|
|
errorMessage := diagnostics.Cannot_find_module_0_or_its_corresponding_type_declarations
|
|
return c.resolveExternalModuleNameWorker(location, moduleReferenceExpression, core.IfElse(ignoreErrors, nil, errorMessage), ignoreErrors, false /*isForAugmentation*/)
|
|
}
|
|
|
|
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) 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.IsVariableDeclaration(location) && location.AsVariableDeclaration().Initializer != nil && ast.IsRequireCall(location.AsVariableDeclaration().Initializer, true /*requireStringLiteralLikeArgument*/) {
|
|
contextSpecifier = location.AsVariableDeclaration().Initializer.AsCallExpression().Arguments.Nodes[0]
|
|
} else {
|
|
ancestor := ast.FindAncestor(location, ast.IsImportCall)
|
|
if ancestor != nil {
|
|
contextSpecifier = ancestor.AsCallExpression().Arguments.Nodes[0]
|
|
}
|
|
|
|
if ancestor == nil {
|
|
ancestor = ast.FindAncestor(location, ast.IsImportDeclarationOrJSImportDeclaration)
|
|
if ancestor != nil {
|
|
contextSpecifier = ancestor.AsImportDeclaration().ModuleSpecifier
|
|
}
|
|
}
|
|
if ancestor == nil {
|
|
ancestor = ast.FindAncestor(location, ast.IsExportDeclaration)
|
|
if ancestor != nil {
|
|
contextSpecifier = ancestor.AsExportDeclaration().ModuleSpecifier
|
|
}
|
|
}
|
|
if ancestor == nil {
|
|
ancestor = ast.FindAncestor(location, ast.IsImportEqualsDeclaration)
|
|
if ancestor != nil {
|
|
if moduleRefrence := ancestor.AsImportEqualsDeclaration().ModuleReference; moduleRefrence.Kind == ast.KindExternalModuleReference {
|
|
contextSpecifier = moduleRefrence.AsExternalModuleReference().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 == "" {
|
|
panic("should be able to extract TS extension from string that passes IsDeclarationFileName")
|
|
}
|
|
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.AsImportDeclaration().ImportClause != nil && overrideHost.AsImportDeclaration().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.diagnostics.Add(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, sourceFile.FileName())
|
|
}
|
|
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.diagnostics.Add(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 {
|
|
if resolvedModule.AlternateResult != "" {
|
|
if strings.Contains(resolvedModule.AlternateResult, "/node_modules/@types/") {
|
|
packageName = "@types/" + module.MangleScopedPackageName(packageName)
|
|
}
|
|
return NewDiagnosticForNode(errorNode, diagnostics.There_are_types_at_0_but_this_result_could_not_be_resolved_when_respecting_package_json_exports_The_1_library_may_need_to_update_its_package_json_or_typings, resolvedModule.AlternateResult, packageName)
|
|
}
|
|
if c.typesPackageExists(packageName) {
|
|
return NewDiagnosticForNode(errorNode, diagnostics.If_the_0_package_actually_exposes_this_module_consider_sending_a_pull_request_to_amend_https_Colon_Slash_Slashgithub_com_SlashDefinitelyTyped_SlashDefinitelyTyped_Slashtree_Slashmaster_Slashtypes_Slash_1, packageName, module.MangleScopedPackageName(packageName))
|
|
}
|
|
if c.packageBundlesTypes(packageName) {
|
|
return NewDiagnosticForNode(errorNode, diagnostics.If_the_0_package_actually_exposes_this_module_try_adding_a_new_declaration_d_ts_file_containing_declare_module_1, packageName, moduleReference)
|
|
}
|
|
return NewDiagnosticForNode(errorNode, diagnostics.Try_npm_i_save_dev_types_Slash_1_if_it_exists_or_add_a_new_declaration_d_ts_file_containing_declare_module_0, moduleReference, module.MangleScopedPackageName(packageName))
|
|
}
|
|
|
|
func (c *Checker) createModeMismatchDetails(sourceFile *ast.SourceFile, errorNode *ast.Node) *ast.Diagnostic {
|
|
ext := tspath.TryGetExtensionFromPath(sourceFile.FileName())
|
|
targetExt := core.IfElse(ext == tspath.ExtensionTs, tspath.ExtensionMts, core.IfElse(ext == tspath.ExtensionJs, tspath.ExtensionMjs, ""))
|
|
meta := c.program.GetSourceFileMetaData(sourceFile.Path())
|
|
packageJsonType := meta.PackageJsonType
|
|
packageJsonDirectory := meta.PackageJsonDirectory
|
|
var result *ast.Diagnostic
|
|
if packageJsonDirectory != "" && packageJsonType == "" {
|
|
if targetExt != "" {
|
|
result = NewDiagnosticForNode(errorNode, diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_add_the_field_type_Colon_module_to_1, targetExt, tspath.CombinePaths(packageJsonDirectory, "package.json"))
|
|
} else {
|
|
result = NewDiagnosticForNode(errorNode, diagnostics.To_convert_this_file_to_an_ECMAScript_module_add_the_field_type_Colon_module_to_0, tspath.CombinePaths(packageJsonDirectory, "package.json"))
|
|
}
|
|
} else if targetExt != "" {
|
|
result = NewDiagnosticForNode(errorNode, diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_create_a_local_package_json_file_with_type_Colon_module, targetExt)
|
|
} else {
|
|
result = NewDiagnosticForNode(errorNode, diagnostics.To_convert_this_file_to_an_ECMAScript_module_create_a_local_package_json_file_with_type_Colon_module)
|
|
}
|
|
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
|
|
}
|
|
|
|
// An external module with an 'export =' declaration may be referenced as an ES6 module provided the 'export ='
|
|
// references a symbol that is at least declared as a module or a variable. The target of the 'export =' may
|
|
// combine other declarations with the module or variable (e.g. a class/module, function/module, interface/variable).
|
|
func (c *Checker) resolveESModuleSymbol(moduleSymbol *ast.Symbol, referencingLocation *ast.Node, dontResolveAlias bool, suppressInteropError bool) *ast.Symbol {
|
|
symbol := c.resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias)
|
|
if !dontResolveAlias && symbol != nil {
|
|
if !suppressInteropError && symbol.Flags&(ast.SymbolFlagsModule|ast.SymbolFlagsVariable) == 0 && ast.GetDeclarationOfKind(symbol, ast.KindSourceFile) == nil {
|
|
compilerOptionName := core.IfElse(c.moduleKind >= core.ModuleKindES2015, "allowSyntheticDefaultImports", "esModuleInterop")
|
|
c.error(referencingLocation, diagnostics.This_module_can_only_be_referenced_with_ECMAScript_imports_Slashexports_by_turning_on_the_0_flag_and_referencing_its_default_export, compilerOptionName)
|
|
return symbol
|
|
}
|
|
referenceParent := referencingLocation.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.AsCallExpression().Arguments.Nodes[0]
|
|
} else {
|
|
reference = referenceParent.AsImportDeclaration().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, dontResolveAlias)
|
|
}
|
|
if exportModuleDotExportsSymbol != nil {
|
|
if !suppressInteropError && symbol.Flags&(ast.SymbolFlagsModule|ast.SymbolFlagsVariable) == 0 {
|
|
c.error(referencingLocation, diagnostics.This_module_can_only_be_referenced_with_ECMAScript_imports_Slashexports_by_turning_on_the_0_flag_and_referencing_its_default_export, "esModuleInterop")
|
|
}
|
|
if c.compilerOptions.GetESModuleInterop() && c.hasSignatures(typ) {
|
|
return c.cloneTypeAsModuleType(exportModuleDotExportsSymbol, typ, referenceParent)
|
|
}
|
|
return exportModuleDotExportsSymbol
|
|
}
|
|
|
|
isEsmCjsRef := targetFile != nil && isESMFormatImportImportingCommonjsFormatFile(usageMode, c.program.GetImpliedNodeFormatForEmit(targetFile.AsSourceFile()))
|
|
if c.compilerOptions.GetESModuleInterop() || isEsmCjsRef {
|
|
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 c.allowSyntheticDefaultImports && 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, dontRecursivelyResolve bool) *ast.Symbol {
|
|
if node == nil {
|
|
return nil
|
|
}
|
|
switch node.Kind {
|
|
case ast.KindImportEqualsDeclaration, ast.KindVariableDeclaration:
|
|
return c.getTargetOfImportEqualsDeclaration(node, dontRecursivelyResolve)
|
|
case ast.KindImportClause:
|
|
return c.getTargetOfImportClause(node, dontRecursivelyResolve)
|
|
case ast.KindNamespaceImport:
|
|
return c.getTargetOfNamespaceImport(node, dontRecursivelyResolve)
|
|
case ast.KindNamespaceExport:
|
|
return c.getTargetOfNamespaceExport(node, dontRecursivelyResolve)
|
|
case ast.KindImportSpecifier, ast.KindBindingElement:
|
|
return c.getTargetOfImportSpecifier(node, dontRecursivelyResolve)
|
|
case ast.KindExportSpecifier:
|
|
return c.getTargetOfExportSpecifier(node, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, dontRecursivelyResolve)
|
|
case ast.KindExportAssignment, ast.KindJSExportAssignment:
|
|
return c.getTargetOfExportAssignment(node, dontRecursivelyResolve)
|
|
case ast.KindBinaryExpression:
|
|
return c.getTargetOfBinaryExpression(node, dontRecursivelyResolve)
|
|
case ast.KindNamespaceExportDeclaration:
|
|
return c.getTargetOfNamespaceExportDeclaration(node, dontRecursivelyResolve)
|
|
case ast.KindShorthandPropertyAssignment:
|
|
return c.resolveEntityName(node.AsShorthandPropertyAssignment().Name(), ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace, true /*ignoreErrors*/, dontRecursivelyResolve, nil /*location*/)
|
|
case ast.KindPropertyAssignment:
|
|
return c.getTargetOfAliasLikeExpression(node.Initializer(), dontRecursivelyResolve)
|
|
case ast.KindElementAccessExpression, ast.KindPropertyAccessExpression:
|
|
return c.getTargetOfAccessExpression(node, dontRecursivelyResolve)
|
|
}
|
|
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
|
|
}
|
|
symbol = c.getMergedSymbol(c.resolveName(resolveLocation, name.AsIdentifier().Text, meaning, message, true /*isUse*/, false /*excludeGlobals*/))
|
|
case ast.KindQualifiedName:
|
|
qualified := name.AsQualifiedName()
|
|
symbol = c.resolveQualifiedName(name, qualified.Left, qualified.Right, meaning, ignoreErrors, dontResolveAlias, location)
|
|
case ast.KindPropertyAccessExpression:
|
|
access := name.AsPropertyAccessExpression()
|
|
symbol = c.resolveQualifiedName(name, access.Expression, access.Name(), meaning, ignoreErrors, dontResolveAlias, 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 ||
|
|
name.Parent != nil && name.Parent.Kind == ast.KindJSExportAssignment) {
|
|
c.markSymbolOfAliasDeclarationIfTypeOnly(getAliasDeclarationFromName(name), symbol, nil /*finalTarget*/, true /*overwriteEmpty*/, nil, "")
|
|
}
|
|
if symbol.Flags&meaning == 0 && !dontResolveAlias && symbol.Flags&ast.SymbolFlagsAlias != 0 {
|
|
return c.resolveAlias(symbol)
|
|
}
|
|
}
|
|
return symbol
|
|
}
|
|
|
|
func (c *Checker) resolveQualifiedName(name *ast.Node, left *ast.Node, right *ast.Node, meaning ast.SymbolFlags, ignoreErrors bool, dontResolveAlias 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.AsVariableDeclaration().Initializer != nil &&
|
|
c.isCommonJSRequire(namespace.ValueDeclaration.AsVariableDeclaration().Initializer) {
|
|
moduleName := namespace.ValueDeclaration.AsVariableDeclaration().Initializer.AsCallExpression().Arguments.Nodes[0]
|
|
moduleSym := c.resolveExternalModuleName(moduleName, moduleName, false /*ignoreErrors*/)
|
|
if moduleSym != nil {
|
|
resolvedModuleSymbol := c.resolveExternalModuleSymbol(moduleSym, false /*dontResolveAlias*/)
|
|
if resolvedModuleSymbol != nil {
|
|
namespace = resolvedModuleSymbol
|
|
}
|
|
}
|
|
}
|
|
text := right.AsIdentifier().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.AsIdentifier().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.AsIdentifier().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.AsIdentifier().Text)
|
|
if symbol == nil {
|
|
return nil
|
|
}
|
|
n = n.Parent
|
|
}
|
|
return symbol
|
|
}
|
|
|
|
func (c *Checker) getSuggestedSymbolForNonexistentModule(name *ast.Node, targetModule *ast.Symbol) *ast.Symbol {
|
|
exports := slices.Collect(maps.Values(c.getExportsOfModule(targetModule)))
|
|
c.sortSymbols(exports)
|
|
return c.getSpellingSuggestionForName(name.Text(), exports, 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 {
|
|
for member := range symbol.AssignmentDeclarationMembers.Keys() {
|
|
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.AssertIsDefined(decl.Symbol(), "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)
|
|
}
|
|
}
|
|
|
|
// 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.")
|
|
symbol.Flags |= symbolFlags
|
|
c.lateBoundLinks.Get(member.Symbol()).lateSymbol = symbol
|
|
if len(symbol.Declarations) == 0 || member.Symbol().Flags&ast.SymbolFlagsReplaceableByMethod == 0 {
|
|
symbol.Declarations = append(symbol.Declarations, member)
|
|
}
|
|
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.AsExportDeclaration().ModuleSpecifier, false /*ignoreErrors*/)
|
|
exportedSymbols := visit(resolvedModule, node, isTypeOnly || node.AsExportDeclaration().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.diagnostics.Add(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.AsExportDeclaration().IsTypeOnly {
|
|
if typeOnlyExportStarMap == nil {
|
|
typeOnlyExportStarMap = make(map[string]*ast.Node)
|
|
}
|
|
for name := range symbols {
|
|
typeOnlyExportStarMap[name] = exportStar
|
|
}
|
|
}
|
|
return symbols
|
|
}
|
|
// 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)
|
|
}
|
|
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.AsExportDeclaration().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
|
|
}
|
|
|
|
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 {
|
|
links.aliasTarget = c.resolvingSymbol
|
|
node := c.getDeclarationOfAliasSymbol(symbol)
|
|
if node == nil {
|
|
panic("Unexpected nil in resolveAlias for symbol: " + c.symbolToString(symbol))
|
|
}
|
|
target := c.getTargetOfAliasDeclaration(node, false /*dontRecursivelyResolve*/)
|
|
if links.aliasTarget == c.resolvingSymbol {
|
|
if target == nil {
|
|
target = c.unknownSymbol
|
|
}
|
|
links.aliasTarget = target
|
|
} else {
|
|
c.error(node, diagnostics.Circular_definition_of_import_alias_0, c.symbolToString(symbol))
|
|
}
|
|
} else if links.aliasTarget == c.resolvingSymbol {
|
|
links.aliasTarget = c.unknownSymbol
|
|
}
|
|
return links.aliasTarget
|
|
}
|
|
|
|
func (c *Checker) tryResolveAlias(symbol *ast.Symbol) *ast.Symbol {
|
|
links := c.aliasSymbolLinks.Get(symbol)
|
|
if links.aliasTarget != c.resolvingSymbol {
|
|
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 typeOnlyDeclaration *ast.Node
|
|
if excludeTypeOnlyMeanings {
|
|
typeOnlyDeclaration = c.getTypeOnlyAliasDeclaration(symbol)
|
|
}
|
|
typeOnlyDeclarationIsExportStar := typeOnlyDeclaration != nil && ast.IsExportDeclaration(typeOnlyDeclaration)
|
|
var typeOnlyResolution *ast.Symbol
|
|
if typeOnlyDeclaration != nil {
|
|
if typeOnlyDeclarationIsExportStar {
|
|
moduleSpecifier := typeOnlyDeclaration.AsExportDeclaration().ModuleSpecifier
|
|
typeOnlyResolution = c.resolveExternalModuleName(moduleSpecifier, moduleSpecifier /*ignoreErrors*/, true)
|
|
} else {
|
|
typeOnlyResolution = c.resolveAlias(typeOnlyDeclaration.Symbol())
|
|
}
|
|
}
|
|
var typeOnlyExportStarTargets ast.SymbolTable
|
|
if typeOnlyDeclarationIsExportStar && typeOnlyResolution != nil {
|
|
typeOnlyExportStarTargets = c.getExportsOfModule(typeOnlyResolution)
|
|
}
|
|
var flags ast.SymbolFlags
|
|
if !excludeLocalMeanings {
|
|
flags = symbol.Flags
|
|
}
|
|
var seenSymbols collections.Set[*ast.Symbol]
|
|
for symbol.Flags&ast.SymbolFlagsAlias != 0 {
|
|
target := c.getExportSymbolOfValueSymbolIfExported(c.resolveAlias(symbol))
|
|
if !typeOnlyDeclarationIsExportStar && target == typeOnlyResolution || typeOnlyExportStarTargets[target.Name] == target {
|
|
break
|
|
}
|
|
if target == c.unknownSymbol {
|
|
return ast.SymbolFlagsAll
|
|
}
|
|
// Optimizations - try to avoid creating or adding to
|
|
// `seenSymbols` if possible
|
|
if target == symbol || seenSymbols.Has(target) {
|
|
break
|
|
}
|
|
if target.Flags&ast.SymbolFlagsAlias != 0 {
|
|
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) {
|
|
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.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.SymbolFlagsAccessor != 0 {
|
|
return c.getTypeOfAccessors(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.IsParameter(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
|
|
}
|
|
if symbol.Flags&ast.SymbolFlagsModuleExports != 0 && symbol.ValueDeclaration != nil {
|
|
fileSymbol := c.resolveExternalModuleSymbol(symbol.ValueDeclaration.Symbol(), false /*dontResolveAlias*/)
|
|
members := make(ast.SymbolTable, 1)
|
|
members["exports"] = fileSymbol
|
|
return c.newAnonymousType(symbol, members, nil, nil, nil)
|
|
}
|
|
debug.AssertIsDefined(symbol.ValueDeclaration)
|
|
declaration := symbol.ValueDeclaration
|
|
if ast.IsSourceFile(declaration) && ast.IsJsonSourceFile(declaration.AsSourceFile()) {
|
|
statements := declaration.AsSourceFile().Statements.Nodes
|
|
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)
|
|
}
|
|
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, ast.KindJSExportAssignment:
|
|
if declaration.Type() != nil {
|
|
result = c.getTypeFromTypeNode(declaration.Type())
|
|
} else {
|
|
result = c.widenTypeForVariableLikeDeclaration(c.checkExpressionCached(declaration.AsExportAssignment().Expression), declaration, false /*reportErrors*/)
|
|
}
|
|
case ast.KindBinaryExpression:
|
|
result = c.getWidenedTypeForAssignmentDeclaration(symbol)
|
|
case ast.KindJsxAttribute:
|
|
result = c.checkJsxAttribute(declaration, CheckModeNormal)
|
|
case ast.KindEnumMember:
|
|
result = c.getTypeOfEnumMember(symbol)
|
|
case ast.KindCommonJSExport:
|
|
result = c.checkExpression(declaration.AsCommonJSExport().Initializer)
|
|
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.IsParameter(declaration) {
|
|
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.AssertNil(thisParameter.Type())
|
|
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.IsParameter(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.AsBindingPattern().Elements.Nodes {
|
|
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.AsBindingPattern().Elements.Nodes
|
|
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
|
|
}
|
|
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.IsParameter(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.AsMappedTypeNode().Type != nil &&
|
|
ast.SkipParentheses(parent.AsMappedTypeNode().Type) == declaration.Parent &&
|
|
ast.IsConditionalTypeNode(parent.Parent) &&
|
|
parent.Parent.AsConditionalTypeNode().ExtendsType == parent &&
|
|
ast.IsMappedTypeNode(parent.Parent.AsConditionalTypeNode().CheckType) &&
|
|
parent.Parent.AsConditionalTypeNode().CheckType.AsMappedTypeNode().Type != nil:
|
|
checkMappedType := parent.Parent.AsConditionalTypeNode().CheckType
|
|
nodeType := c.getTypeFromTypeNode(checkMappedType.AsMappedTypeNode().Type)
|
|
checkMappedTypeParameter := checkMappedType.AsMappedTypeNode().TypeParameter
|
|
mapper := newSimpleTypeMapper(c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(checkMappedTypeParameter)),
|
|
core.IfElse(checkMappedTypeParameter.AsTypeParameter().Constraint != nil,
|
|
c.getTypeFromTypeNode(checkMappedTypeParameter.AsTypeParameter().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[string]*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
|
|
}
|
|
|
|
type KeyBuilder struct {
|
|
strings.Builder
|
|
}
|
|
|
|
var base64chars = []byte{
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
|
|
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
|
|
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
|
|
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '$', '%',
|
|
}
|
|
|
|
func (b *KeyBuilder) WriteUint64(value uint64) {
|
|
for value != 0 {
|
|
b.WriteByte(base64chars[value&0x3F])
|
|
value >>= 6
|
|
}
|
|
}
|
|
|
|
func (b *KeyBuilder) WriteInt(value int) {
|
|
b.WriteUint64(uint64(int64(value)))
|
|
}
|
|
|
|
func (b *KeyBuilder) WriteSymbolId(id ast.SymbolId) {
|
|
b.WriteUint64(uint64(id))
|
|
}
|
|
|
|
func (b *KeyBuilder) WriteSymbol(s *ast.Symbol) {
|
|
b.WriteSymbolId(ast.GetSymbolId(s))
|
|
}
|
|
|
|
func (b *KeyBuilder) WriteTypeId(id TypeId) {
|
|
b.WriteUint64(uint64(id))
|
|
}
|
|
|
|
func (b *KeyBuilder) WriteType(t *Type) {
|
|
b.WriteTypeId(t.id)
|
|
}
|
|
|
|
func (b *KeyBuilder) WriteTypes(types []*Type) {
|
|
i := 0
|
|
var tail bool
|
|
for i < len(types) {
|
|
startId := types[i].id
|
|
count := 1
|
|
for i+count < len(types) && types[i+count].id == startId+TypeId(count) {
|
|
count++
|
|
}
|
|
if tail {
|
|
b.WriteByte(',')
|
|
}
|
|
b.WriteTypeId(startId)
|
|
if count > 1 {
|
|
b.WriteByte(':')
|
|
b.WriteInt(count)
|
|
}
|
|
i += count
|
|
tail = true
|
|
}
|
|
}
|
|
|
|
func (b *KeyBuilder) WriteAlias(alias *TypeAlias) {
|
|
if alias != nil {
|
|
b.WriteByte('@')
|
|
b.WriteSymbol(alias.symbol)
|
|
if len(alias.typeArguments) != 0 {
|
|
b.WriteByte(':')
|
|
b.WriteTypes(alias.typeArguments)
|
|
}
|
|
}
|
|
}
|
|
|
|
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(A<T, number, U>) writes "111=0-12=1"
|
|
// where A.id=111 and number.id=12
|
|
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) {
|
|
b.WriteUint64(uint64(id))
|
|
}
|
|
|
|
func (b *KeyBuilder) WriteNode(node *ast.Node) {
|
|
if node != nil {
|
|
b.WriteNodeId(ast.GetNodeId(node))
|
|
}
|
|
}
|
|
|
|
func getTypeListKey(types []*Type) string {
|
|
var b KeyBuilder
|
|
b.WriteTypes(types)
|
|
return b.String()
|
|
}
|
|
|
|
func getAliasKey(alias *TypeAlias) string {
|
|
var b KeyBuilder
|
|
b.WriteAlias(alias)
|
|
return b.String()
|
|
}
|
|
|
|
func getUnionKey(types []*Type, origin *Type, alias *TypeAlias) string {
|
|
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.String()
|
|
}
|
|
|
|
func getIntersectionKey(types []*Type, flags IntersectionFlags, alias *TypeAlias) string {
|
|
var b KeyBuilder
|
|
b.WriteTypes(types)
|
|
if flags&IntersectionFlagsNoConstraintReduction == 0 {
|
|
b.WriteAlias(alias)
|
|
} else {
|
|
b.WriteByte('*')
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func getTupleKey(elementInfos []TupleElementInfo, readonly bool) string {
|
|
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.String()
|
|
}
|
|
|
|
func getTypeAliasInstantiationKey(typeArguments []*Type, alias *TypeAlias) string {
|
|
return getTypeInstantiationKey(typeArguments, alias, false)
|
|
}
|
|
|
|
func getTypeInstantiationKey(typeArguments []*Type, alias *TypeAlias, singleSignature bool) string {
|
|
var b KeyBuilder
|
|
b.WriteTypes(typeArguments)
|
|
b.WriteAlias(alias)
|
|
if singleSignature {
|
|
b.WriteByte('!')
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func getIndexedAccessKey(objectType *Type, indexType *Type, accessFlags AccessFlags, alias *TypeAlias) string {
|
|
var b KeyBuilder
|
|
b.WriteType(objectType)
|
|
b.WriteByte(',')
|
|
b.WriteType(indexType)
|
|
b.WriteByte(',')
|
|
b.WriteUint64(uint64(accessFlags))
|
|
b.WriteAlias(alias)
|
|
return b.String()
|
|
}
|
|
|
|
func getTemplateTypeKey(texts []string, types []*Type) string {
|
|
var b KeyBuilder
|
|
b.WriteTypes(types)
|
|
b.WriteByte('|')
|
|
for i, s := range texts {
|
|
if i != 0 {
|
|
b.WriteByte(',')
|
|
}
|
|
b.WriteInt(len(s))
|
|
}
|
|
b.WriteByte('|')
|
|
for _, s := range texts {
|
|
b.WriteString(s)
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func getConditionalTypeKey(typeArguments []*Type, alias *TypeAlias, forConstraint bool) string {
|
|
var b KeyBuilder
|
|
b.WriteTypes(typeArguments)
|
|
b.WriteAlias(alias)
|
|
if forConstraint {
|
|
b.WriteByte('!')
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func getRelationKey(source *Type, target *Type, intersectionState IntersectionState, isIdentity bool, ignoreConstraints bool) string {
|
|
if isIdentity && source.id > target.id {
|
|
source, target = target, source
|
|
}
|
|
var b KeyBuilder
|
|
var constrained bool
|
|
if isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target) {
|
|
constrained = b.WriteGenericTypeReferences(source, target, ignoreConstraints)
|
|
} else {
|
|
b.WriteType(source)
|
|
b.WriteByte(',')
|
|
b.WriteType(target)
|
|
}
|
|
if intersectionState != IntersectionStateNone {
|
|
b.WriteByte(':')
|
|
b.WriteUint64(uint64(intersectionState))
|
|
}
|
|
if constrained {
|
|
// We mark keys with type references that reference constrained type parameters such that we know
|
|
// to obtain and look for a "broadest equivalent key" in the cache.
|
|
b.WriteByte('*')
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
func getNodeListKey(nodes []*ast.Node) string {
|
|
var b KeyBuilder
|
|
for i, n := range nodes {
|
|
if i > 0 {
|
|
b.WriteByte(',')
|
|
}
|
|
b.WriteNode(n)
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
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.AsTypeParameter().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 {
|
|
return c.getTypeForVariableLikeDeclaration(node, false /*includeOptionality*/, checkMode)
|
|
}
|
|
symbol := c.getSymbolOfDeclaration(node)
|
|
if symbol != nil {
|
|
resolvedType := c.valueSymbolLinks.Get(symbol).resolvedType
|
|
if resolvedType != nil {
|
|
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.AsBindingPattern().Elements.Nodes
|
|
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.AsBindingPattern().Elements.Nodes, 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)
|
|
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.AsBindingPattern().Elements.Nodes {
|
|
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.AsBindingPattern().Elements.Nodes
|
|
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 {
|
|
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.IsParameter(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 _, declaration := range symbol.Declarations {
|
|
if ast.IsBinaryExpression(declaration) {
|
|
if declaration.Type() != nil {
|
|
t = c.getTypeFromTypeNode(declaration.Type())
|
|
break
|
|
}
|
|
types = core.AppendIfUnique(types, c.checkExpressionForMutableLocation(declaration.AsBinaryExpression().Right, CheckModeNormal))
|
|
}
|
|
}
|
|
if kind == thisAssignmentDeclarationMethod && len(types) > 0 {
|
|
if c.strictNullChecks {
|
|
types = core.AppendIfUnique(types, c.undefinedOrMissingType)
|
|
}
|
|
}
|
|
if t == nil {
|
|
t = c.getWidenedType(c.getUnionType(types))
|
|
}
|
|
}
|
|
// 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
|
|
}
|
|
|
|
// 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(bin) == 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.IsParameter(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) {
|
|
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 {
|
|
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)
|
|
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 = &WideningContext{parent: context, propertyName: prop.Name}
|
|
}
|
|
widened := c.getWidenedTypeWithContext(original, propContext)
|
|
if widened == original {
|
|
return prop
|
|
}
|
|
return c.createSymbolWithType(prop, widened)
|
|
}
|
|
|
|
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)
|
|
property := ast.GetDeclarationOfKind(symbol, ast.KindPropertyDeclaration)
|
|
var accessor *ast.Node
|
|
if property != nil && ast.IsAutoAccessorPropertyDeclaration(property) {
|
|
accessor = property
|
|
}
|
|
// 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), true /*dontRecursivelyResolve*/)
|
|
// 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
|
|
}
|
|
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.IsParameter(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, len(typeParameters) == 1 /*mappingThisOnly*/)
|
|
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)
|
|
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
|
|
}))
|
|
}
|
|
}
|
|
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 {
|
|
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.diagnostics.Add(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 N type params thru the immediate constraint mapper up to N times
|
|
// This way any noncircular interdependent type parameters are definitely resolved to their external dependencies
|
|
for range typeParameters {
|
|
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))
|
|
}
|
|
|
|
// 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].AsParameterDeclaration().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.
|
|
if ast.IsFunctionExpressionOrArrowFunction(decl) || ast.IsObjectLiteralMethod(decl) {
|
|
if sig := c.getSignatureOfFullSignatureType(decl); sig != nil {
|
|
result = append(result, sig)
|
|
continue
|
|
}
|
|
}
|
|
result = append(result, c.getSignatureFromDeclaration(decl))
|
|
}
|
|
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)
|
|
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.AsCallExpression().Arguments.Nodes) && 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 {
|
|
var result []*Type
|
|
for _, node := range declaration.TypeParameters() {
|
|
result = core.AppendIfUnique(result, c.getDeclaredTypeOfTypeParameter(node.Symbol()))
|
|
}
|
|
if len(result) == 0 && ast.IsFunctionDeclaration(declaration) {
|
|
if sig := c.getSignatureOfFullSignatureType(declaration); sig != nil {
|
|
return sig.TypeParameters()
|
|
}
|
|
}
|
|
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.isTypeUsableAsIndexSignature(c.checkComputedPropertyName(node))
|
|
}
|
|
return c.isTypeUsableAsIndexSignature(c.checkExpressionCached(node.AsElementAccessExpression().ArgumentExpression))
|
|
}
|
|
|
|
func (c *Checker) isTypeUsableAsIndexSignature(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.AsComputedPropertyName().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.IsFunctionLike(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 getSetAccessorValueParameter(accessor *ast.Node) *ast.Node {
|
|
parameters := accessor.Parameters()
|
|
if len(parameters) > 0 {
|
|
hasThis := len(parameters) == 2 && ast.IsThisParameter(parameters[0])
|
|
return parameters[core.IfElse(hasThis, 1, 0)]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Checker) getReturnTypeFromBody(fn *ast.Node, checkMode CheckMode) *Type {
|
|
body := fn.Body()
|
|
if body == nil {
|
|
return c.errorType
|
|
}
|
|
functionFlags := getFunctionFlags(fn)
|
|
isAsync := (functionFlags & FunctionFlagsAsync) != 0
|
|
isGenerator := (functionFlags & 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 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&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 && core.OrElse(c.unwrapReturnType(contextualReturnType, functionFlags), c.voidType).flags&TypeFlagsUndefined != 0 {
|
|
returnType = c.undefinedType
|
|
} else {
|
|
returnType = c.voidType
|
|
}
|
|
if functionFlags&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 := 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&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&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
|
|
}
|
|
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 := (getFunctionFlags(fn) & FunctionFlagsAsync) != 0
|
|
forEachYieldExpression(fn.Body(), func(yieldExpr *ast.Node) {
|
|
yieldExprType := c.undefinedWideningType
|
|
if yieldExpr.Expression() != nil {
|
|
yieldExprType = c.checkExpressionEx(yieldExpr.Expression(), checkMode)
|
|
}
|
|
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 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 FunctionFlags) *Type {
|
|
isGenerator := functionFlags&FunctionFlagsGenerator != 0
|
|
isAsync := functionFlags&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 := getFunctionFlags(declaration)
|
|
switch wideningKind {
|
|
case WideningKindFunctionReturn:
|
|
if flags&FunctionFlagsGenerator != 0 {
|
|
returnType = core.OrElse(c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, returnType, flags&FunctionFlagsAsync != 0), returnType)
|
|
} else if flags&FunctionFlagsAsync != 0 {
|
|
returnType = core.OrElse(c.getAwaitedTypeNoAlias(returnType), returnType)
|
|
}
|
|
return c.isGenericType(returnType)
|
|
case WideningKindGeneratorYield:
|
|
yieldType := c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindYield, returnType, flags&FunctionFlagsAsync != 0)
|
|
return yieldType != nil && c.isGenericType(yieldType)
|
|
case WideningKindGeneratorNext:
|
|
nextType := c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindNext, returnType, flags&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 := getFunctionFlags(fn)
|
|
if functionFlags != 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...)
|
|
}
|
|
}
|
|
|
|
// The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true,
|
|
// we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation.
|
|
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
|
|
}
|
|
|
|
// The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true,
|
|
// we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation.
|
|
func (c *Checker) instantiateSymbolTable(symbols ast.SymbolTable, m *TypeMapper, mappingThisOnly bool) ast.SymbolTable {
|
|
if len(symbols) == 0 {
|
|
return nil
|
|
}
|
|
result := make(ast.SymbolTable, len(symbols))
|
|
for id, symbol := range symbols {
|
|
if c.isNamedMember(symbol, id) {
|
|
if mappingThisOnly && isThisless(symbol) {
|
|
result[id] = symbol
|
|
} else {
|
|
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 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 class or 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.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.AsTypeParameter().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() {
|
|
c.error(c.currentNode, diagnostics.Type_of_property_0_circularly_references_itself_in_mapped_type_1, c.symbolToString(symbol), c.TypeToString(mappedType))
|
|
propType = c.errorType
|
|
}
|
|
if links.resolvedType == nil {
|
|
links.resolvedType = propType
|
|
}
|
|
}
|
|
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.AssertIsDefined(signature, "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.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), 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), 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
|
|
}
|
|
|
|
type allAccessorDeclarations struct {
|
|
firstAccessor *ast.AccessorDeclaration
|
|
secondAccessor *ast.AccessorDeclaration
|
|
setAccessor *ast.SetAccessorDeclaration
|
|
getAccessor *ast.GetAccessorDeclaration
|
|
}
|
|
|
|
func (c *Checker) getAllAccessorDeclarationsForDeclaration(accessor *ast.AccessorDeclaration) allAccessorDeclarations {
|
|
var otherKind ast.Kind
|
|
if accessor.Kind == ast.KindSetAccessor {
|
|
otherKind = ast.KindGetAccessor
|
|
} else if accessor.Kind == ast.KindGetAccessor {
|
|
otherKind = ast.KindSetAccessor
|
|
} else {
|
|
panic(fmt.Sprintf("Unexpected node kind %q", accessor.Kind))
|
|
}
|
|
otherAccessor := ast.GetDeclarationOfKind(c.getSymbolOfDeclaration(accessor), otherKind)
|
|
|
|
var firstAccessor *ast.AccessorDeclaration
|
|
var secondAccessor *ast.AccessorDeclaration
|
|
if otherAccessor != nil && (otherAccessor.Pos() < accessor.Pos()) {
|
|
firstAccessor = otherAccessor
|
|
secondAccessor = accessor
|
|
} else {
|
|
firstAccessor = accessor
|
|
secondAccessor = otherAccessor
|
|
}
|
|
|
|
var setAccessor *ast.SetAccessorDeclaration
|
|
var getAccessor *ast.GetAccessorDeclaration
|
|
if accessor.Kind == ast.KindSetAccessor {
|
|
setAccessor = accessor.AsSetAccessorDeclaration()
|
|
if otherAccessor != nil {
|
|
getAccessor = otherAccessor.AsGetAccessorDeclaration()
|
|
}
|
|
} else {
|
|
getAccessor = accessor.AsGetAccessorDeclaration()
|
|
if otherAccessor != nil {
|
|
setAccessor = otherAccessor.AsSetAccessorDeclaration()
|
|
}
|
|
}
|
|
|
|
return allAccessorDeclarations{
|
|
firstAccessor: firstAccessor,
|
|
secondAccessor: secondAccessor,
|
|
setAccessor: setAccessor,
|
|
getAccessor: getAccessor,
|
|
}
|
|
}
|
|
|
|
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.AsTupleTypeNode().Elements.Nodes, 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.AsTypeParameter().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 >= minTypeArgumentCount && 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.AsTypeParameter().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) []*ast.Symbol {
|
|
if len(members) == 0 {
|
|
return nil
|
|
}
|
|
result := make([]*ast.Symbol, 0, len(members))
|
|
for id, symbol := range members {
|
|
if c.isNamedMember(symbol, id) {
|
|
result = append(result, symbol)
|
|
}
|
|
}
|
|
c.sortSymbols(result)
|
|
return result
|
|
}
|
|
|
|
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.
|
|
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.String()
|
|
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[string]*Type, 1)
|
|
}
|
|
} else {
|
|
c.activeTypeMappersCaches = append(c.activeTypeMappersCaches, make(map[string]*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[string]*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, func(t *Type) *Type { return mapper.Map(t) })
|
|
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.AsTypeParameter().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.AsJSDocNonNullableType().Type)
|
|
case ast.KindJSDocNullableType:
|
|
t := c.getTypeFromTypeNode(node.AsJSDocNullableType().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.AsJSDocOptionalType().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.AsParenthesizedTypeNode().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 c.getDeclaredTypeOfClassOrInterface(c.getSymbolOfDeclaration(parent)).AsInterfaceType().thisType
|
|
}
|
|
}
|
|
}
|
|
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.AsTypeOperatorNode().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 KeyBuilder
|
|
b.WriteString(ast.InternalSymbolNamePrefix)
|
|
b.WriteByte('@')
|
|
b.WriteString(symbol.Name)
|
|
b.WriteByte('@')
|
|
b.WriteSymbol(symbol)
|
|
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.
|
|
symbol := c.getSymbolFromTypeReference(node)
|
|
// 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 {
|
|
links.resolvedType = c.getTypeReferenceType(node, symbol)
|
|
}
|
|
}
|
|
return links.resolvedType
|
|
}
|
|
|
|
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)
|
|
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.createTypeReference(t, typeArguments)
|
|
}
|
|
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 {
|
|
c.error(node, diagnostics.Type_0_is_not_generic, c.symbolToString(symbol))
|
|
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.AsTupleTypeNode().Elements.Nodes, 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.AsTypeOperatorNode().Type)
|
|
case ast.KindParenthesizedType:
|
|
return c.mayResolveTypeAlias(node.AsParenthesizedTypeNode().Type)
|
|
case ast.KindOptionalType:
|
|
return c.mayResolveTypeAlias(node.AsOptionalTypeNode().Type)
|
|
case ast.KindNamedTupleMember:
|
|
return c.mayResolveTypeAlias(node.AsNamedTupleMember().Type)
|
|
case ast.KindRestType:
|
|
return node.AsRestTypeNode().Type.Kind != ast.KindArrayType || c.mayResolveTypeAlias(node.AsRestTypeNode().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) createNormalizedTupleType(target *Type, elementTypes []*Type) *Type {
|
|
d := target.AsTupleType()
|
|
if d.combinedFlags&ElementFlagsNonRequired == 0 {
|
|
// No need to normalize when we only have regular required elements
|
|
return c.createTypeReference(target, elementTypes)
|
|
}
|
|
if d.combinedFlags&ElementFlagsVariadic != 0 {
|
|
for i, e := range elementTypes {
|
|
if 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 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.createNormalizedTupleType(target, core.ReplaceElement(elementTypes, i, t))
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// We have optional, rest, or variadic n that may need normalizing. Normalization ensures that all variadic
|
|
// n are generic and that the tuple type has one of the following layouts, disregarding variadic n:
|
|
// (1) Zero or more required n, followed by zero or more optional n, followed by zero or one rest element.
|
|
// (2) Zero or more required n, followed by a rest element, followed by zero or more required n.
|
|
// In either layout, zero or more generic variadic n may be present at any location.
|
|
n := &TupleNormalizer{}
|
|
if !n.normalize(c, elementTypes, d.elementInfos) {
|
|
return c.errorType
|
|
}
|
|
tupleTarget := c.getTupleTargetType(n.infos, d.readonly)
|
|
switch {
|
|
case tupleTarget == c.emptyGenericType:
|
|
return c.emptyObjectType
|
|
case len(n.types) != 0:
|
|
return c.createTypeReference(tupleTarget, n.types)
|
|
}
|
|
return tupleTarget
|
|
}
|
|
|
|
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.AsTypeReference().TypeName
|
|
case ast.KindExpressionWithTypeArguments:
|
|
// We only support expressions that are simple qualified names. For other
|
|
// expressions this produces nil
|
|
expr := node.AsExpressionWithTypeArguments().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.AssertIsDefined(declaration, "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.AsTypeAliasDeclaration().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[string]*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
|
|
var autoValue 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 {
|
|
autoValue = value + 1
|
|
} else {
|
|
autoValue = jsnum.NaN()
|
|
}
|
|
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) && !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.IsNaN() {
|
|
c.error(member.Name(), diagnostics.Enum_member_must_have_initializer)
|
|
return evaluator.NewResult(nil, false, false, false)
|
|
}
|
|
if c.compilerOptions.GetIsolatedModules() && previous != nil && previous.AsEnumMember().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 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 {
|
|
debug.Assert(ast.GetSourceFileOfNode(member.ValueDeclaration) == ast.GetSourceFileOfNode(rootSymbol.ValueDeclaration))
|
|
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.AsTupleTypeNode().Elements.Nodes, c.isVariadicTupleElement)) && c.isDeferredTypeReferenceNode(node, false) {
|
|
if node.Kind == ast.KindTupleType && len(node.AsTupleTypeNode().Elements.Nodes) == 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.AsTupleTypeNode().Elements.Nodes, c.getTypeFromTypeNode)
|
|
}
|
|
links.resolvedType = c.createNormalizedTypeReference(target, elementTypes)
|
|
}
|
|
}
|
|
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.AsTupleTypeNode().Elements.Nodes, 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.AsNamedTupleMember().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.AsParenthesizedTypeNode().Type)
|
|
case ast.KindTupleType:
|
|
if len(node.AsTupleTypeNode().Elements.Nodes) == 1 {
|
|
node = node.AsTupleTypeNode().Elements.Nodes[0]
|
|
if node.Kind == ast.KindRestType {
|
|
return c.getArrayElementTypeNode(node.AsRestTypeNode().Type)
|
|
}
|
|
if node.Kind == ast.KindNamedTupleMember && node.AsNamedTupleMember().DotDotDotToken != nil {
|
|
return c.getArrayElementTypeNode(node.AsNamedTupleMember().Type)
|
|
}
|
|
}
|
|
case ast.KindArrayType:
|
|
return node.AsArrayTypeNode().ElementType
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *Checker) getTypeFromOptionalTypeNode(node *ast.Node) *Type {
|
|
return c.addOptionalityEx(c.getTypeFromTypeNode(node.AsOptionalTypeNode().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.AsTemplateLiteralTypeSpan().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[string]*Type)
|
|
root.instantiations[getTypeListKey(outerTypeParameters)] = 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.AsTupleTypeNode().Elements.Nodes) == len(extendsTypeNode.AsTupleTypeNode().Elements.Nodes)
|
|
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.AsTupleTypeNode().Elements.Nodes) > 0 && !core.Some(node.AsTupleTypeNode().Elements.Nodes, func(e *ast.Node) bool {
|
|
return ast.IsOptionalTypeNode(e) || ast.IsRestTypeNode(e) || ast.IsNamedTupleMember(e) && (e.AsNamedTupleMember().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)
|
|
}
|
|
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 moduleSymbol.Flags&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.AsRestTypeNode().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.IsParameter(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[string]*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.IsParameter(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.AsMappedTypeNode().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.AsTupleTypeNode().Elements.Nodes[0], extendsNode.AsTupleTypeNode().Elements.Nodes[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.AsTupleTypeNode().Elements.Nodes) == 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
|
|
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 {
|
|
id := getTypeListKey(typeArguments)
|
|
intf := target.AsInterfaceType()
|
|
if t, ok := intf.instantiations[id]; ok {
|
|
return t
|
|
}
|
|
t := c.newObjectType(ObjectFlagsReference, target.symbol)
|
|
t.objectFlags |= c.getPropagatingFlagsOfTypes(typeArguments, TypeFlagsNone)
|
|
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)
|
|
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 {
|
|
sig := c.signaturePool.New()
|
|
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.indexInfoPool.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 {
|
|
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 value.(type) {
|
|
case string:
|
|
flags = TypeFlagsEnumLiteral | TypeFlagsStringLiteral
|
|
case jsnum.Number:
|
|
flags = TypeFlagsEnumLiteral | TypeFlagsNumberLiteral
|
|
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())
|
|
}
|
|
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 {
|
|
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))
|
|
orderedTypes.valuesByKey = make(map[*Type]struct{}, 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 {
|
|
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:
|
|
size *= len(t.Types())
|
|
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 {
|
|
var origin *Type
|
|
if includeOrigin && t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) != 0 || t.alias != nil {
|
|
origin = c.newIndexType(t, IndexFlagsNone)
|
|
}
|
|
var types []*Type
|
|
for _, prop := range c.getPropertiesOfType(t) {
|
|
types = append(types, c.getLiteralTypeFromProperty(prop, include, false))
|
|
}
|
|
for _, info := range c.getIndexInfosOfType(t) {
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
return c.getUnionTypeEx(types, UnionReductionLiteral, nil, origin)
|
|
}
|
|
|
|
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.hasDistributiveNameType(t) || c.getMappedTypeNameTypeKind(t) == MappedTypeNameTypeKindRemapping) ||
|
|
t.flags&TypeFlagsUnion != 0 && indexFlags&IndexFlagsNoReducibleCheck == 0 && c.isGenericReducibleType(t) ||
|
|
t.flags&TypeFlagsIntersection != 0 && c.maybeTypeOfKind(t, TypeFlagsInstantiable) && core.Some(t.Types(), c.IsEmptyAnonymousObjectType)
|
|
}
|
|
|
|
// Ordinarily we reduce a keyof M, where M is a mapped type { [P in K as N<P>]: X }, to simply N<K>. This however presumes
|
|
// that N distributes over union types, i.e. that N<A | B | C> is equivalent to N<A> | N<B> | N<C>. Specifically, we only
|
|
// want to perform the reduction when the name type of a mapped type is distributive with respect to the type variable
|
|
// introduced by the 'in' clause of the mapped type. Note that non-generic types are considered to be distributive because
|
|
// they're the same type regardless of what's being distributed over.
|
|
func (c *Checker) hasDistributiveNameType(mappedType *Type) bool {
|
|
typeVariable := c.getTypeParameterFromMappedType(mappedType)
|
|
var isDistributive func(*Type) bool
|
|
isDistributive = func(t *Type) bool {
|
|
switch {
|
|
case t.flags&(TypeFlagsAnyOrUnknown|TypeFlagsPrimitive|TypeFlagsNever|TypeFlagsTypeParameter|TypeFlagsObject|TypeFlagsNonPrimitive) != 0:
|
|
return true
|
|
case t.flags&TypeFlagsConditional != 0:
|
|
return t.AsConditionalType().root.isDistributive && t.AsConditionalType().checkType == typeVariable
|
|
case t.flags&TypeFlagsUnionOrIntersection != 0:
|
|
return core.Every(t.Types(), isDistributive)
|
|
case t.flags&TypeFlagsTemplateLiteral != 0:
|
|
return core.Every(t.AsTemplateLiteralType().types, isDistributive)
|
|
case t.flags&TypeFlagsIndexedAccess != 0:
|
|
return isDistributive(t.AsIndexedAccessType().objectType) && isDistributive(t.AsIndexedAccessType().indexType)
|
|
case t.flags&TypeFlagsSubstitution != 0:
|
|
return isDistributive(t.AsSubstitutionType().baseType) && isDistributive(t.AsSubstitutionType().constraint)
|
|
case t.flags&TypeFlagsStringMapping != 0:
|
|
return isDistributive(t.Target())
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
nameType := c.getNameTypeFromMappedType(mappedType)
|
|
if nameType == nil {
|
|
nameType = typeVariable
|
|
}
|
|
return isDistributive(nameType)
|
|
}
|
|
|
|
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 (but a distributive 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) {
|
|
// deprecatedNode := /* TODO(TS-TO-GO) QuestionQuestionToken BinaryExpression: accessExpression?.argumentExpression ?? (isIndexedAccessTypeNode(accessNode) ? accessNode.indexType : accessNode) */ TODO
|
|
// c.addDeprecatedSuggestion(deprecatedNode, prop.declarations, propName /* as string */)
|
|
// }
|
|
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 accessExpression != nil && !isConstEnumObjectType(objectType) {
|
|
if isObjectLiteralType(objectType) {
|
|
if c.noImplicitAny && indexType.flags&(TypeFlagsStringLiteral|TypeFlagsNumberLiteral) != 0 {
|
|
c.diagnostics.Add(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.diagnostics.Add(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 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, 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.Filter(target.Types(), func(t *Type) bool { return t.flags&TypeFlagsStringLiteral != 0 })
|
|
return core.GetSpellingSuggestion(getStringLiteralValue(source), candidates, getStringLiteralValue)
|
|
}
|
|
|
|
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.AsComputedPropertyName().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
|
|
}
|
|
// references through namespace import should be readonly
|
|
if expressionSymbol.Flags&ast.SymbolFlagsAlias != 0 {
|
|
declaration := c.getDeclarationOfAliasSymbol(expressionSymbol)
|
|
return declaration != nil && ast.IsNamespaceImport(declaration)
|
|
}
|
|
}
|
|
}
|
|
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
|
|
}
|
|
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) != 0 || c.isGenericTupleType(t) {
|
|
constraint := c.getResolvedBaseConstraint(t, nil)
|
|
if constraint != c.noConstraintType && constraint != c.circularConstraintType {
|
|
return constraint
|
|
}
|
|
return nil
|
|
}
|
|
if t.flags&TypeFlagsIndex != 0 {
|
|
return c.stringNumberSymbolType
|
|
}
|
|
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:
|
|
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:
|
|
return c.getNextBaseConstraint(c.getConstraintFromConditionalType(t), 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.getTypeOfSymbol(sourceProp), c.getTypeOfSymbol(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
|
|
// 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 {
|
|
c.cachedTypes[key] = distributedOverIndex
|
|
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 {
|
|
c.cachedTypes[key] = distributedOverObject
|
|
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 {
|
|
c.cachedTypes[key] = elementType
|
|
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 {
|
|
result := c.mapType(c.substituteIndexedMappedType(objectType, t.AsIndexedAccessType().indexType), func(t *Type) *Type {
|
|
return c.getSimplifiedType(t, writing)
|
|
})
|
|
c.cachedTypes[key] = result
|
|
return result
|
|
}
|
|
}
|
|
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).AsTypeOperatorNode().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:
|
|
// 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 || !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.AsExportAssignment().Expression == n && ast.IsEntityNameExpression(n)
|
|
}
|
|
if ast.IsExportSpecifier(parent) {
|
|
return parent.AsExportSpecifier().Name() == n || parent.AsExportSpecifier().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.AsExportSpecifier().IsTypeOnly {
|
|
return false
|
|
}
|
|
if parent.Parent != nil {
|
|
greatGrandparent := parent.Parent.Parent
|
|
if greatGrandparent != nil && ast.IsExportDeclaration(greatGrandparent) && greatGrandparent.AsExportDeclaration().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) {
|
|
var left *ast.Node
|
|
if ast.IsPropertyAccessExpression(location) {
|
|
left = location.AsPropertyAccessExpression().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 (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 refereced
|
|
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.AsExportDeclaration().ModuleSpecifier == nil && !location.AsExportSpecifier().IsTypeOnly && !location.Parent.Parent.AsExportDeclaration().IsTypeOnly {
|
|
exportedName := location.PropertyName()
|
|
if exportedName == nil {
|
|
exportedName = location.Name()
|
|
}
|
|
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) markDecoratorAliasReferenced(node *ast.Node /*HasDecorators*/) {
|
|
// !!! Implement if/when we support emitDecoratorMetadata
|
|
}
|
|
|
|
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.AssertIsDefined(thisTypeForError)
|
|
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.AsTypeParameter().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, 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(sb.String())
|
|
}
|
|
newTexts = append(newTexts, 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 strings.ToUpper(str)
|
|
case IntrinsicTypeKindLowercase:
|
|
return strings.ToLower(str)
|
|
case IntrinsicTypeKindCapitalize:
|
|
_, size := utf8.DecodeRuneInString(str)
|
|
return strings.ToUpper(str[:size]) + str[size:]
|
|
case IntrinsicTypeKindUncapitalize:
|
|
_, size := utf8.DecodeRuneInString(str)
|
|
return strings.ToLower(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 isConstAssertion(parent) {
|
|
return c.getContextualType(parent, contextFlags)
|
|
}
|
|
return c.getTypeFromTypeNode(getAssertedTypeNode(parent))
|
|
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.AsArrayLiteralExpression().Elements.Nodes, node)
|
|
firstSpreadIndex, lastSpreadIndex := c.getSpreadIndices(parent)
|
|
return c.getContextualTypeForElementExpression(t, elementIndex, len(parent.AsArrayLiteralExpression().Elements.Nodes), 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.AsSatisfiesExpression().Type)
|
|
case ast.KindExportAssignment, ast.KindJSExportAssignment, ast.KindCommonJSExport:
|
|
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().AsBindingPattern().Elements.Nodes) > 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.AsBindingPattern().Elements.Nodes, 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 := getFunctionFlags(fn)
|
|
if functionFlags&FunctionFlagsGenerator != 0 {
|
|
isAsyncGenerator := (functionFlags & 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&FunctionFlagsAsync) != 0)
|
|
if iterationReturnType == nil {
|
|
return nil
|
|
}
|
|
contextualReturnType = iterationReturnType
|
|
// falls through to unwrap Promise for AsyncGenerators
|
|
}
|
|
if functionFlags&FunctionFlagsAsync != 0 {
|
|
// Get the awaited type without the `Awaited<T>` alias
|
|
contextualAwaitedType := c.mapType(contextualReturnType, c.getAwaitedTypeNoAlias)
|
|
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 := getFunctionFlags(functionDecl)&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 := getFunctionFlags(functionDecl)
|
|
if functionFlags&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&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 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&FunctionFlagsAsync) != 0), c.anyType)
|
|
generatorReturnType := core.OrElse(c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindReturn, returnType, (functionFlags&FunctionFlagsAsync) != 0), generatorYieldType)
|
|
generatorNextType := core.OrElse(c.getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKindNext, returnType, (functionFlags&FunctionFlagsAsync) != 0), c.unknownType)
|
|
generatorInstantiation := c.createGeneratorType(generatorYieldType, generatorReturnType, generatorNextType, functionFlags&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 := getFunctionFlags(fn)
|
|
contextualReturnType := c.getContextualReturnType(fn, contextFlags)
|
|
if contextualReturnType != nil {
|
|
isAsyncGenerator := functionFlags&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.
|
|
kind := ast.GetAssignmentDeclarationKind(binary)
|
|
if node == binary.Right && kind != ast.JSDeclarationKindModuleExports && kind != ast.JSDeclarationKindExportsProperty {
|
|
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) {
|
|
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().AsJsxAttributes().Properties.Nodes) != 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.AsArrayLiteralExpression().Elements.Nodes {
|
|
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
|
|
}
|
|
panic("Unhandled case in getClassElementPropertyKeyType")
|
|
}
|
|
|
|
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.AsObjectLiteralExpression().Properties.Nodes, 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.AsObjectLiteralExpression().Properties.Nodes, 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.AsJsxExpression().Expression == nil || c.isPossiblyDiscriminantValue(node.AsJsxExpression().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.
|
|
return c.instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper)
|
|
}
|
|
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&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.AsObjectLiteralExpression().Properties.Nodes, c.isContextSensitive)
|
|
case ast.KindArrayLiteralExpression:
|
|
return core.Some(node.AsArrayLiteralExpression().Elements.Nodes, 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.AsJsxAttributes().Properties.Nodes, 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)
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *Checker) isContextSensitiveFunctionLikeDeclaration(node *ast.Node) bool {
|
|
return hasContextSensitiveParameters(node) || c.hasContextSensitiveReturnExpression(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) 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.diagnostics.Add(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.AsBindingPattern().Elements.Nodes, 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)) && getTagNameOfNode(node.Parent) == 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
|
|
if node.Parent == nil || node.Parent.Parent == nil {
|
|
return nil
|
|
}
|
|
return c.getSymbolAtLocation(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 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)
|
|
}
|
|
|
|
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)
|
|
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 || name.Parent.Kind == ast.KindJSExportAssignment) && 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, true /*dontResolveAlias*/)
|
|
}
|
|
|
|
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)
|
|
result := c.resolveEntityName(name, meaning, true /*ignoreErrors*/, true /*dontResolveAlias*/, nil /*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 {
|
|
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.AsPropertyAccessExpression().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 {
|
|
infos := c.getApplicableIndexInfos(t, keyType)
|
|
if len(infos) > 0 && t.flags&TypeFlagsObject != 0 && t.AsObjectType().members != nil {
|
|
symbol := getIndexSymbolFromSymbolTable(c.resolveStructuredTypeMembers(t).members)
|
|
if core.Same(infos, c.getIndexInfosOfType(t)) {
|
|
return symbol
|
|
} else if symbol != nil {
|
|
indexSymbolLinks := c.indexSymbolLinks.Get(symbol)
|
|
declarationList := core.MapNonNil(infos, func(info *IndexInfo) *ast.Node { return info.declaration })
|
|
nodeListId := getNodeListKey(declarationList)
|
|
if indexSymbolLinks.filteredIndexSymbolCache == nil {
|
|
indexSymbolLinks.filteredIndexSymbolCache = make(map[string]*ast.Symbol)
|
|
}
|
|
if result, ok := indexSymbolLinks.filteredIndexSymbolCache[nodeListId]; ok {
|
|
return result
|
|
} else {
|
|
symbolCopy := c.newSymbol(ast.SymbolFlagsSignature, ast.InternalSymbolNameIndex)
|
|
symbolCopy.Declarations = declarationList
|
|
if t.alias != nil && t.alias.symbol != nil {
|
|
symbolCopy.Parent = t.alias.symbol
|
|
} else if t.symbol != nil {
|
|
symbolCopy.Parent = t.symbol
|
|
} else {
|
|
symbolCopy.Parent = c.getSymbolAtLocation(symbolCopy.Declarations[0].Parent, false /*ignoreErrors*/)
|
|
}
|
|
indexSymbolLinks.filteredIndexSymbolCache[nodeListId] = symbolCopy
|
|
return symbolCopy
|
|
}
|
|
}
|
|
}
|
|
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.AsPropertyAssignment().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(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)
|
|
}
|