Skip to content
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

Default to 'any' for unsupplied generics in JavaScript #14832

Closed
wants to merge 1 commit into from
Closed
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
35 changes: 19 additions & 16 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3993,14 +3993,15 @@ namespace ts {
return getClassExtendsHeritageClauseElement(<ClassLikeDeclaration>type.symbol.valueDeclaration);
}

function getConstructorsForTypeArguments(type: Type, typeArgumentNodes: TypeNode[]): Signature[] {
function getConstructorsForTypeArguments(type: Type, typeArgumentNodes: TypeNode[], location: Node): Signature[] {
const typeArgCount = length(typeArgumentNodes);
const isJavaScript = isInJavaScriptFile(location);
return filter(getSignaturesOfType(type, SignatureKind.Construct),
sig => typeArgCount >= getMinTypeArgumentCount(sig.typeParameters) && typeArgCount <= length(sig.typeParameters));
sig => (isJavaScript || typeArgCount >= getMinTypeArgumentCount(sig.typeParameters)) && typeArgCount <= length(sig.typeParameters));
}

function getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: TypeNode[]): Signature[] {
let signatures = getConstructorsForTypeArguments(type, typeArgumentNodes);
function getInstantiatedConstructorsForTypeArguments(type: Type, typeArgumentNodes: TypeNode[], location: Node): Signature[] {
let signatures = getConstructorsForTypeArguments(type, typeArgumentNodes, location);
if (typeArgumentNodes) {
const typeArguments = map(typeArgumentNodes, getTypeFromTypeNode);
signatures = map(signatures, sig => getSignatureInstantiation(sig, typeArguments));
Expand Down Expand Up @@ -4083,7 +4084,7 @@ namespace ts {
// 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.
const constructors = getInstantiatedConstructorsForTypeArguments(baseConstructorType, baseTypeNode.typeArguments);
const constructors = getInstantiatedConstructorsForTypeArguments(baseConstructorType, baseTypeNode.typeArguments, baseTypeNode);
if (!constructors.length) {
error(baseTypeNode.expression, Diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments);
return;
Expand Down Expand Up @@ -4593,14 +4594,15 @@ namespace ts {
return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false)];
}
const baseTypeNode = getBaseTypeNodeOfClass(classType);
const isJavaScript = isInJavaScriptFile(baseTypeNode);
const typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNode);
const typeArgCount = length(typeArguments);
const result: Signature[] = [];
for (const baseSig of baseSignatures) {
const minTypeArgumentCount = getMinTypeArgumentCount(baseSig.typeParameters);
const typeParamCount = length(baseSig.typeParameters);
if (typeArgCount >= minTypeArgumentCount && typeArgCount <= typeParamCount) {
const sig = typeParamCount ? createSignatureInstantiation(baseSig, fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount)) : cloneSignature(baseSig);
if ((isJavaScript || typeArgCount >= minTypeArgumentCount) && typeArgCount <= typeParamCount) {
const sig = typeParamCount ? createSignatureInstantiation(baseSig, fillMissingTypeArguments(typeArguments, baseSig.typeParameters, minTypeArgumentCount, baseTypeNode)) : cloneSignature(baseSig);
sig.typeParameters = classType.localTypeParameters;
sig.resolvedReturnType = classType;
result.push(sig);
Expand Down Expand Up @@ -5433,11 +5435,12 @@ namespace ts {
* @param typeParameters The requested type parameters.
* @param minTypeArgumentCount The minimum number of required type arguments.
*/
function fillMissingTypeArguments(typeArguments: Type[] | undefined, typeParameters: TypeParameter[] | undefined, minTypeArgumentCount: number) {
function fillMissingTypeArguments(typeArguments: Type[] | undefined, typeParameters: TypeParameter[] | undefined, minTypeArgumentCount: number, location?: Node) {
const numTypeParameters = length(typeParameters);
if (numTypeParameters) {
const numTypeArguments = length(typeArguments);
if (numTypeArguments >= minTypeArgumentCount && numTypeArguments <= numTypeParameters) {
const isJavaScript = isInJavaScriptFile(location);
if ((isJavaScript || numTypeArguments >= minTypeArgumentCount) && numTypeArguments <= numTypeParameters) {
if (!typeArguments) {
typeArguments = [];
}
Expand All @@ -5446,12 +5449,12 @@ namespace ts {
// If a type parameter does not have a default type, or if the default type
// is a forward reference, the empty object type is used.
for (let i = numTypeArguments; i < numTypeParameters; i++) {
typeArguments[i] = emptyObjectType;
typeArguments[i] = isJavaScript ? anyType : emptyObjectType;
}
for (let i = numTypeArguments; i < numTypeParameters; i++) {
const mapper = createTypeMapper(typeParameters, typeArguments);
const defaultType = getDefaultFromTypeParameter(typeParameters[i]);
typeArguments[i] = defaultType ? instantiateType(defaultType, mapper) : emptyObjectType;
typeArguments[i] = defaultType ? instantiateType(defaultType, mapper) : isJavaScript ? anyType : emptyObjectType;
}
}
}
Expand Down Expand Up @@ -5825,7 +5828,7 @@ namespace ts {
if (typeParameters) {
const numTypeArguments = length(node.typeArguments);
const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters);
if (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length) {
if (!isInJavaScriptFile(node) && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) {
error(node,
minTypeArgumentCount === typeParameters.length
? Diagnostics.Generic_type_0_requires_1_type_argument_s
Expand All @@ -5838,7 +5841,7 @@ namespace ts {
// 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.
const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(map(node.typeArguments, getTypeFromTypeNode), typeParameters, minTypeArgumentCount));
const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(map(node.typeArguments, getTypeFromTypeNode), typeParameters, minTypeArgumentCount, node));
return createTypeReference(<GenericType>type, typeArguments);
}
if (node.typeArguments) {
Expand Down Expand Up @@ -14481,7 +14484,7 @@ namespace ts {
// with the type arguments specified in the extends clause.
const baseTypeNode = getClassExtendsHeritageClauseElement(getContainingClass(node));
if (baseTypeNode) {
const baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments);
const baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments, baseTypeNode);
return resolveCall(node, baseConstructors, candidatesOutArray);
}
}
Expand Down Expand Up @@ -19595,7 +19598,7 @@ namespace ts {
checkSourceElement(baseTypeNode.expression);
if (baseTypeNode.typeArguments) {
forEach(baseTypeNode.typeArguments, checkSourceElement);
for (const constructor of getConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments)) {
for (const constructor of getConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments, baseTypeNode)) {
if (!checkTypeArgumentConstraints(constructor.typeParameters, baseTypeNode.typeArguments)) {
break;
}
Expand All @@ -19613,7 +19616,7 @@ namespace ts {
// that all instantiated base constructor signatures return the same type. We can simply compare the type
// references (as opposed to checking the structure of the types) because elsewhere we have already checked
// that the base type is a class or interface type (and not, for example, an anonymous object type).
const constructors = getInstantiatedConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments);
const constructors = getInstantiatedConstructorsForTypeArguments(staticBaseType, baseTypeNode.typeArguments, baseTypeNode);
if (forEach(constructors, sig => getReturnTypeOfSignature(sig) !== baseType)) {
error(baseTypeNode.expression, Diagnostics.Base_constructors_must_all_have_the_same_return_type);
}
Expand Down
226 changes: 226 additions & 0 deletions tests/baselines/reference/genericDefaultsJs.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
=== tests/cases/compiler/decls.d.ts ===
declare function f0<T>(x?: T): T;
>f0 : Symbol(f0, Decl(decls.d.ts, 0, 0))
>T : Symbol(T, Decl(decls.d.ts, 0, 20))
>x : Symbol(x, Decl(decls.d.ts, 0, 23))
>T : Symbol(T, Decl(decls.d.ts, 0, 20))
>T : Symbol(T, Decl(decls.d.ts, 0, 20))

declare function f1<T, U = number>(x?: T): [T, U];
>f1 : Symbol(f1, Decl(decls.d.ts, 0, 33))
>T : Symbol(T, Decl(decls.d.ts, 1, 20))
>U : Symbol(U, Decl(decls.d.ts, 1, 22))
>x : Symbol(x, Decl(decls.d.ts, 1, 35))
>T : Symbol(T, Decl(decls.d.ts, 1, 20))
>T : Symbol(T, Decl(decls.d.ts, 1, 20))
>U : Symbol(U, Decl(decls.d.ts, 1, 22))

declare class C0<T> {
>C0 : Symbol(C0, Decl(decls.d.ts, 1, 50))
>T : Symbol(T, Decl(decls.d.ts, 2, 17))

y: T;
>y : Symbol(C0.y, Decl(decls.d.ts, 2, 21))
>T : Symbol(T, Decl(decls.d.ts, 2, 17))

constructor(x?: T);
>x : Symbol(x, Decl(decls.d.ts, 4, 16))
>T : Symbol(T, Decl(decls.d.ts, 2, 17))
}
declare class C1<T, U = number> {
>C1 : Symbol(C1, Decl(decls.d.ts, 5, 1))
>T : Symbol(T, Decl(decls.d.ts, 6, 17))
>U : Symbol(U, Decl(decls.d.ts, 6, 19))

y: [T, U];
>y : Symbol(C1.y, Decl(decls.d.ts, 6, 33))
>T : Symbol(T, Decl(decls.d.ts, 6, 17))
>U : Symbol(U, Decl(decls.d.ts, 6, 19))

constructor(x?: T);
>x : Symbol(x, Decl(decls.d.ts, 8, 16))
>T : Symbol(T, Decl(decls.d.ts, 6, 17))
}
=== tests/cases/compiler/main.js ===
const f0_v0 = f0();
>f0_v0 : Symbol(f0_v0, Decl(main.js, 0, 5))
>f0 : Symbol(f0, Decl(decls.d.ts, 0, 0))

const f0_v1 = f0(1);
>f0_v1 : Symbol(f0_v1, Decl(main.js, 1, 5))
>f0 : Symbol(f0, Decl(decls.d.ts, 0, 0))

const f1_c0 = f1();
>f1_c0 : Symbol(f1_c0, Decl(main.js, 3, 5))
>f1 : Symbol(f1, Decl(decls.d.ts, 0, 33))

const f1_c1 = f1(1);
>f1_c1 : Symbol(f1_c1, Decl(main.js, 4, 5))
>f1 : Symbol(f1, Decl(decls.d.ts, 0, 33))

const C0_v0 = new C0();
>C0_v0 : Symbol(C0_v0, Decl(main.js, 6, 5))
>C0 : Symbol(C0, Decl(decls.d.ts, 1, 50))

const C0_v0_y = C0_v0.y;
>C0_v0_y : Symbol(C0_v0_y, Decl(main.js, 7, 5))
>C0_v0.y : Symbol(C0.y, Decl(decls.d.ts, 2, 21))
>C0_v0 : Symbol(C0_v0, Decl(main.js, 6, 5))
>y : Symbol(C0.y, Decl(decls.d.ts, 2, 21))

const C0_v1 = new C0(1);
>C0_v1 : Symbol(C0_v1, Decl(main.js, 9, 5))
>C0 : Symbol(C0, Decl(decls.d.ts, 1, 50))

const C0_v1_y = C0_v1.y;
>C0_v1_y : Symbol(C0_v1_y, Decl(main.js, 10, 5))
>C0_v1.y : Symbol(C0.y, Decl(decls.d.ts, 2, 21))
>C0_v1 : Symbol(C0_v1, Decl(main.js, 9, 5))
>y : Symbol(C0.y, Decl(decls.d.ts, 2, 21))

const C1_v0 = new C1();
>C1_v0 : Symbol(C1_v0, Decl(main.js, 12, 5))
>C1 : Symbol(C1, Decl(decls.d.ts, 5, 1))

const C1_v0_y = C1_v0.y;
>C1_v0_y : Symbol(C1_v0_y, Decl(main.js, 13, 5))
>C1_v0.y : Symbol(C1.y, Decl(decls.d.ts, 6, 33))
>C1_v0 : Symbol(C1_v0, Decl(main.js, 12, 5))
>y : Symbol(C1.y, Decl(decls.d.ts, 6, 33))

const C1_v1 = new C1(1);
>C1_v1 : Symbol(C1_v1, Decl(main.js, 15, 5))
>C1 : Symbol(C1, Decl(decls.d.ts, 5, 1))

const C1_v1_y = C1_v1.y;
>C1_v1_y : Symbol(C1_v1_y, Decl(main.js, 16, 5))
>C1_v1.y : Symbol(C1.y, Decl(decls.d.ts, 6, 33))
>C1_v1 : Symbol(C1_v1, Decl(main.js, 15, 5))
>y : Symbol(C1.y, Decl(decls.d.ts, 6, 33))

class C0_B0 extends C0 {}
>C0_B0 : Symbol(C0_B0, Decl(main.js, 16, 24))
>C0 : Symbol(C0, Decl(decls.d.ts, 1, 50))

class C0_B1 extends C0 {
>C0_B1 : Symbol(C0_B1, Decl(main.js, 18, 25))
>C0 : Symbol(C0, Decl(decls.d.ts, 1, 50))

constructor() {
super();
>super : Symbol(C0, Decl(decls.d.ts, 1, 50))
}
}
class C0_B2 extends C0 {
>C0_B2 : Symbol(C0_B2, Decl(main.js, 23, 1))
>C0 : Symbol(C0, Decl(decls.d.ts, 1, 50))

constructor() {
super(1);
>super : Symbol(C0, Decl(decls.d.ts, 1, 50))
}
}

const C0_B0_v0 = new C0_B0();
>C0_B0_v0 : Symbol(C0_B0_v0, Decl(main.js, 30, 5))
>C0_B0 : Symbol(C0_B0, Decl(main.js, 16, 24))

const C0_B0_v0_y = C0_B0_v0.y;
>C0_B0_v0_y : Symbol(C0_B0_v0_y, Decl(main.js, 31, 5))
>C0_B0_v0.y : Symbol(C0.y, Decl(decls.d.ts, 2, 21))
>C0_B0_v0 : Symbol(C0_B0_v0, Decl(main.js, 30, 5))
>y : Symbol(C0.y, Decl(decls.d.ts, 2, 21))

const C0_B0_v1 = new C0_B0(1);
>C0_B0_v1 : Symbol(C0_B0_v1, Decl(main.js, 33, 5))
>C0_B0 : Symbol(C0_B0, Decl(main.js, 16, 24))

const C0_B0_v1_y = C0_B0_v1.y;
>C0_B0_v1_y : Symbol(C0_B0_v1_y, Decl(main.js, 34, 5))
>C0_B0_v1.y : Symbol(C0.y, Decl(decls.d.ts, 2, 21))
>C0_B0_v1 : Symbol(C0_B0_v1, Decl(main.js, 33, 5))
>y : Symbol(C0.y, Decl(decls.d.ts, 2, 21))

const C0_B1_v0 = new C0_B1();
>C0_B1_v0 : Symbol(C0_B1_v0, Decl(main.js, 36, 5))
>C0_B1 : Symbol(C0_B1, Decl(main.js, 18, 25))

const C0_B1_v0_y = C0_B1_v0.y;
>C0_B1_v0_y : Symbol(C0_B1_v0_y, Decl(main.js, 37, 5))
>C0_B1_v0.y : Symbol(C0.y, Decl(decls.d.ts, 2, 21))
>C0_B1_v0 : Symbol(C0_B1_v0, Decl(main.js, 36, 5))
>y : Symbol(C0.y, Decl(decls.d.ts, 2, 21))

const C0_B2_v0 = new C0_B2();
>C0_B2_v0 : Symbol(C0_B2_v0, Decl(main.js, 39, 5))
>C0_B2 : Symbol(C0_B2, Decl(main.js, 23, 1))

const C0_B2_v0_y = C0_B2_v0.y;
>C0_B2_v0_y : Symbol(C0_B2_v0_y, Decl(main.js, 40, 5))
>C0_B2_v0.y : Symbol(C0.y, Decl(decls.d.ts, 2, 21))
>C0_B2_v0 : Symbol(C0_B2_v0, Decl(main.js, 39, 5))
>y : Symbol(C0.y, Decl(decls.d.ts, 2, 21))

class C1_B0 extends C1 {}
>C1_B0 : Symbol(C1_B0, Decl(main.js, 40, 30))
>C1 : Symbol(C1, Decl(decls.d.ts, 5, 1))

class C1_B1 extends C1 {
>C1_B1 : Symbol(C1_B1, Decl(main.js, 42, 25))
>C1 : Symbol(C1, Decl(decls.d.ts, 5, 1))

constructor() {
super();
>super : Symbol(C1, Decl(decls.d.ts, 5, 1))
}
}
class C1_B2 extends C1 {
>C1_B2 : Symbol(C1_B2, Decl(main.js, 47, 1))
>C1 : Symbol(C1, Decl(decls.d.ts, 5, 1))

constructor() {
super(1);
>super : Symbol(C1, Decl(decls.d.ts, 5, 1))
}
}

const C1_B0_v0 = new C1_B0();
>C1_B0_v0 : Symbol(C1_B0_v0, Decl(main.js, 54, 5))
>C1_B0 : Symbol(C1_B0, Decl(main.js, 40, 30))

const C1_B0_v0_y = C1_B0_v0.y;
>C1_B0_v0_y : Symbol(C1_B0_v0_y, Decl(main.js, 55, 5))
>C1_B0_v0.y : Symbol(C1.y, Decl(decls.d.ts, 6, 33))
>C1_B0_v0 : Symbol(C1_B0_v0, Decl(main.js, 54, 5))
>y : Symbol(C1.y, Decl(decls.d.ts, 6, 33))

const C1_B0_v1 = new C1_B0(1);
>C1_B0_v1 : Symbol(C1_B0_v1, Decl(main.js, 57, 5))
>C1_B0 : Symbol(C1_B0, Decl(main.js, 40, 30))

const C1_B0_v1_y = C1_B0_v1.y;
>C1_B0_v1_y : Symbol(C1_B0_v1_y, Decl(main.js, 58, 5))
>C1_B0_v1.y : Symbol(C1.y, Decl(decls.d.ts, 6, 33))
>C1_B0_v1 : Symbol(C1_B0_v1, Decl(main.js, 57, 5))
>y : Symbol(C1.y, Decl(decls.d.ts, 6, 33))

const C1_B1_v0 = new C1_B1();
>C1_B1_v0 : Symbol(C1_B1_v0, Decl(main.js, 60, 5))
>C1_B1 : Symbol(C1_B1, Decl(main.js, 42, 25))

const C1_B1_v0_y = C1_B1_v0.y;
>C1_B1_v0_y : Symbol(C1_B1_v0_y, Decl(main.js, 61, 5))
>C1_B1_v0.y : Symbol(C1.y, Decl(decls.d.ts, 6, 33))
>C1_B1_v0 : Symbol(C1_B1_v0, Decl(main.js, 60, 5))
>y : Symbol(C1.y, Decl(decls.d.ts, 6, 33))

const C1_B2_v0 = new C1_B2();
>C1_B2_v0 : Symbol(C1_B2_v0, Decl(main.js, 63, 5))
>C1_B2 : Symbol(C1_B2, Decl(main.js, 47, 1))

const C1_B2_v0_y = C1_B2_v0.y;
>C1_B2_v0_y : Symbol(C1_B2_v0_y, Decl(main.js, 64, 5))
>C1_B2_v0.y : Symbol(C1.y, Decl(decls.d.ts, 6, 33))
>C1_B2_v0 : Symbol(C1_B2_v0, Decl(main.js, 63, 5))
>y : Symbol(C1.y, Decl(decls.d.ts, 6, 33))

Loading