Skip to content

Commit 0b44a2c

Browse files
committed
Flexible declaration merging
1 parent ca16ba8 commit 0b44a2c

File tree

9 files changed

+244
-116
lines changed

9 files changed

+244
-116
lines changed

src/compiler/checker.ts

Lines changed: 102 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -18403,16 +18403,110 @@ namespace ts {
1840318403
if (symbol.declarations.length === 1) {
1840418404
return;
1840518405
}
18406-
let firstDecl: ClassLikeDeclaration | InterfaceDeclaration;
18407-
for (const declaration of symbol.declarations) {
18408-
if (declaration.kind === SyntaxKind.ClassDeclaration || declaration.kind === SyntaxKind.InterfaceDeclaration) {
18409-
if (!firstDecl) {
18410-
firstDecl = <ClassLikeDeclaration | InterfaceDeclaration>declaration;
18411-
}
18412-
else if (!areTypeParametersIdentical(firstDecl.typeParameters, node.typeParameters)) {
18413-
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
18406+
18407+
// Resolve the type parameters and minimum type argument count for all declarations
18408+
resolveTypeParametersOfClassOrInterface(symbol);
18409+
18410+
const { typeParameters, minTypeArgumentCount } = getSymbolLinks(symbol);
18411+
const maxTypeArgumentCount = typeParameters ? typeParameters.length : 0;
18412+
const numTypeParameters = node.typeParameters ? node.typeParameters.length : 0;
18413+
18414+
// If this declaration has too few or too many type parameters, we report an error
18415+
if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) {
18416+
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
18417+
return;
18418+
}
18419+
18420+
for (let i = 0; i < numTypeParameters; i++) {
18421+
const source = node.typeParameters[i];
18422+
const target = typeParameters[i];
18423+
18424+
// If the type parameter node does not have the same name as the resolved type
18425+
// parameter at this position, we report an error.
18426+
if (source.name.text !== target.symbol.name) {
18427+
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
18428+
return;
18429+
}
18430+
18431+
// If the type parameter node does not have an identical constraint as the resolved
18432+
// type parameter at this position, we report an error.
18433+
const sourceConstraint = source.constraint && getTypeFromTypeNode(source.constraint);
18434+
const targetConstraint = getConstraintFromTypeParameter(target);
18435+
if ((sourceConstraint || targetConstraint) &&
18436+
(!sourceConstraint || !targetConstraint || !isTypeIdenticalTo(sourceConstraint, targetConstraint))) {
18437+
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
18438+
return;
18439+
}
18440+
18441+
// If the type parameter node has a default and it is not identical to the default
18442+
// for the type parameter at this position, we report an error.
18443+
const sourceDefault = source.default && getTypeFromTypeNode(source.default);
18444+
const targetDefault = getDefaultFromTypeParameter(target);
18445+
if (sourceDefault && targetDefault && !isTypeIdenticalTo(sourceDefault, targetDefault)) {
18446+
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
18447+
return;
18448+
}
18449+
}
18450+
}
18451+
18452+
function resolveTypeParametersOfClassOrInterface(symbol: Symbol) {
18453+
const links = getSymbolLinks(symbol);
18454+
if (!links.typeParameters) {
18455+
let typeParameters: TypeParameter[] | undefined;
18456+
let minTypeArgumentCount = -1;
18457+
let maxTypeArgumentCount = -1;
18458+
for (const declaration of symbol.declarations) {
18459+
if (declaration.kind === SyntaxKind.ClassDeclaration || declaration.kind === SyntaxKind.InterfaceDeclaration) {
18460+
const typeParameterNodes = (<ClassDeclaration | InterfaceDeclaration>declaration).typeParameters;
18461+
const numTypeParameters = typeParameterNodes ? typeParameterNodes.length : 0;
18462+
if (maxTypeArgumentCount === -1) {
18463+
// For the first declaration, establish the initial maximum and
18464+
// minimum type argument counts. These only change when we
18465+
// encounter default type arguments.
18466+
maxTypeArgumentCount = numTypeParameters;
18467+
minTypeArgumentCount = numTypeParameters;
18468+
}
18469+
18470+
if (typeParameterNodes) {
18471+
if (!typeParameters) {
18472+
typeParameters = [];
18473+
}
18474+
18475+
for (let i = 0; i < typeParameterNodes.length; i++) {
18476+
if (typeParameterNodes[i].default) {
18477+
// When we encounter a type parameter with a default, establish
18478+
// new minimum and maximum type arguments if necessary.
18479+
if (minTypeArgumentCount > i) {
18480+
minTypeArgumentCount = i;
18481+
}
18482+
if (maxTypeArgumentCount < i + 1) {
18483+
maxTypeArgumentCount = i + 1;
18484+
}
18485+
}
18486+
if (typeParameters.length <= i) {
18487+
// When we encounter a new type parameter at this position,
18488+
// get the declared type for the type parameter. If another
18489+
// declaration attempts to establish a type parameter with a
18490+
// different name or constraint than the first one we find,
18491+
// we will report an error when checking the type parameters.
18492+
typeParameters[i] = getDeclaredTypeOfTypeParameter(getSymbolOfNode(typeParameterNodes[i]));
18493+
}
18494+
}
18495+
}
1841418496
}
1841518497
}
18498+
if (maxTypeArgumentCount === -1) {
18499+
maxTypeArgumentCount = 0;
18500+
}
18501+
if (minTypeArgumentCount === -1) {
18502+
minTypeArgumentCount = maxTypeArgumentCount;
18503+
}
18504+
if (typeParameters && typeParameters.length > maxTypeArgumentCount) {
18505+
// Trim the type parameters to the maximum length
18506+
typeParameters.length = maxTypeArgumentCount;
18507+
}
18508+
links.typeParameters = typeParameters || emptyArray;
18509+
links.minTypeArgumentCount = minTypeArgumentCount;
1841618510
}
1841718511
}
1841818512

@@ -18654,36 +18748,6 @@ namespace ts {
1865418748
return kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor;
1865518749
}
1865618750

18657-
function areTypeParametersIdentical(list1: TypeParameterDeclaration[], list2: TypeParameterDeclaration[]) {
18658-
if (!list1 && !list2) {
18659-
return true;
18660-
}
18661-
if (!list1 || !list2 || list1.length !== list2.length) {
18662-
return false;
18663-
}
18664-
// TypeScript 1.0 spec (April 2014):
18665-
// When a generic interface has multiple declarations, all declarations must have identical type parameter
18666-
// lists, i.e. identical type parameter names with identical constraints in identical order.
18667-
for (let i = 0; i < list1.length; i++) {
18668-
const tp1 = list1[i];
18669-
const tp2 = list2[i];
18670-
if (tp1.name.text !== tp2.name.text) {
18671-
return false;
18672-
}
18673-
if (tp1.constraint || tp2.constraint) {
18674-
if (!tp1.constraint || !tp2.constraint || !isTypeIdenticalTo(getTypeFromTypeNode(tp1.constraint), getTypeFromTypeNode(tp2.constraint))) {
18675-
return false;
18676-
}
18677-
}
18678-
if (tp1.default || tp2.default) {
18679-
if (!tp1.default || !tp2.default || !isTypeIdenticalTo(getTypeFromTypeNode(tp1.default), getTypeFromTypeNode(tp2.default))) {
18680-
return false;
18681-
}
18682-
}
18683-
}
18684-
return true;
18685-
}
18686-
1868718751
function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean {
1868818752
const baseTypes = getBaseTypes(type);
1868918753
if (baseTypes.length < 2) {

src/compiler/utilities.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,20 @@ namespace ts {
2424
return undefined;
2525
}
2626

27+
export function findDeclaration<T extends Declaration>(symbol: Symbol, predicate: (node: Declaration) => node is T): T | undefined;
28+
export function findDeclaration(symbol: Symbol, predicate: (node: Declaration) => boolean): Declaration | undefined;
29+
export function findDeclaration(symbol: Symbol, predicate: (node: Declaration) => boolean): Declaration | undefined {
30+
const declarations = symbol.declarations;
31+
if (declarations) {
32+
for (const declaration of declarations) {
33+
if (predicate(declaration)) {
34+
return declaration;
35+
}
36+
}
37+
}
38+
return undefined;
39+
}
40+
2741
export interface StringSymbolWriter extends SymbolWriter {
2842
string(): string;
2943
}

tests/baselines/reference/genericDefaults.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ const i03c02 = (<i03<number, number>>x).a;
8686
const i03c03 = (<i03<1, 1>>x).a;
8787
const i03c04 = (<i03<number, 1>>x).a;
8888

89+
interface i04 {}
90+
interface i04<T> {}
91+
interface i04<T = number> {}
92+
interface i04<T = number, U = string> {}
93+
8994
interface Base01<T> { a: T; }
9095
interface Base01Constructor { new <T = number>(a?: T): Base01<T>; }
9196

@@ -293,6 +298,14 @@ declare const i03c01: [1, 1];
293298
declare const i03c02: [number, number];
294299
declare const i03c03: [1, 1];
295300
declare const i03c04: [number, 1];
301+
interface i04 {
302+
}
303+
interface i04<T> {
304+
}
305+
interface i04<T = number> {
306+
}
307+
interface i04<T = number, U = string> {
308+
}
296309
interface Base01<T> {
297310
a: T;
298311
}

tests/baselines/reference/genericDefaults.symbols

Lines changed: 60 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -408,81 +408,97 @@ const i03c04 = (<i03<number, 1>>x).a;
408408
>x : Symbol(x, Decl(genericDefaults.ts, 0, 13))
409409
>a : Symbol(i03.a, Decl(genericDefaults.ts, 80, 50))
410410

411+
interface i04 {}
412+
>i04 : Symbol(i04, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 87, 16), Decl(genericDefaults.ts, 88, 19), Decl(genericDefaults.ts, 89, 28))
413+
414+
interface i04<T> {}
415+
>i04 : Symbol(i04, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 87, 16), Decl(genericDefaults.ts, 88, 19), Decl(genericDefaults.ts, 89, 28))
416+
>T : Symbol(T, Decl(genericDefaults.ts, 88, 14), Decl(genericDefaults.ts, 89, 14), Decl(genericDefaults.ts, 90, 14))
417+
418+
interface i04<T = number> {}
419+
>i04 : Symbol(i04, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 87, 16), Decl(genericDefaults.ts, 88, 19), Decl(genericDefaults.ts, 89, 28))
420+
>T : Symbol(T, Decl(genericDefaults.ts, 88, 14), Decl(genericDefaults.ts, 89, 14), Decl(genericDefaults.ts, 90, 14))
421+
422+
interface i04<T = number, U = string> {}
423+
>i04 : Symbol(i04, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 87, 16), Decl(genericDefaults.ts, 88, 19), Decl(genericDefaults.ts, 89, 28))
424+
>T : Symbol(T, Decl(genericDefaults.ts, 88, 14), Decl(genericDefaults.ts, 89, 14), Decl(genericDefaults.ts, 90, 14))
425+
>U : Symbol(U, Decl(genericDefaults.ts, 90, 25))
426+
411427
interface Base01<T> { a: T; }
412-
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
413-
>T : Symbol(T, Decl(genericDefaults.ts, 87, 17))
414-
>a : Symbol(Base01.a, Decl(genericDefaults.ts, 87, 21))
415-
>T : Symbol(T, Decl(genericDefaults.ts, 87, 17))
428+
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
429+
>T : Symbol(T, Decl(genericDefaults.ts, 92, 17))
430+
>a : Symbol(Base01.a, Decl(genericDefaults.ts, 92, 21))
431+
>T : Symbol(T, Decl(genericDefaults.ts, 92, 17))
416432

