Skip to content

Port implicit any type arguments in JavaScript #1242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -9624,6 +9624,10 @@ func (node *JSDocAugmentsTag) Clone(f NodeFactoryCoercible) *Node {
return cloneNode(f.AsNodeFactory().NewJSDocAugmentsTag(node.TagName, node.ClassName, node.Comment), node.AsNode(), f.AsNodeFactory().hooks)
}

func IsJSDocAugmentsTag(node *Node) bool {
return node.Kind == KindJSDocAugmentsTag
}

// JSDocSatisfiesTag
type JSDocSatisfiesTag struct {
JSDocTagBase
Expand Down
88 changes: 62 additions & 26 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -7466,12 +7466,14 @@ 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
Expand All @@ -7492,6 +7494,7 @@ func (c *Checker) checkSuperExpression(node *ast.Node) *Type {
}
return false
}

if container == nil || !isLegalUsageOfSuperExpression() {
// issue more specific error if super is used in computed property name
// class A { foo() { return "1" }}
Expand Down Expand Up @@ -8740,7 +8743,7 @@ func (c *Checker) chooseOverload(s *CallState, relation *Relation) *Signature {
if inferenceContext != nil {
inferredTypeParameters = inferenceContext.inferredTypeParameters
}
checkCandidate = c.getSignatureInstantiation(candidate, typeArgumentTypes, 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) {
Expand All @@ -8762,7 +8765,7 @@ func (c *Checker) chooseOverload(s *CallState, relation *Relation) *Signature {
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, inferenceContext.inferredTypeParameters)
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) {
Expand Down Expand Up @@ -8908,8 +8911,9 @@ func (c *Checker) hasCorrectTypeArgumentArity(signature *Signature, typeArgument
}

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))
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")
Expand Down Expand Up @@ -10164,7 +10168,7 @@ func (c *Checker) getInstantiationExpressionType(exprType *Type, node *ast.Node)
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, nil)
return c.getSignatureInstantiation(sig, typeArgumentTypes, ast.IsInJSFile(sig.declaration), nil)
}
return sig
})
Expand Down Expand Up @@ -18346,21 +18350,22 @@ func (c *Checker) getInstantiatedConstructorsForTypeArguments(t *Type, typeArgum
typeArguments := core.Map(typeArgumentNodes, c.getTypeFromTypeNode)
return core.SameMap(signatures, func(sig *Signature) *Signature {
if len(sig.typeParameters) != 0 {
return c.getSignatureInstantiation(sig, typeArguments, nil)
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)
isJavaScript := ast.IsInJSFile(location)
return core.Filter(c.getSignaturesOfType(t, SignatureKindConstruct), func(sig *Signature) bool {
return typeArgCount >= c.getMinTypeArgumentCount(sig.typeParameters) && typeArgCount <= len(sig.typeParameters)
return isJavaScript || typeArgCount >= c.getMinTypeArgumentCount(sig.typeParameters) && typeArgCount <= len(sig.typeParameters)
})
}

func (c *Checker) getSignatureInstantiation(sig *Signature, typeArguments []*Type, inferredTypeParameters []*Type) *Signature {
instantiatedSignature := c.getSignatureInstantiationWithoutFillingInTypeArguments(sig, c.fillMissingTypeArguments(typeArguments, sig.typeParameters, c.getMinTypeArgumentCount(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 {
Expand Down Expand Up @@ -18498,12 +18503,13 @@ func (c *Checker) createCanonicalSignature(signature *Signature) *Signature {
// 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.
return c.getSignatureInstantiation(signature, core.Map(signature.typeParameters, func(tp *Type) *Type {
typeArguments := core.Map(signature.typeParameters, func(tp *Type) *Type {
if tp.Target() != nil && c.getConstraintOfTypeParameter(tp.Target()) == nil {
return tp.Target()
}
return tp
}), nil)
})
return c.getSignatureInstantiation(signature, typeArguments, ast.IsInJSFile(signature.declaration), nil /*inferredTypeParameters*/)
}

func (c *Checker) getBaseSignature(signature *Signature) *Signature {
Expand Down Expand Up @@ -18563,7 +18569,7 @@ func (c *Checker) instantiateSignatureInContextOf(signature *Signature, contextu
c.inferTypes(context.inferences, source, target, InferencePriorityReturnType, false)
})
}
return c.getSignatureInstantiation(signature, c.getInferredTypes(context), nil)
return c.getSignatureInstantiation(signature, c.getInferredTypes(context), ast.IsInJSFile(contextualSignature.declaration), nil /*inferredTypeParameters*/)
}

func (c *Checker) resolveBaseTypesOfInterface(t *Type) {
Expand Down Expand Up @@ -19884,16 +19890,17 @@ func (c *Checker) getDefaultConstructSignatures(classType *Type) []*Signature {
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 typeArgCount >= minTypeArgumentCount && typeArgCount <= typeParamCount {
if isJavaScript || typeArgCount >= minTypeArgumentCount && typeArgCount <= typeParamCount {
var sig *Signature
if typeParamCount != 0 {
sig = c.createSignatureInstantiation(baseSig, c.fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount))
sig = c.createSignatureInstantiation(baseSig, c.fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, isJavaScript))
} else {
sig = c.cloneSignature(baseSig)
}
Expand Down Expand Up @@ -20987,7 +20994,7 @@ func (c *Checker) getTypeArguments(t *Type) []*Type {
}

func (c *Checker) getEffectiveTypeArguments(node *ast.Node, typeParameters []*Type) []*Type {
return c.fillMissingTypeArguments(core.Map(node.TypeArguments(), c.getTypeFromTypeNode), typeParameters, c.getMinTypeArgumentCount(typeParameters))
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.
Expand All @@ -21007,32 +21014,45 @@ func (c *Checker) hasTypeParameterDefault(t *Type) bool {
})
}

func (c *Checker) fillMissingTypeArguments(typeArguments []*Type, typeParameters []*Type, minTypeArgumentCount int) []*Type {
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 numTypeArguments >= minTypeArgumentCount && numTypeArguments < numTypeParameters {
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] = c.unknownType
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.
Expand Down Expand Up @@ -22072,6 +22092,8 @@ func (c *Checker) getTypeReferenceType(node *ast.Node, symbol *ast.Symbol) *Type
if res != nil && c.checkNoTypeArguments(node, symbol) {
return c.getRegularTypeOfLiteralType(res)
}

// !!! Resolving values as types for JS
return c.errorType
}

Expand All @@ -22085,23 +22107,37 @@ func (c *Checker) getTypeFromClassOrInterfaceReference(node *ast.Node, symbol *a
if len(typeParameters) != 0 {
numTypeArguments := len(node.TypeArguments())
minTypeArgumentCount := c.getMinTypeArgumentCount(typeParameters)
if numTypeArguments < minTypeArgumentCount || numTypeArguments > len(typeParameters) {
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
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.TypeToString(t) // !!! /*enclosingDeclaration*/, nil, TypeFormatFlagsWriteArrayAsGenericType
typeStr := c.TypeToStringEx(t, nil /*enclosingDeclaration*/, TypeFormatFlagsWriteArrayAsGenericType)
c.error(node, message, typeStr, minTypeArgumentCount, len(typeParameters))
// TODO: Adopt same permissive behavior in TS as in JS to reduce follow-on editing experience failures (requires editing fillMissingTypeArguments)
return c.errorType
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)
localTypeArguments := c.fillMissingTypeArguments(c.getTypeArgumentsFromNode(node), typeParameters, minTypeArgumentCount, isJs)
typeArguments := append(d.OuterTypeParameters(), localTypeArguments...)
return c.createTypeReference(t, typeArguments)
}
Expand Down Expand Up @@ -22542,7 +22578,7 @@ func (c *Checker) getTypeAliasInstantiation(symbol *ast.Symbol, typeArguments []
key := getTypeAliasInstantiationKey(typeArguments, alias)
instantiation := links.instantiations[key]
if instantiation == nil {
mapper := newTypeMapper(typeParameters, c.fillMissingTypeArguments(typeArguments, typeParameters, c.getMinTypeArgumentCount(typeParameters)))
mapper := newTypeMapper(typeParameters, c.fillMissingTypeArguments(typeArguments, typeParameters, c.getMinTypeArgumentCount(typeParameters), ast.IsInJSFile(symbol.ValueDeclaration)))
instantiation = c.instantiateTypeWithAlias(t, mapper, alias)
links.instantiations[key] = instantiation
}
Expand Down
5 changes: 3 additions & 2 deletions internal/checker/inference.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ func (c *Checker) inferFromTypes(n *InferenceState, source *Type, target *Type)
// Simply infer from source type arguments to target type arguments, with defaults applied.
params := c.typeAliasLinks.Get(source.alias.symbol).typeParameters
minParams := c.getMinTypeArgumentCount(params)
sourceTypes := c.fillMissingTypeArguments(source.alias.typeArguments, params, minParams)
targetTypes := c.fillMissingTypeArguments(target.alias.typeArguments, params, minParams)
nodeIsInJsFile := ast.IsInJSFile(source.alias.symbol.ValueDeclaration)
sourceTypes := c.fillMissingTypeArguments(source.alias.typeArguments, params, minParams, nodeIsInJsFile)
targetTypes := c.fillMissingTypeArguments(target.alias.typeArguments, params, minParams, nodeIsInJsFile)
c.inferFromTypeArguments(n, sourceTypes, targetTypes, c.getAliasVariances(source.alias.symbol))
}
// And if there weren't any type arguments, there's no reason to run inference as the types must be the same.
Expand Down
16 changes: 8 additions & 8 deletions internal/checker/jsx.go
Original file line number Diff line number Diff line change
Expand Up @@ -952,16 +952,16 @@ func (c *Checker) getJsxPropsTypeFromClassType(sig *Signature, context *ast.Node
// Props is of type 'any' or unknown
return attributesType
}
// Normal case -- add in IntrinsicClassElements<T> and IntrinsicElements
// Normal case -- add in IntrinsicClassAttributes<T> and IntrinsicAttributes
apparentAttributesType := attributesType
intrinsicClassAttribs := c.getJsxType(JsxNames.IntrinsicClassAttributes, context)
if !c.isErrorType(intrinsicClassAttribs) {
typeParams := c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(intrinsicClassAttribs.symbol)
hostClassType := c.getReturnTypeOfSignature(sig)
var libraryManagedAttributeType *Type
if typeParams != nil {
// apply JSX.IntrinsicClassElements<hostClassType, ...>
inferredArgs := c.fillMissingTypeArguments([]*Type{hostClassType}, typeParams, c.getMinTypeArgumentCount(typeParams))
// apply JSX.IntrinsicClassAttributes<hostClassType, ...>
inferredArgs := c.fillMissingTypeArguments([]*Type{hostClassType}, typeParams, c.getMinTypeArgumentCount(typeParams), ast.IsInJSFile(context))
libraryManagedAttributeType = c.instantiateType(intrinsicClassAttribs, newTypeMapper(typeParams, inferredArgs))
} else {
libraryManagedAttributeType = intrinsicClassAttribs
Expand Down Expand Up @@ -1008,29 +1008,29 @@ func (c *Checker) getJsxManagedAttributesFromLocatedAttributes(context *ast.Node
managedSym := c.getJsxLibraryManagedAttributes(ns)
if managedSym != nil {
ctorType := c.getStaticTypeOfReferencedJsxConstructor(context)
result := c.instantiateAliasOrInterfaceWithDefaults(managedSym, []*Type{ctorType, attributesType})
result := c.instantiateAliasOrInterfaceWithDefaults(managedSym, []*Type{ctorType, attributesType}, ast.IsInJSFile(context))
if result != nil {
return result
}
}
return attributesType
}

func (c *Checker) instantiateAliasOrInterfaceWithDefaults(managedSym *ast.Symbol, typeArguments []*Type) *Type {
func (c *Checker) instantiateAliasOrInterfaceWithDefaults(managedSym *ast.Symbol, typeArguments []*Type, inJavaScript bool) *Type {
declaredManagedType := c.getDeclaredTypeOfSymbol(managedSym)
// fetches interface type, or initializes symbol links type parmaeters
if managedSym.Flags&ast.SymbolFlagsTypeAlias != 0 {
params := c.typeAliasLinks.Get(managedSym).typeParameters
if len(params) >= len(typeArguments) {
args := c.fillMissingTypeArguments(typeArguments, params, len(typeArguments))
args := c.fillMissingTypeArguments(typeArguments, params, len(typeArguments), inJavaScript)
if len(args) == 0 {
return declaredManagedType
}
return c.getTypeAliasInstantiation(managedSym, args, nil)
}
}
if len(declaredManagedType.AsInterfaceType().TypeParameters()) >= len(typeArguments) {
args := c.fillMissingTypeArguments(typeArguments, declaredManagedType.AsInterfaceType().TypeParameters(), len(typeArguments))
args := c.fillMissingTypeArguments(typeArguments, declaredManagedType.AsInterfaceType().TypeParameters(), len(typeArguments), inJavaScript)
return c.createTypeReference(declaredManagedType, args)
}
return nil
Expand Down Expand Up @@ -1272,7 +1272,7 @@ func (c *Checker) getJsxElementTypeTypeAt(location *ast.Node) *Type {
if sym == nil {
return nil
}
t := c.instantiateAliasOrInterfaceWithDefaults(sym, nil)
t := c.instantiateAliasOrInterfaceWithDefaults(sym, nil, ast.IsInJSFile(location))
if t == nil || c.isErrorType(t) {
return nil
}
Expand Down
Loading