Skip to content

Commit cc678a5

Browse files
authored
Merge pull request #18440 from Microsoft/fix-javascript-signature-instantiation
Fix javascript signature instantiation
2 parents 951974d + a1d1a22 commit cc678a5

File tree

4 files changed

+99
-19
lines changed

4 files changed

+99
-19
lines changed

src/compiler/checker.ts

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4916,7 +4916,7 @@ namespace ts {
49164916
function getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: ReadonlyArray<TypeNode>, location: Node): Signature[] {
49174917
const signatures = getConstructorsForTypeArguments(type, typeArgumentNodes, location);
49184918
const typeArguments = map(typeArgumentNodes, getTypeFromTypeNode);
4919-
return sameMap(signatures, sig => some(sig.typeParameters) ? getSignatureInstantiation(sig, typeArguments) : sig);
4919+
return sameMap(signatures, sig => some(sig.typeParameters) ? getSignatureInstantiation(sig, typeArguments, isInJavaScriptFile(location)) : sig);
49204920
}
49214921

49224922
/**
@@ -5512,7 +5512,7 @@ namespace ts {
55125512
const minTypeArgumentCount = getMinTypeArgumentCount(baseSig.typeParameters);
55135513
const typeParamCount = length(baseSig.typeParameters);
55145514
if ((isJavaScript || typeArgCount >= minTypeArgumentCount) && typeArgCount <= typeParamCount) {
5515-
const sig = typeParamCount ? createSignatureInstantiation(baseSig, fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, baseTypeNode)) : cloneSignature(baseSig);
5515+
const sig = typeParamCount ? createSignatureInstantiation(baseSig, fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, isJavaScript)) : cloneSignature(baseSig);
55165516
sig.typeParameters = classType.localTypeParameters;
55175517
sig.resolvedReturnType = classType;
55185518
result.push(sig);
@@ -6371,11 +6371,10 @@ namespace ts {
63716371
* @param typeParameters The requested type parameters.
63726372
* @param minTypeArgumentCount The minimum number of required type arguments.
63736373
*/
6374-
function fillMissingTypeArguments(typeArguments: Type[] | undefined, typeParameters: TypeParameter[] | undefined, minTypeArgumentCount: number, location?: Node) {
6374+
function fillMissingTypeArguments(typeArguments: Type[] | undefined, typeParameters: TypeParameter[] | undefined, minTypeArgumentCount: number, isJavaScript: boolean) {
63756375
const numTypeParameters = length(typeParameters);
63766376
if (numTypeParameters) {
63776377
const numTypeArguments = length(typeArguments);
6378-
const isJavaScript = isInJavaScriptFile(location);
63796378
if ((isJavaScript || numTypeArguments >= minTypeArgumentCount) && numTypeArguments <= numTypeParameters) {
63806379
if (!typeArguments) {
63816380
typeArguments = [];
@@ -6633,8 +6632,8 @@ namespace ts {
66336632
return anyType;
66346633
}
66356634

6636-
function getSignatureInstantiation(signature: Signature, typeArguments: Type[]): Signature {
6637-
typeArguments = fillMissingTypeArguments(typeArguments, signature.typeParameters, getMinTypeArgumentCount(signature.typeParameters));
6635+
function getSignatureInstantiation(signature: Signature, typeArguments: Type[], isJavascript: boolean): Signature {
6636+
typeArguments = fillMissingTypeArguments(typeArguments, signature.typeParameters, getMinTypeArgumentCount(signature.typeParameters), isJavascript);
66386637
const instantiations = signature.instantiations || (signature.instantiations = createMap<Signature>());
66396638
const id = getTypeListId(typeArguments);
66406639
let instantiation = instantiations.get(id);
@@ -6672,7 +6671,10 @@ namespace ts {
66726671
// where different generations of the same type parameter are in scope). This leads to a lot of new type
66736672
// identities, and potentially a lot of work comparing those identities, so here we create an instantiation
66746673
// that uses the original type identities for all unconstrained type parameters.
6675-
return getSignatureInstantiation(signature, map(signature.typeParameters, tp => tp.target && !getConstraintOfTypeParameter(tp.target) ? tp.target : tp));
6674+
return getSignatureInstantiation(
6675+
signature,
6676+
map(signature.typeParameters, tp => tp.target && !getConstraintOfTypeParameter(tp.target) ? tp.target : tp),
6677+
isInJavaScriptFile(signature.declaration));
66766678
}
66776679

66786680
function getOrCreateTypeFromSignature(signature: Signature): ObjectType {
@@ -6823,7 +6825,8 @@ namespace ts {
68236825
if (typeParameters) {
68246826
const numTypeArguments = length(node.typeArguments);
68256827
const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters);
6826-
if (!isInJavaScriptFile(node) && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) {
6828+
const isJavascript = isInJavaScriptFile(node);
6829+
if (!isJavascript && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) {
68276830
error(node,
68286831
minTypeArgumentCount === typeParameters.length
68296832
? Diagnostics.Generic_type_0_requires_1_type_argument_s
@@ -6836,7 +6839,7 @@ namespace ts {
68366839
// In a type reference, the outer type parameters of the referenced class or interface are automatically
68376840
// supplied as type arguments and the type reference only specifies arguments for the local type parameters
68386841
// of the class or interface.
6839-
const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, node));
6842+
const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgs, typeParameters, minTypeArgumentCount, isJavascript));
68406843
return createTypeReference(<GenericType>type, typeArguments);
68416844
}
68426845
if (node.typeArguments) {
@@ -6853,7 +6856,7 @@ namespace ts {
68536856
const id = getTypeListId(typeArguments);
68546857
let instantiation = links.instantiations.get(id);
68556858
if (!instantiation) {
6856-
links.instantiations.set(id, instantiation = instantiateType(type, createTypeMapper(typeParameters, fillMissingTypeArguments(typeArguments, typeParameters, getMinTypeArgumentCount(typeParameters)))));
6859+
links.instantiations.set(id, instantiation = instantiateType(type, createTypeMapper(typeParameters, fillMissingTypeArguments(typeArguments, typeParameters, getMinTypeArgumentCount(typeParameters), isInJavaScriptFile(symbol.valueDeclaration)))));
68576860
}
68586861
return instantiation;
68596862
}
@@ -14054,8 +14057,9 @@ namespace ts {
1405414057
const instantiatedSignatures = [];
1405514058
for (const signature of signatures) {
1405614059
if (signature.typeParameters) {
14057-
const typeArguments = fillMissingTypeArguments(/*typeArguments*/ undefined, signature.typeParameters, /*minTypeArgumentCount*/ 0);
14058-
instantiatedSignatures.push(getSignatureInstantiation(signature, typeArguments));
14060+
const isJavascript = isInJavaScriptFile(node);
14061+
const typeArguments = fillMissingTypeArguments(/*typeArguments*/ undefined, signature.typeParameters, /*minTypeArgumentCount*/ 0, isJavascript);
14062+
instantiatedSignatures.push(getSignatureInstantiation(signature, typeArguments, isJavascript));
1405914063
}
1406014064
else {
1406114065
instantiatedSignatures.push(signature);
@@ -15325,7 +15329,7 @@ namespace ts {
1532515329
if (!contextualMapper) {
1532615330
inferTypes(context.inferences, getReturnTypeOfSignature(contextualSignature), getReturnTypeOfSignature(signature), InferencePriority.ReturnType);
1532715331
}
15328-
return getSignatureInstantiation(signature, getInferredTypes(context));
15332+
return getSignatureInstantiation(signature, getInferredTypes(context), isInJavaScriptFile(contextualSignature.declaration));
1532915333
}
1533015334

1533115335
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray<Expression>, excludeArgument: boolean[], context: InferenceContext): Type[] {
@@ -15360,7 +15364,7 @@ namespace ts {
1536015364
// Above, the type of the 'value' parameter is inferred to be 'A'.
1536115365
const contextualSignature = getSingleCallSignature(instantiatedType);
1536215366
const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ?
15363-
getOrCreateTypeFromSignature(getSignatureInstantiation(contextualSignature, contextualSignature.typeParameters)) :
15367+
getOrCreateTypeFromSignature(getSignatureInstantiation(contextualSignature, contextualSignature.typeParameters, isInJavaScriptFile(node))) :
1536415368
instantiatedType;
1536515369
const inferenceTargetType = getReturnTypeOfSignature(signature);
1536615370
// Inferences made from return types have lower priority than all other inferences.
@@ -16076,8 +16080,9 @@ namespace ts {
1607616080
candidate = originalCandidate;
1607716081
if (candidate.typeParameters) {
1607816082
let typeArgumentTypes: Type[];
16083+
const isJavascript = isInJavaScriptFile(candidate.declaration);
1607916084
if (typeArguments) {
16080-
typeArgumentTypes = fillMissingTypeArguments(map(typeArguments, getTypeFromTypeNode), candidate.typeParameters, getMinTypeArgumentCount(candidate.typeParameters));
16085+
typeArgumentTypes = fillMissingTypeArguments(map(typeArguments, getTypeFromTypeNode), candidate.typeParameters, getMinTypeArgumentCount(candidate.typeParameters), isJavascript);
1608116086
if (!checkTypeArguments(candidate, typeArguments, typeArgumentTypes, /*reportErrors*/ false)) {
1608216087
candidateForTypeArgumentError = originalCandidate;
1608316088
break;
@@ -16086,7 +16091,7 @@ namespace ts {
1608616091
else {
1608716092
typeArgumentTypes = inferTypeArguments(node, candidate, args, excludeArgument, inferenceContext);
1608816093
}
16089-
candidate = getSignatureInstantiation(candidate, typeArgumentTypes);
16094+
candidate = getSignatureInstantiation(candidate, typeArgumentTypes, isJavascript);
1609016095
}
1609116096
if (!checkApplicableSignature(node, args, candidate, relation, excludeArgument, /*reportErrors*/ false)) {
1609216097
candidateForArgumentError = candidate;
@@ -18850,7 +18855,7 @@ namespace ts {
1885018855
const constraint = getConstraintOfTypeParameter(typeParameters[i]);
1885118856
if (constraint) {
1885218857
if (!typeArguments) {
18853-
typeArguments = fillMissingTypeArguments(map(typeArgumentNodes, getTypeFromTypeNode), typeParameters, minTypeArgumentCount);
18858+
typeArguments = fillMissingTypeArguments(map(typeArgumentNodes, getTypeFromTypeNode), typeParameters, minTypeArgumentCount, isInJavaScriptFile(typeArgumentNodes[i]));
1885418859
mapper = createTypeMapper(typeParameters, typeArguments);
1885518860
}
1885618861
const typeArgument = typeArguments[i];

src/compiler/utilities.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,11 +1326,11 @@ namespace ts {
13261326
return isInJavaScriptFile(file);
13271327
}
13281328

1329-
export function isInJavaScriptFile(node: Node): boolean {
1329+
export function isInJavaScriptFile(node: Node | undefined): boolean {
13301330
return node && !!(node.flags & NodeFlags.JavaScriptFile);
13311331
}
13321332

1333-
export function isInJSDoc(node: Node): boolean {
1333+
export function isInJSDoc(node: Node | undefined): boolean {
13341334
return node && !!(node.flags & NodeFlags.JSDoc);
13351335
}
13361336

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
tests/cases/compiler/BaseB.js(2,24): error TS8004: 'type parameter declarations' can only be used in a .ts file.
2+
tests/cases/compiler/BaseB.js(2,25): error TS1005: ',' expected.
3+
tests/cases/compiler/BaseB.js(3,14): error TS2304: Cannot find name 'Class'.
4+
tests/cases/compiler/BaseB.js(3,14): error TS8010: 'types' can only be used in a .ts file.
5+
tests/cases/compiler/BaseB.js(4,25): error TS2304: Cannot find name 'Class'.
6+
tests/cases/compiler/BaseB.js(4,25): error TS8010: 'types' can only be used in a .ts file.
7+
tests/cases/compiler/SubB.js(3,41): error TS8011: 'type arguments' can only be used in a .ts file.
8+
9+
10+
==== tests/cases/compiler/BaseA.js (0 errors) ====
11+
// regression test for #18254
12+
export default class BaseA {
13+
}
14+
==== tests/cases/compiler/SubA.js (0 errors) ====
15+
import BaseA from './BaseA';
16+
export default class SubA extends BaseA {
17+
}
18+
==== tests/cases/compiler/BaseB.js (6 errors) ====
19+
import BaseA from './BaseA';
20+
export default class B<T: BaseA> {
21+
~~~~~~~~
22+
!!! error TS8004: 'type parameter declarations' can only be used in a .ts file.
23+
~
24+
!!! error TS1005: ',' expected.
25+
_AClass: Class<T>;
26+
~~~~~
27+
!!! error TS2304: Cannot find name 'Class'.
28+
~~~~~~~~
29+
!!! error TS8010: 'types' can only be used in a .ts file.
30+
constructor(AClass: Class<T>) {
31+
~~~~~
32+
!!! error TS2304: Cannot find name 'Class'.
33+
~~~~~~~~
34+
!!! error TS8010: 'types' can only be used in a .ts file.
35+
this._AClass = AClass;
36+
}
37+
}
38+
==== tests/cases/compiler/SubB.js (1 errors) ====
39+
import SubA from './SubA';
40+
import BaseB from './BaseB';
41+
export default class SubB extends BaseB<SubA> {
42+
~~~~
43+
!!! error TS8011: 'type arguments' can only be used in a .ts file.
44+
constructor() {
45+
super(SubA);
46+
}
47+
}
48+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// @allowJs: true
2+
// @checkJs: true
3+
// @noEmit: true
4+
// regression test for #18254
5+
// @Filename: BaseA.js
6+
export default class BaseA {
7+
}
8+
// @Filename: SubA.js
9+
import BaseA from './BaseA';
10+
export default class SubA extends BaseA {
11+
}
12+
// @Filename: BaseB.js
13+
import BaseA from './BaseA';
14+
export default class B<T: BaseA> {
15+
_AClass: Class<T>;
16+
constructor(AClass: Class<T>) {
17+
this._AClass = AClass;
18+
}
19+
}
20+
// @Filename: SubB.js
21+
import SubA from './SubA';
22+
import BaseB from './BaseB';
23+
export default class SubB extends BaseB<SubA> {
24+
constructor() {
25+
super(SubA);
26+
}
27+
}

0 commit comments

Comments
 (0)