Fix recursive reference in type parameter default

This commit is contained in:
Ron Buckton 2017-10-09 15:40:52 -07:00
parent 1db762356e
commit aaa06122b9
11 changed files with 98 additions and 22 deletions

View file

@ -281,6 +281,7 @@ namespace ts {
const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const circularConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const resolvingDefaultType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const markerSuperType = <TypeParameter>createType(TypeFlags.TypeParameter);
const markerSubType = <TypeParameter>createType(TypeFlags.TypeParameter);
@ -6055,27 +6056,51 @@ namespace ts {
return type.resolvedApparentType || (type.resolvedApparentType = getTypeWithThisArgument(type, type));
}
function getResolvedTypeParameterDefault(typeParameter: TypeParameter): Type | undefined {
if (!typeParameter.default) {
if (typeParameter.target) {
const targetDefault = getResolvedTypeParameterDefault(typeParameter.target);
typeParameter.default = targetDefault ? instantiateType(targetDefault, typeParameter.mapper) : noConstraintType;
}
else {
// To block recursion, set the initial value to the resolvingDefaultType.
typeParameter.default = resolvingDefaultType;
const defaultDeclaration = typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameterDeclaration(decl) && decl.default);
const defaultType = defaultDeclaration ? getTypeFromTypeNode(defaultDeclaration) : noConstraintType;
if (typeParameter.default === resolvingDefaultType) {
// If we have not been called recursively, set the correct default type.
typeParameter.default = defaultType;
}
}
}
else if (typeParameter.default === resolvingDefaultType) {
// If we are called recursively for this type parameter, mark the default as circular.
typeParameter.default = circularConstraintType;
}
return typeParameter.default;
}
/**
* 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, `undefined`
* is returned.
*
* This function *does not* perform a circularity check.
* default type of its target. If the type parameter has no default type or the default is
* circular, `undefined` is returned.
*/
function getDefaultFromTypeParameter(typeParameter: TypeParameter): Type | undefined {
if (!typeParameter.default) {
if (typeParameter.target) {
const targetDefault = getDefaultFromTypeParameter(typeParameter.target);
typeParameter.default = targetDefault ? instantiateType(targetDefault, typeParameter.mapper) : noConstraintType;
}
else {
const defaultDeclaration = typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameterDeclaration(decl) && decl.default);
typeParameter.default = defaultDeclaration ? getTypeFromTypeNode(defaultDeclaration) : noConstraintType;
}
}
return typeParameter.default === noConstraintType ? undefined : typeParameter.default;
const defaultType = getResolvedTypeParameterDefault(typeParameter);
return defaultType !== noConstraintType && defaultType !== circularConstraintType ? defaultType : undefined;
}
function hasNonCircularTypeParameterDefault(typeParameter: TypeParameter) {
return getResolvedTypeParameterDefault(typeParameter) !== circularConstraintType;
}
/**
* Indicates whether the declaration of a typeParameter has a default type.
*/
function hasTypeParameterDefault(typeParameter: TypeParameter): boolean {
return !!(typeParameter.symbol && forEach(typeParameter.symbol.declarations, decl => isTypeParameterDeclaration(decl) && decl.default));
}
/**
@ -6361,7 +6386,7 @@ namespace ts {
let minTypeArgumentCount = 0;
if (typeParameters) {
for (let i = 0; i < typeParameters.length; i++) {
if (!getDefaultFromTypeParameter(typeParameters[i])) {
if (!hasTypeParameterDefault(typeParameters[i])) {
minTypeArgumentCount = i + 1;
}
}
@ -18478,6 +18503,9 @@ namespace ts {
if (!hasNonCircularBaseConstraint(typeParameter)) {
error(node.constraint, Diagnostics.Type_parameter_0_has_a_circular_constraint, typeToString(typeParameter));
}
if (!hasNonCircularTypeParameterDefault(typeParameter)) {
error(node.default, Diagnostics.Type_parameter_0_has_a_circular_default, typeToString(typeParameter));
}
const constraintType = getConstraintOfTypeParameter(typeParameter);
const defaultType = getDefaultFromTypeParameter(typeParameter);
if (constraintType && defaultType) {

View file

@ -2224,6 +2224,10 @@
"category": "Error",
"code": 2715
},
"Type parameter '{0}' has a circular default.": {
"category": "Error",
"code": 2716
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",

View file

@ -487,7 +487,10 @@ const t03c00 = (<t03<number>>x).a;
const t03c01 = (<t03<1>>x).a;
const t03c02 = (<t03<number, number>>x).a;
const t03c03 = (<t03<1, 1>>x).a;
const t03c04 = (<t03<number, 1>>x).a;
const t03c04 = (<t03<number, 1>>x).a;
// https://github.com/Microsoft/TypeScript/issues/16221
interface SelfReference<T = SelfReference<string>> {}
//// [genericDefaults.js]
// no inference
@ -1024,3 +1027,5 @@ declare const t03c01: [1, 1];
declare const t03c02: [number, number];
declare const t03c03: [1, 1];
declare const t03c04: [number, 1];
interface SelfReference<T = SelfReference<string>> {
}

View file

@ -2291,3 +2291,9 @@ const t03c04 = (<t03<number, 1>>x).a;
>x : Symbol(x, Decl(genericDefaults.ts, 13, 13))
>a : Symbol(a, Decl(genericDefaults.ts, 483, 47))
// https://github.com/Microsoft/TypeScript/issues/16221
interface SelfReference<T = SelfReference<string>> {}
>SelfReference : Symbol(SelfReference, Decl(genericDefaults.ts, 488, 37))
>T : Symbol(T, Decl(genericDefaults.ts, 491, 24))
>SelfReference : Symbol(SelfReference, Decl(genericDefaults.ts, 488, 37))

View file

@ -2643,3 +2643,9 @@ const t03c04 = (<t03<number, 1>>x).a;
>x : any
>a : [number, 1]
// https://github.com/Microsoft/TypeScript/issues/16221
interface SelfReference<T = SelfReference<string>> {}
>SelfReference : SelfReference<T>
>T : T
>SelfReference : SelfReference<T>

View file

@ -21,9 +21,10 @@ tests/cases/compiler/genericDefaultsErrors.ts(33,15): error TS2707: Generic type
tests/cases/compiler/genericDefaultsErrors.ts(36,15): error TS2707: Generic type 'i09<T, U, V>' requires between 2 and 3 type arguments.
tests/cases/compiler/genericDefaultsErrors.ts(38,20): error TS2304: Cannot find name 'T'.
tests/cases/compiler/genericDefaultsErrors.ts(38,20): error TS4033: Property 'x' of exported interface has or is using private name 'T'.
tests/cases/compiler/genericDefaultsErrors.ts(42,29): error TS2715: Type parameter 'T' has a circular default.
==== tests/cases/compiler/genericDefaultsErrors.ts (21 errors) ====
==== tests/cases/compiler/genericDefaultsErrors.ts (22 errors) ====
declare const x: any;
declare function f03<T extends string = number>(): void; // error
@ -106,4 +107,9 @@ tests/cases/compiler/genericDefaultsErrors.ts(38,20): error TS4033: Property 'x'
!!! error TS2304: Cannot find name 'T'.
~
!!! error TS4033: Property 'x' of exported interface has or is using private name 'T'.
interface i10<T = number> {}
interface i10<T = number> {}
// https://github.com/Microsoft/TypeScript/issues/16221
interface SelfReference<T = SelfReference> {}
~~~~~~~~~~~~~
!!! error TS2715: Type parameter 'T' has a circular default.

View file

@ -37,7 +37,10 @@ type i09t03 = i09<1, 2, 3>; // ok
type i09t04 = i09<1, 2, 3, 4>; // error
interface i10 { x: T; } // error
interface i10<T = number> {}
interface i10<T = number> {}
// https://github.com/Microsoft/TypeScript/issues/16221
interface SelfReference<T = SelfReference> {}
//// [genericDefaultsErrors.js]
f11(); // ok

View file

@ -136,3 +136,9 @@ interface i10<T = number> {}
>i10 : Symbol(i10, Decl(genericDefaultsErrors.ts, 35, 30), Decl(genericDefaultsErrors.ts, 37, 23))
>T : Symbol(T, Decl(genericDefaultsErrors.ts, 38, 14))
// https://github.com/Microsoft/TypeScript/issues/16221
interface SelfReference<T = SelfReference> {}
>SelfReference : Symbol(SelfReference, Decl(genericDefaultsErrors.ts, 38, 28))
>T : Symbol(T, Decl(genericDefaultsErrors.ts, 41, 24))
>SelfReference : Symbol(SelfReference, Decl(genericDefaultsErrors.ts, 38, 28))

View file

@ -145,3 +145,9 @@ interface i10<T = number> {}
>i10 : i10<T>
>T : T
// https://github.com/Microsoft/TypeScript/issues/16221
interface SelfReference<T = SelfReference> {}
>SelfReference : SelfReference<T>
>T : T
>SelfReference : SelfReference<T>

View file

@ -487,4 +487,7 @@ const t03c00 = (<t03<number>>x).a;
const t03c01 = (<t03<1>>x).a;
const t03c02 = (<t03<number, number>>x).a;
const t03c03 = (<t03<1, 1>>x).a;
const t03c04 = (<t03<number, 1>>x).a;
const t03c04 = (<t03<number, 1>>x).a;
// https://github.com/Microsoft/TypeScript/issues/16221
interface SelfReference<T = SelfReference<string>> {}

View file

@ -38,4 +38,7 @@ type i09t03 = i09<1, 2, 3>; // ok
type i09t04 = i09<1, 2, 3, 4>; // error
interface i10 { x: T; } // error
interface i10<T = number> {}
interface i10<T = number> {}
// https://github.com/Microsoft/TypeScript/issues/16221
interface SelfReference<T = SelfReference> {}