417433
interface Base01Constructor { new <T = number>(a?: T): Base01<T>; }
418-
>Base01Constructor : Symbol(Base01Constructor, Decl(genericDefaults.ts, 87, 29))
419-
>T : Symbol(T, Decl(genericDefaults.ts, 88, 35))
420-
>a : Symbol(a, Decl(genericDefaults.ts, 88, 47))
421-
>T : Symbol(T, Decl(genericDefaults.ts, 88, 35))
422-
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
423-
>T : Symbol(T, Decl(genericDefaults.ts, 88, 35))
434+
>Base01Constructor : Symbol(Base01Constructor, Decl(genericDefaults.ts, 92, 29))
435+
>T : Symbol(T, Decl(genericDefaults.ts, 93, 35))
436+
>a : Symbol(a, Decl(genericDefaults.ts, 93, 47))
437+
>T : Symbol(T, Decl(genericDefaults.ts, 93, 35))
438+
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
439+
>T : Symbol(T, Decl(genericDefaults.ts, 93, 35))
424440

425441
declare const Base01: Base01Constructor;
426-
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
427-
>Base01Constructor : Symbol(Base01Constructor, Decl(genericDefaults.ts, 87, 29))
442+
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
443+
>Base01Constructor : Symbol(Base01Constructor, Decl(genericDefaults.ts, 92, 29))
428444

