Skip to content

Commit 5bb2fe0

Browse files
committed
Simplify checkTypeParameterListsIdentical
1 parent 6ffcbf5 commit 5bb2fe0

13 files changed

+149
-107
lines changed

src/compiler/checker.ts

Lines changed: 53 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ namespace ts {
376376
Type,
377377
ResolvedBaseConstructorType,
378378
DeclaredType,
379-
ResolvedReturnType,
379+
ResolvedReturnType
380380
}
381381

382382
const builtinGlobals = createMap<Symbol>();
@@ -18549,115 +18549,67 @@ namespace ts {
1854918549
}
1855018550

1855118551
/** Check that type parameter lists are identical across multiple declarations */
18552-
function checkTypeParameterListsIdentical(node: ClassLikeDeclaration | InterfaceDeclaration, symbol: Symbol) {
18552+
function checkTypeParameterListsIdentical(symbol: Symbol) {
1855318553
if (symbol.declarations.length === 1) {
1855418554
return;
1855518555
}
1855618556

18557-
// Resolve the type parameters and minimum type argument count for all declarations
18558-
resolveTypeParametersOfClassOrInterface(symbol);
18557+
const links = getSymbolLinks(symbol);
18558+
if (!links.typeParametersChecked) {
18559+
links.typeParametersChecked = true;
18560+
const type = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
18561+
const declarations = getClassOrInterfaceDeclarationsOfSymbol(symbol);
18562+
if (!areTypeParametersIdentical(declarations, type.localTypeParameters)) {
18563+
// Report an error on every conflicting declaration.
18564+
const name = symbolToString(symbol);
18565+
for (const declaration of declarations) {
18566+
error(declaration.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, name);
18567+
}
18568+
}
18569+
}
18570+
}
1855918571

18560-
const typeParameters = getSymbolLinks(symbol).typeParameters;
18572+
function areTypeParametersIdentical(declarations: (ClassDeclaration | InterfaceDeclaration)[], typeParameters: TypeParameter[]) {
1856118573
const maxTypeArgumentCount = length(typeParameters);
1856218574
const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters);
18563-
const numTypeParameters = length(node.typeParameters);
18564-
18565-
// If this declaration has too few or too many type parameters, we report an error
18566-
if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) {
18567-
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
18568-
return;
18569-
}
18570-
18571-
for (let i = 0; i < numTypeParameters; i++) {
18572-
const source = node.typeParameters[i];
18573-
const target = typeParameters[i];
1857418575

18575-
// If the type parameter node does not have the same name as the resolved type
18576-
// parameter at this position, we report an error.
18577-
if (source.name.text !== target.symbol.name) {
18578-
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
18579-
return;
18576+
for (const declaration of declarations) {
18577+
// If this declaration has too few or too many type parameters, we report an error
18578+
const numTypeParameters = length(declaration.typeParameters);
18579+
if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) {
18580+
return false;
1858018581
}
1858118582

18582-
// If the type parameter node does not have an identical constraint as the resolved
18583-
// type parameter at this position, we report an error.
18584-
const sourceConstraint = source.constraint && getTypeFromTypeNode(source.constraint);
18585-
const targetConstraint = getConstraintFromTypeParameter(target);
18586-
if ((sourceConstraint || targetConstraint) &&
18587-
(!sourceConstraint || !targetConstraint || !isTypeIdenticalTo(sourceConstraint, targetConstraint))) {
18588-
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
18589-
return;
18590-
}
18583+
for (let i = 0; i < numTypeParameters; i++) {
18584+
const source = declaration.typeParameters[i];
18585+
const target = typeParameters[i];
1859118586

18592-
// If the type parameter node has a default and it is not identical to the default
18593-
// for the type parameter at this position, we report an error.
18594-
const sourceDefault = source.default && getTypeFromTypeNode(source.default);
18595-
const targetDefault = getDefaultFromTypeParameter(target);
18596-
if (sourceDefault && targetDefault && !isTypeIdenticalTo(sourceDefault, targetDefault)) {
18597-
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
18598-
return;
18599-
}
18600-
}
18601-
}
18587+
// If the type parameter node does not have the same as the resolved type
18588+
// parameter at this position, we report an error.
18589+
if (source.name.text !== target.symbol.name) {
18590+
return false;
18591+
}
1860218592

18603-
function resolveTypeParametersOfClassOrInterface(symbol: Symbol) {
18604-
const links = getSymbolLinks(symbol);
18605-
if (!links.typeParameters) {
18606-
let typeParameters: TypeParameter[] | undefined;
18607-
let minTypeArgumentCount = -1;
18608-
let maxTypeArgumentCount = -1;
18609-
for (const declaration of symbol.declarations) {
18610-
if (declaration.kind === SyntaxKind.ClassDeclaration || declaration.kind === SyntaxKind.InterfaceDeclaration) {
18611-
const typeParameterNodes = (<ClassDeclaration | InterfaceDeclaration>declaration).typeParameters;
18612-
const numTypeParameters = length(typeParameterNodes);
18613-
if (maxTypeArgumentCount === -1) {
18614-
// For the first declaration, establish the initial maximum and
18615-
// minimum type argument counts. These only change when we
18616-
// encounter default type arguments.
18617-
maxTypeArgumentCount = numTypeParameters;
18618-
minTypeArgumentCount = numTypeParameters;
18619-
}
18620-
18621-
if (typeParameterNodes) {
18622-
if (!typeParameters) {
18623-
typeParameters = [];
18624-
}
18593+
// If the type parameter node does not have an identical constraint as the resolved
18594+
// type parameter at this position, we report an error.
18595+
const sourceConstraint = source.constraint && getTypeFromTypeNode(source.constraint);
18596+
const targetConstraint = getConstraintFromTypeParameter(target);
18597+
if ((sourceConstraint || targetConstraint) &&
18598+
(!sourceConstraint || !targetConstraint || !isTypeIdenticalTo(sourceConstraint, targetConstraint))) {
18599+
return false;
18600+
}
1862518601

18626-
for (let i = 0; i < typeParameterNodes.length; i++) {
18627-
if (typeParameterNodes[i].default) {
18628-
// When we encounter a type parameter with a default, establish
18629-
// new minimum and maximum type arguments if necessary.
18630-
if (minTypeArgumentCount > i) {
18631-
minTypeArgumentCount = i;
18632-
}
18633-
if (maxTypeArgumentCount < i + 1) {
18634-
maxTypeArgumentCount = i + 1;
18635-
}
18636-
}
18637-
if (typeParameters.length <= i) {
18638-
// When we encounter a new type parameter at this position,
18639-
// get the declared type for the type parameter. If another
18640-
// declaration attempts to establish a type parameter with a
18641-
// different name or constraint than the first one we find,
18642-
// we will report an error when checking the type parameters.
18643-
typeParameters[i] = getDeclaredTypeOfTypeParameter(getSymbolOfNode(typeParameterNodes[i]));
18644-
}
18645-
}
18646-
}
18602+
// If the type parameter node has a default and it is not identical to the default
18603+
// for the type parameter at this position, we report an error.
18604+
const sourceDefault = source.default && getTypeFromTypeNode(source.default);
18605+
const targetDefault = getDefaultFromTypeParameter(target);
18606+
if (sourceDefault && targetDefault && !isTypeIdenticalTo(sourceDefault, targetDefault)) {
18607+
return false;
1864718608
}
1864818609
}
18649-
if (maxTypeArgumentCount === -1) {
18650-
maxTypeArgumentCount = 0;
18651-
}
18652-
if (minTypeArgumentCount === -1) {
18653-
minTypeArgumentCount = maxTypeArgumentCount;
18654-
}
18655-
if (typeParameters && typeParameters.length > maxTypeArgumentCount) {
18656-
// Trim the type parameters to the maximum length
18657-
typeParameters.length = maxTypeArgumentCount;
18658-
}
18659-
links.typeParameters = typeParameters || emptyArray;
1866018610
}
18611+
18612+
return true;
1866118613
}
1866218614

1866318615
function checkClassExpression(node: ClassExpression): Type {
@@ -18697,7 +18649,7 @@ namespace ts {
1869718649
const type = <InterfaceType>getDeclaredTypeOfSymbol(symbol);
1869818650
const typeWithThis = getTypeWithThisArgument(type);
1869918651
const staticType = <ObjectType>getTypeOfSymbol(symbol);
18700-
checkTypeParameterListsIdentical(node, symbol);
18652+
checkTypeParameterListsIdentical(symbol);
1870118653
checkClassForDuplicateDeclarations(node);
1870218654

1870318655
// Only check for reserved static identifiers on non-ambient context.
@@ -18805,6 +18757,11 @@ namespace ts {
1880518757
return forEach(symbol.declarations, d => isClassLike(d) ? d : undefined);
1880618758
}
1880718759

18760+
function getClassOrInterfaceDeclarationsOfSymbol(symbol: Symbol) {
18761+
return filter(symbol.declarations, (d: Declaration): d is ClassDeclaration | InterfaceDeclaration =>
18762+
d.kind === SyntaxKind.ClassDeclaration || d.kind === SyntaxKind.InterfaceDeclaration);
18763+
}
18764+
1880818765
function checkKindsOfPropertyMemberOverrides(type: InterfaceType, baseType: BaseType): void {
1880918766

1881018767
// TypeScript 1.0 spec (April 2014): 8.2.3
@@ -18952,7 +18909,7 @@ namespace ts {
1895218909

1895318910
checkExportsOnMergedDeclarations(node);
1895418911
const symbol = getSymbolOfNode(node);
18955-
checkTypeParameterListsIdentical(node, symbol);
18912+
checkTypeParameterListsIdentical(symbol);
1895618913

1895718914
// Only check this symbol once
1895818915
const firstInterfaceDecl = <InterfaceDeclaration>getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration);

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2725,6 +2725,7 @@
27252725
isDiscriminantProperty?: boolean; // True if discriminant synthetic property
27262726
resolvedExports?: SymbolTable; // Resolved exports of module
27272727
exportsChecked?: boolean; // True if exports of external module have been checked
2728+
typeParametersChecked?: boolean; // True if type parameters of merged class and interface declarations have been checked.
27282729
isDeclarationWithCollidingName?: boolean; // True if symbol is block scoped redeclaration
27292730
bindingElement?: BindingElement; // Binding element associated with property symbol
27302731
exportsSomeValue?: boolean; // True if module exports some value (not just types)

tests/baselines/reference/constructSignaturesWithOverloads2.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
tests/cases/conformance/types/objectTypeLiteral/constructSignatures/constructSignaturesWithOverloads2.ts(27,11): error TS2428: All declarations of 'I' must have identical type parameters.
12
tests/cases/conformance/types/objectTypeLiteral/constructSignatures/constructSignaturesWithOverloads2.ts(32,11): error TS2428: All declarations of 'I' must have identical type parameters.
23

34

4-
==== tests/cases/conformance/types/objectTypeLiteral/constructSignatures/constructSignaturesWithOverloads2.ts (1 errors) ====
5+
==== tests/cases/conformance/types/objectTypeLiteral/constructSignatures/constructSignaturesWithOverloads2.ts (2 errors) ====
56
// No errors expected for basic overloads of construct signatures with merged declarations
67

78
// clodules
@@ -29,6 +30,8 @@ tests/cases/conformance/types/objectTypeLiteral/constructSignatures/constructSig
2930

3031
// merged interfaces
3132
interface I {
33+
~
34+
!!! error TS2428: All declarations of 'I' must have identical type parameters.
3235
new (x: number, y?: string): C;
3336
new (x: number, y: string): C;
3437
}

tests/baselines/reference/extendedInterfacesWithDuplicateTypeParameters.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
tests/cases/compiler/extendedInterfacesWithDuplicateTypeParameters.ts(1,42): error TS2300: Duplicate identifier 'A'.
2+
tests/cases/compiler/extendedInterfacesWithDuplicateTypeParameters.ts(5,11): error TS2428: All declarations of 'InterfaceWithSomeTypars' must have identical type parameters.
23
tests/cases/compiler/extendedInterfacesWithDuplicateTypeParameters.ts(9,11): error TS2428: All declarations of 'InterfaceWithSomeTypars' must have identical type parameters.
34
tests/cases/compiler/extendedInterfacesWithDuplicateTypeParameters.ts(9,38): error TS2300: Duplicate identifier 'C'.
45

56

6-
==== tests/cases/compiler/extendedInterfacesWithDuplicateTypeParameters.ts (3 errors) ====
7+
==== tests/cases/compiler/extendedInterfacesWithDuplicateTypeParameters.ts (4 errors) ====
78
interface InterfaceWithMultipleTypars<A, A> { // should error
89
~
910
!!! error TS2300: Duplicate identifier 'A'.
1011
bar(): void;
1112
}
1213

1314
interface InterfaceWithSomeTypars<B> { // should not error
15+
~~~~~~~~~~~~~~~~~~~~~~~
16+
!!! error TS2428: All declarations of 'InterfaceWithSomeTypars' must have identical type parameters.
1417
bar(): void;
1518
}
1619

tests/baselines/reference/genericAndNonGenericInterfaceWithTheSameName.errors.txt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1+
tests/cases/conformance/interfaces/declarationMerging/genericAndNonGenericInterfaceWithTheSameName.ts(3,11): error TS2428: All declarations of 'A' must have identical type parameters.
12
tests/cases/conformance/interfaces/declarationMerging/genericAndNonGenericInterfaceWithTheSameName.ts(7,11): error TS2428: All declarations of 'A' must have identical type parameters.
3+
tests/cases/conformance/interfaces/declarationMerging/genericAndNonGenericInterfaceWithTheSameName.ts(12,15): error TS2428: All declarations of 'A' must have identical type parameters.
24
tests/cases/conformance/interfaces/declarationMerging/genericAndNonGenericInterfaceWithTheSameName.ts(16,15): error TS2428: All declarations of 'A' must have identical type parameters.
5+
tests/cases/conformance/interfaces/declarationMerging/genericAndNonGenericInterfaceWithTheSameName.ts(34,22): error TS2428: All declarations of 'A' must have identical type parameters.
36
tests/cases/conformance/interfaces/declarationMerging/genericAndNonGenericInterfaceWithTheSameName.ts(40,22): error TS2428: All declarations of 'A' must have identical type parameters.
47

58

6-
==== tests/cases/conformance/interfaces/declarationMerging/genericAndNonGenericInterfaceWithTheSameName.ts (3 errors) ====
9+
==== tests/cases/conformance/interfaces/declarationMerging/genericAndNonGenericInterfaceWithTheSameName.ts (6 errors) ====
710
// generic and non-generic interfaces with the same name do not merge
811

912
interface A {
13+
~
14+
!!! error TS2428: All declarations of 'A' must have identical type parameters.
1015
foo: string;
1116
}
1217

@@ -18,6 +23,8 @@ tests/cases/conformance/interfaces/declarationMerging/genericAndNonGenericInterf
1823

1924
module M {
2025
interface A<T> {
26+
~
27+
!!! error TS2428: All declarations of 'A' must have identical type parameters.
2128
bar: T;
2229
}
2330

@@ -42,6 +49,8 @@ tests/cases/conformance/interfaces/declarationMerging/genericAndNonGenericInterf
4249

4350
module M3 {
4451
export interface A {
52+
~
53+
!!! error TS2428: All declarations of 'A' must have identical type parameters.
4554
foo: string;
4655
}
4756
}

tests/baselines/reference/genericDefaultsErrors.errors.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ tests/cases/compiler/genericDefaultsErrors.ts(7,39): error TS2344: Type 'number'
66
tests/cases/compiler/genericDefaultsErrors.ts(11,1): error TS2346: Supplied parameters do not match any signature of call target.
77
tests/cases/compiler/genericDefaultsErrors.ts(14,1): error TS2346: Supplied parameters do not match any signature of call target.
88
tests/cases/compiler/genericDefaultsErrors.ts(18,13): error TS2345: Argument of type '"a"' is not assignable to parameter of type 'number'.
9+
tests/cases/compiler/genericDefaultsErrors.ts(20,11): error TS2428: All declarations of 'i00' must have identical type parameters.
910
tests/cases/compiler/genericDefaultsErrors.ts(21,11): error TS2428: All declarations of 'i00' must have identical type parameters.
11+
tests/cases/compiler/genericDefaultsErrors.ts(23,11): error TS2428: All declarations of 'i01' must have identical type parameters.
1012
tests/cases/compiler/genericDefaultsErrors.ts(24,11): error TS2428: All declarations of 'i01' must have identical type parameters.
1113
tests/cases/compiler/genericDefaultsErrors.ts(26,27): error TS2705: Required type parameters may not follow optional type parameters.
1214
tests/cases/compiler/genericDefaultsErrors.ts(27,34): error TS2344: Type 'number' does not satisfy the constraint 'string'.
@@ -21,7 +23,7 @@ tests/cases/compiler/genericDefaultsErrors.ts(39,20): error TS2304: Cannot find
2123
tests/cases/compiler/genericDefaultsErrors.ts(39,20): error TS4033: Property 'x' of exported interface has or is using private name 'T'.
2224

2325

24-
==== tests/cases/compiler/genericDefaultsErrors.ts (19 errors) ====
26+
==== tests/cases/compiler/genericDefaultsErrors.ts (21 errors) ====
2527

2628
declare const x: any;
2729

@@ -57,11 +59,15 @@ tests/cases/compiler/genericDefaultsErrors.ts(39,20): error TS4033: Property 'x'
5759
!!! error TS2345: Argument of type '"a"' is not assignable to parameter of type 'number'.
5860

5961
interface i00<T> { } // ok
62+
~~~
63+
!!! error TS2428: All declarations of 'i00' must have identical type parameters.
6064
interface i00<U = number> { } // error
6165
~~~
6266
!!! error TS2428: All declarations of 'i00' must have identical type parameters.
6367

6468
interface i01<T = number> { } // ok
69+
~~~
70+
!!! error TS2428: All declarations of 'i01' must have identical type parameters.
6571
interface i01<T = string> { } // error
6672
~~~
6773
!!! error TS2428: All declarations of 'i01' must have identical type parameters.

0 commit comments

Comments
 (0)