429445
const Base01c00 = new Base01();
430-
>Base01c00 : Symbol(Base01c00, Decl(genericDefaults.ts, 91, 5))
431-
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
446+
>Base01c00 : Symbol(Base01c00, Decl(genericDefaults.ts, 96, 5))
447+
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
432448

433449
const Base01c01 = new Base01(1);
434-
>Base01c01 : Symbol(Base01c01, Decl(genericDefaults.ts, 92, 5))
435-
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
450+
>Base01c01 : Symbol(Base01c01, Decl(genericDefaults.ts, 97, 5))
451+
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
436452

437453
const Base01c02 = new Base01<number>();
438-
>Base01c02 : Symbol(Base01c02, Decl(genericDefaults.ts, 93, 5))
439-
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
454+
>Base01c02 : Symbol(Base01c02, Decl(genericDefaults.ts, 98, 5))
455+
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
440456

441457
const Base01c03 = new Base01<number>(1);
442-
>Base01c03 : Symbol(Base01c03, Decl(genericDefaults.ts, 94, 5))
443-
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
458+
>Base01c03 : Symbol(Base01c03, Decl(genericDefaults.ts, 99, 5))
459+
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
444460

445461
declare class Derived01<T> extends Base01<T> { }
446-
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 94, 40))
447-
>T : Symbol(T, Decl(genericDefaults.ts, 96, 24))
448-
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
449-
>T : Symbol(T, Decl(genericDefaults.ts, 96, 24))
462+
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 99, 40))
463+
>T : Symbol(T, Decl(genericDefaults.ts, 101, 24))
464+
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
465+
>T : Symbol(T, Decl(genericDefaults.ts, 101, 24))
450466

451467
const Derived01c00 = new Derived01();
452-
>Derived01c00 : Symbol(Derived01c00, Decl(genericDefaults.ts, 97, 5))
453-
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 94, 40))
468+
>Derived01c00 : Symbol(Derived01c00, Decl(genericDefaults.ts, 102, 5))
469+
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 99, 40))
454470

455471
const Derived01c01 = new Derived01(1);
456-
>Derived01c01 : Symbol(Derived01c01, Decl(genericDefaults.ts, 98, 5))
457-
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 94, 40))
472+
>Derived01c01 : Symbol(Derived01c01, Decl(genericDefaults.ts, 103, 5))
473+
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 99, 40))
458474

459475
const Derived01c02 = new Derived01<number>();
460-
>Derived01c02 : Symbol(Derived01c02, Decl(genericDefaults.ts, 99, 5))
461-
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 94, 40))
476+
>Derived01c02 : Symbol(Derived01c02, Decl(genericDefaults.ts, 104, 5))
477+
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 99, 40))
462478

463479
const Derived01c03 = new Derived01<number>(1);
464-
>Derived01c03 : Symbol(Derived01c03, Decl(genericDefaults.ts, 100, 5))
465-
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 94, 40))
480+
>Derived01c03 : Symbol(Derived01c03, Decl(genericDefaults.ts, 105, 5))
481+
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 99, 40))
466482

467483
declare class Derived02<T = string> extends Base01<T> { }
468-
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 100, 46))
469-
>T : Symbol(T, Decl(genericDefaults.ts, 102, 24))
470-
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
471-
>T : Symbol(T, Decl(genericDefaults.ts, 102, 24))
484+
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 105, 46))
485+
>T : Symbol(T, Decl(genericDefaults.ts, 107, 24))
486+
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
487+
>T : Symbol(T, Decl(genericDefaults.ts, 107, 24))
472488

473489
const Derived02c00 = new Derived02();
474-
>Derived02c00 : Symbol(Derived02c00, Decl(genericDefaults.ts, 103, 5))
475-
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 100, 46))
490+
>Derived02c00 : Symbol(Derived02c00, Decl(genericDefaults.ts, 108, 5))
491+
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 105, 46))
476492

477493
const Derived02c01 = new Derived02(1);
478-
>Derived02c01 : Symbol(Derived02c01, Decl(genericDefaults.ts, 104, 5))
479-
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 100, 46))
494+
>Derived02c01 : Symbol(Derived02c01, Decl(genericDefaults.ts, 109, 5))
495+
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 105, 46))
480496

481497
const Derived02c02 = new Derived02<number>();
482-
>Derived02c02 : Symbol(Derived02c02, Decl(genericDefaults.ts, 105, 5))
483-
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 100, 46))
498+
>Derived02c02 : Symbol(Derived02c02, Decl(genericDefaults.ts, 110, 5))
499+
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 105, 46))
484500

485501
const Derived02c03 = new Derived02<number>(1);
486-
>Derived02c03 : Symbol(Derived02c03, Decl(genericDefaults.ts, 106, 5))
487-
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 100, 46))
502+
>Derived02c03 : Symbol(Derived02c03, Decl(genericDefaults.ts, 111, 5))
503+
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 105, 46))
488504

tests/baselines/reference/genericDefaults.types

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,22 @@ const i03c04 = (<i03<number, 1>>x).a;
536536
>x : any
537537
>a : [number, 1]
538538

539+
interface i04 {}
540+
>i04 : i04<T, U>
541+
542+
interface i04<T> {}
543+
>i04 : i04<T, U>
544+
>T : T
545+
546+
interface i04<T = number> {}
547+
>i04 : i04<T, U>
548+
>T : T
549+
550+
interface i04<T = number, U = string> {}
551+
>i04 : i04<T, U>
552+
>T : T
553+
>U : U
554+
539555
interface Base01<T> { a: T; }
540556
>Base01 : Base01<T>
541557
>T : T

0 commit comments

Comments
 (0)