Flexible declaration merging

This commit is contained in:
Ron Buckton 2017-01-19 12:38:56 -08:00
parent ca16ba8fe7
commit 0b44a2c74c
9 changed files with 244 additions and 116 deletions

View file

@ -18403,16 +18403,110 @@ namespace ts {
if (symbol.declarations.length === 1) {
return;
}
let firstDecl: ClassLikeDeclaration | InterfaceDeclaration;
for (const declaration of symbol.declarations) {
if (declaration.kind === SyntaxKind.ClassDeclaration || declaration.kind === SyntaxKind.InterfaceDeclaration) {
if (!firstDecl) {
firstDecl = <ClassLikeDeclaration | InterfaceDeclaration>declaration;
}
else if (!areTypeParametersIdentical(firstDecl.typeParameters, node.typeParameters)) {
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
// Resolve the type parameters and minimum type argument count for all declarations
resolveTypeParametersOfClassOrInterface(symbol);
const { typeParameters, minTypeArgumentCount } = getSymbolLinks(symbol);
const maxTypeArgumentCount = typeParameters ? typeParameters.length : 0;
const numTypeParameters = node.typeParameters ? node.typeParameters.length : 0;
// If this declaration has too few or too many type parameters, we report an error
if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) {
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
return;
}
for (let i = 0; i < numTypeParameters; i++) {
const source = node.typeParameters[i];
const target = typeParameters[i];
// If the type parameter node does not have the same name as the resolved type
// parameter at this position, we report an error.
if (source.name.text !== target.symbol.name) {
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
return;
}
// If the type parameter node does not have an identical constraint as the resolved
// type parameter at this position, we report an error.
const sourceConstraint = source.constraint && getTypeFromTypeNode(source.constraint);
const targetConstraint = getConstraintFromTypeParameter(target);
if ((sourceConstraint || targetConstraint) &&
(!sourceConstraint || !targetConstraint || !isTypeIdenticalTo(sourceConstraint, targetConstraint))) {
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
return;
}
// If the type parameter node has a default and it is not identical to the default
// for the type parameter at this position, we report an error.
const sourceDefault = source.default && getTypeFromTypeNode(source.default);
const targetDefault = getDefaultFromTypeParameter(target);
if (sourceDefault && targetDefault && !isTypeIdenticalTo(sourceDefault, targetDefault)) {
error(node.name, Diagnostics.All_declarations_of_0_must_have_identical_type_parameters, node.name.text);
return;
}
}
}
function resolveTypeParametersOfClassOrInterface(symbol: Symbol) {
const links = getSymbolLinks(symbol);
if (!links.typeParameters) {
let typeParameters: TypeParameter[] | undefined;
let minTypeArgumentCount = -1;
let maxTypeArgumentCount = -1;
for (const declaration of symbol.declarations) {
if (declaration.kind === SyntaxKind.ClassDeclaration || declaration.kind === SyntaxKind.InterfaceDeclaration) {
const typeParameterNodes = (<ClassDeclaration | InterfaceDeclaration>declaration).typeParameters;
const numTypeParameters = typeParameterNodes ? typeParameterNodes.length : 0;
if (maxTypeArgumentCount === -1) {
// For the first declaration, establish the initial maximum and
// minimum type argument counts. These only change when we
// encounter default type arguments.
maxTypeArgumentCount = numTypeParameters;
minTypeArgumentCount = numTypeParameters;
}
if (typeParameterNodes) {
if (!typeParameters) {
typeParameters = [];
}
for (let i = 0; i < typeParameterNodes.length; i++) {
if (typeParameterNodes[i].default) {
// When we encounter a type parameter with a default, establish
// new minimum and maximum type arguments if necessary.
if (minTypeArgumentCount > i) {
minTypeArgumentCount = i;
}
if (maxTypeArgumentCount < i + 1) {
maxTypeArgumentCount = i + 1;
}
}
if (typeParameters.length <= i) {
// When we encounter a new type parameter at this position,
// get the declared type for the type parameter. If another
// declaration attempts to establish a type parameter with a
// different name or constraint than the first one we find,
// we will report an error when checking the type parameters.
typeParameters[i] = getDeclaredTypeOfTypeParameter(getSymbolOfNode(typeParameterNodes[i]));
}
}
}
}
}
if (maxTypeArgumentCount === -1) {
maxTypeArgumentCount = 0;
}
if (minTypeArgumentCount === -1) {
minTypeArgumentCount = maxTypeArgumentCount;
}
if (typeParameters && typeParameters.length > maxTypeArgumentCount) {
// Trim the type parameters to the maximum length
typeParameters.length = maxTypeArgumentCount;
}
links.typeParameters = typeParameters || emptyArray;
links.minTypeArgumentCount = minTypeArgumentCount;
}
}
@ -18654,36 +18748,6 @@ namespace ts {
return kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor;
}
function areTypeParametersIdentical(list1: TypeParameterDeclaration[], list2: TypeParameterDeclaration[]) {
if (!list1 && !list2) {
return true;
}
if (!list1 || !list2 || list1.length !== list2.length) {
return false;
}
// TypeScript 1.0 spec (April 2014):
// When a generic interface has multiple declarations, all declarations must have identical type parameter
// lists, i.e. identical type parameter names with identical constraints in identical order.
for (let i = 0; i < list1.length; i++) {
const tp1 = list1[i];
const tp2 = list2[i];
if (tp1.name.text !== tp2.name.text) {
return false;
}
if (tp1.constraint || tp2.constraint) {
if (!tp1.constraint || !tp2.constraint || !isTypeIdenticalTo(getTypeFromTypeNode(tp1.constraint), getTypeFromTypeNode(tp2.constraint))) {
return false;
}
}
if (tp1.default || tp2.default) {
if (!tp1.default || !tp2.default || !isTypeIdenticalTo(getTypeFromTypeNode(tp1.default), getTypeFromTypeNode(tp2.default))) {
return false;
}
}
}
return true;
}
function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean {
const baseTypes = getBaseTypes(type);
if (baseTypes.length < 2) {

View file

@ -24,6 +24,20 @@ namespace ts {
return undefined;
}
export function findDeclaration<T extends Declaration>(symbol: Symbol, predicate: (node: Declaration) => node is T): T | undefined;
export function findDeclaration(symbol: Symbol, predicate: (node: Declaration) => boolean): Declaration | undefined;
export function findDeclaration(symbol: Symbol, predicate: (node: Declaration) => boolean): Declaration | undefined {
const declarations = symbol.declarations;
if (declarations) {
for (const declaration of declarations) {
if (predicate(declaration)) {
return declaration;
}
}
}
return undefined;
}
export interface StringSymbolWriter extends SymbolWriter {
string(): string;
}

View file

@ -86,6 +86,11 @@ const i03c02 = (<i03<number, number>>x).a;
const i03c03 = (<i03<1, 1>>x).a;
const i03c04 = (<i03<number, 1>>x).a;
interface i04 {}
interface i04<T> {}
interface i04<T = number> {}
interface i04<T = number, U = string> {}
interface Base01<T> { a: T; }
interface Base01Constructor { new <T = number>(a?: T): Base01<T>; }
@ -293,6 +298,14 @@ declare const i03c01: [1, 1];
declare const i03c02: [number, number];
declare const i03c03: [1, 1];
declare const i03c04: [number, 1];
interface i04 {
}
interface i04<T> {
}
interface i04<T = number> {
}
interface i04<T = number, U = string> {
}
interface Base01<T> {
a: T;
}

View file

@ -408,81 +408,97 @@ const i03c04 = (<i03<number, 1>>x).a;
>x : Symbol(x, Decl(genericDefaults.ts, 0, 13))
>a : Symbol(i03.a, Decl(genericDefaults.ts, 80, 50))
interface i04 {}
>i04 : Symbol(i04, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 87, 16), Decl(genericDefaults.ts, 88, 19), Decl(genericDefaults.ts, 89, 28))
interface i04<T> {}
>i04 : Symbol(i04, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 87, 16), Decl(genericDefaults.ts, 88, 19), Decl(genericDefaults.ts, 89, 28))
>T : Symbol(T, Decl(genericDefaults.ts, 88, 14), Decl(genericDefaults.ts, 89, 14), Decl(genericDefaults.ts, 90, 14))
interface i04<T = number> {}
>i04 : Symbol(i04, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 87, 16), Decl(genericDefaults.ts, 88, 19), Decl(genericDefaults.ts, 89, 28))
>T : Symbol(T, Decl(genericDefaults.ts, 88, 14), Decl(genericDefaults.ts, 89, 14), Decl(genericDefaults.ts, 90, 14))
interface i04<T = number, U = string> {}
>i04 : Symbol(i04, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 87, 16), Decl(genericDefaults.ts, 88, 19), Decl(genericDefaults.ts, 89, 28))
>T : Symbol(T, Decl(genericDefaults.ts, 88, 14), Decl(genericDefaults.ts, 89, 14), Decl(genericDefaults.ts, 90, 14))
>U : Symbol(U, Decl(genericDefaults.ts, 90, 25))
interface Base01<T> { a: T; }
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
>T : Symbol(T, Decl(genericDefaults.ts, 87, 17))
>a : Symbol(Base01.a, Decl(genericDefaults.ts, 87, 21))
>T : Symbol(T, Decl(genericDefaults.ts, 87, 17))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
>T : Symbol(T, Decl(genericDefaults.ts, 92, 17))
>a : Symbol(Base01.a, Decl(genericDefaults.ts, 92, 21))
>T : Symbol(T, Decl(genericDefaults.ts, 92, 17))
interface Base01Constructor { new <T = number>(a?: T): Base01<T>; }
>Base01Constructor : Symbol(Base01Constructor, Decl(genericDefaults.ts, 87, 29))
>T : Symbol(T, Decl(genericDefaults.ts, 88, 35))
>a : Symbol(a, Decl(genericDefaults.ts, 88, 47))
>T : Symbol(T, Decl(genericDefaults.ts, 88, 35))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
>T : Symbol(T, Decl(genericDefaults.ts, 88, 35))
>Base01Constructor : Symbol(Base01Constructor, Decl(genericDefaults.ts, 92, 29))
>T : Symbol(T, Decl(genericDefaults.ts, 93, 35))
>a : Symbol(a, Decl(genericDefaults.ts, 93, 47))
>T : Symbol(T, Decl(genericDefaults.ts, 93, 35))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
>T : Symbol(T, Decl(genericDefaults.ts, 93, 35))
declare const Base01: Base01Constructor;
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
>Base01Constructor : Symbol(Base01Constructor, Decl(genericDefaults.ts, 87, 29))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
>Base01Constructor : Symbol(Base01Constructor, Decl(genericDefaults.ts, 92, 29))
const Base01c00 = new Base01();
>Base01c00 : Symbol(Base01c00, Decl(genericDefaults.ts, 91, 5))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
>Base01c00 : Symbol(Base01c00, Decl(genericDefaults.ts, 96, 5))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
const Base01c01 = new Base01(1);
>Base01c01 : Symbol(Base01c01, Decl(genericDefaults.ts, 92, 5))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
>Base01c01 : Symbol(Base01c01, Decl(genericDefaults.ts, 97, 5))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
const Base01c02 = new Base01<number>();
>Base01c02 : Symbol(Base01c02, Decl(genericDefaults.ts, 93, 5))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
>Base01c02 : Symbol(Base01c02, Decl(genericDefaults.ts, 98, 5))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
const Base01c03 = new Base01<number>(1);
>Base01c03 : Symbol(Base01c03, Decl(genericDefaults.ts, 94, 5))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
>Base01c03 : Symbol(Base01c03, Decl(genericDefaults.ts, 99, 5))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
declare class Derived01<T> extends Base01<T> { }
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 94, 40))
>T : Symbol(T, Decl(genericDefaults.ts, 96, 24))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
>T : Symbol(T, Decl(genericDefaults.ts, 96, 24))
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 99, 40))
>T : Symbol(T, Decl(genericDefaults.ts, 101, 24))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
>T : Symbol(T, Decl(genericDefaults.ts, 101, 24))
const Derived01c00 = new Derived01();
>Derived01c00 : Symbol(Derived01c00, Decl(genericDefaults.ts, 97, 5))
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 94, 40))
>Derived01c00 : Symbol(Derived01c00, Decl(genericDefaults.ts, 102, 5))
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 99, 40))
const Derived01c01 = new Derived01(1);
>Derived01c01 : Symbol(Derived01c01, Decl(genericDefaults.ts, 98, 5))
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 94, 40))
>Derived01c01 : Symbol(Derived01c01, Decl(genericDefaults.ts, 103, 5))
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 99, 40))
const Derived01c02 = new Derived01<number>();
>Derived01c02 : Symbol(Derived01c02, Decl(genericDefaults.ts, 99, 5))
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 94, 40))
>Derived01c02 : Symbol(Derived01c02, Decl(genericDefaults.ts, 104, 5))
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 99, 40))
const Derived01c03 = new Derived01<number>(1);
>Derived01c03 : Symbol(Derived01c03, Decl(genericDefaults.ts, 100, 5))
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 94, 40))
>Derived01c03 : Symbol(Derived01c03, Decl(genericDefaults.ts, 105, 5))
>Derived01 : Symbol(Derived01, Decl(genericDefaults.ts, 99, 40))
declare class Derived02<T = string> extends Base01<T> { }
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 100, 46))
>T : Symbol(T, Decl(genericDefaults.ts, 102, 24))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 85, 37), Decl(genericDefaults.ts, 90, 13))
>T : Symbol(T, Decl(genericDefaults.ts, 102, 24))
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 105, 46))
>T : Symbol(T, Decl(genericDefaults.ts, 107, 24))
>Base01 : Symbol(Base01, Decl(genericDefaults.ts, 90, 40), Decl(genericDefaults.ts, 95, 13))
>T : Symbol(T, Decl(genericDefaults.ts, 107, 24))
const Derived02c00 = new Derived02();
>Derived02c00 : Symbol(Derived02c00, Decl(genericDefaults.ts, 103, 5))
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 100, 46))
>Derived02c00 : Symbol(Derived02c00, Decl(genericDefaults.ts, 108, 5))
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 105, 46))
const Derived02c01 = new Derived02(1);
>Derived02c01 : Symbol(Derived02c01, Decl(genericDefaults.ts, 104, 5))
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 100, 46))
>Derived02c01 : Symbol(Derived02c01, Decl(genericDefaults.ts, 109, 5))
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 105, 46))
const Derived02c02 = new Derived02<number>();
>Derived02c02 : Symbol(Derived02c02, Decl(genericDefaults.ts, 105, 5))
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 100, 46))
>Derived02c02 : Symbol(Derived02c02, Decl(genericDefaults.ts, 110, 5))
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 105, 46))
const Derived02c03 = new Derived02<number>(1);
>Derived02c03 : Symbol(Derived02c03, Decl(genericDefaults.ts, 106, 5))
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 100, 46))
>Derived02c03 : Symbol(Derived02c03, Decl(genericDefaults.ts, 111, 5))
>Derived02 : Symbol(Derived02, Decl(genericDefaults.ts, 105, 46))

View file

@ -536,6 +536,22 @@ const i03c04 = (<i03<number, 1>>x).a;
>x : any
>a : [number, 1]
interface i04 {}
>i04 : i04<T, U>
interface i04<T> {}
>i04 : i04<T, U>
>T : T
interface i04<T = number> {}
>i04 : i04<T, U>
>T : T
interface i04<T = number, U = string> {}
>i04 : i04<T, U>
>T : T
>U : U
interface Base01<T> { a: T; }
>Base01 : Base01<T>
>T : T

View file

@ -93,38 +93,38 @@ tests/cases/compiler/genericDefaultsErrors.ts(42,15): error TS2707: Generic type
~~~~~~~~~~~~~~~~~
!!! error TS2346: Supplied parameters do not match any signature of call target.
interface i00<T> { }
interface i00<T = number> { }
interface i00<T> { } // ok
interface i00<U = number> { } // error
~~~
!!! error TS2428: All declarations of 'i00' must have identical type parameters.
interface i01<T = number> { }
interface i01<T = string> { }
interface i01<T = number> { } // ok
interface i01<T = string> { } // error
~~~
!!! error TS2428: All declarations of 'i01' must have identical type parameters.
interface i02<T = T> { }
interface i02<T = T> { } // error
~
!!! error TS2706: Type parameter 'T' has a circular default.
interface i03<T = U, U = T> { }
interface i03<T = U, U = T> { } // error
~
!!! error TS2706: Type parameter 'T' has a circular default.
~
!!! error TS2706: Type parameter 'U' has a circular default.
interface i04<T = U, U> { }
interface i04<T = U, U> { } // error
~
!!! error TS2705: Required type parameters may not follow optional type parameters
interface i05<T extends string = number> { }
interface i05<T extends string = number> { } // error
~~~~~~
!!! error TS2344: Type 'number' does not satisfy the constraint 'string'.
interface i06<T extends string, U extends number = T> { }
interface i06<T extends string, U extends number = T> { } // error
~
!!! error TS2344: Type 'T' does not satisfy the constraint 'number'.
!!! error TS2344: Type 'string' is not assignable to type 'number'.
interface i07<T, U extends number = T> { }
interface i07<T, U extends number = T> { } // error
~
!!! error TS2344: Type 'T' does not satisfy the constraint 'number'.
interface i08<T, U extends T = number> { }
interface i08<T, U extends T = number> { } // error
~~~~~~
!!! error TS2344: Type 'number' does not satisfy the constraint 'T'.

View file

@ -21,19 +21,19 @@ f11<1, 2>(); // ok
f11<1, 2, 3>(); // ok
f11<1, 2, 3, 4>(); // error
interface i00<T> { }
interface i00<T = number> { }
interface i00<T> { } // ok
interface i00<U = number> { } // error
interface i01<T = number> { }
interface i01<T = string> { }
interface i01<T = number> { } // ok
interface i01<T = string> { } // error
interface i02<T = T> { }
interface i03<T = U, U = T> { }
interface i04<T = U, U> { }
interface i05<T extends string = number> { }
interface i06<T extends string, U extends number = T> { }
interface i07<T, U extends number = T> { }
interface i08<T, U extends T = number> { }
interface i02<T = T> { } // error
interface i03<T = U, U = T> { } // error
interface i04<T = U, U> { } // error
interface i05<T extends string = number> { } // error
interface i06<T extends string, U extends number = T> { } // error
interface i07<T, U extends number = T> { } // error
interface i08<T, U extends T = number> { } // error
interface i09<T, U, V = number> { }
type i09t00 = i09; // error
@ -74,7 +74,7 @@ declare function f10<T = U, U = T & {
declare function f11<T, U, V = number>(): void;
interface i00<T> {
}
interface i00<T = number> {
interface i00<U = number> {
}
interface i01<T = number> {
}

View file

@ -86,6 +86,11 @@ const i03c02 = (<i03<number, number>>x).a;
const i03c03 = (<i03<1, 1>>x).a;
const i03c04 = (<i03<number, 1>>x).a;
interface i04 {}
interface i04<T> {}
interface i04<T = number> {}
interface i04<T = number, U = string> {}
interface Base01<T> { a: T; }
interface Base01Constructor { new <T = number>(a?: T): Base01<T>; }

View file

@ -21,19 +21,19 @@ f11<1, 2>(); // ok
f11<1, 2, 3>(); // ok
f11<1, 2, 3, 4>(); // error
interface i00<T> { }
interface i00<T = number> { }
interface i00<T> { } // ok
interface i00<U = number> { } // error
interface i01<T = number> { }
interface i01<T = string> { }
interface i01<T = number> { } // ok
interface i01<T = string> { } // error
interface i02<T = T> { }
interface i03<T = U, U = T> { }
interface i04<T = U, U> { }
interface i05<T extends string = number> { }
interface i06<T extends string, U extends number = T> { }
interface i07<T, U extends number = T> { }
interface i08<T, U extends T = number> { }
interface i02<T = T> { } // error
interface i03<T = U, U = T> { } // error
interface i04<T = U, U> { } // error
interface i05<T extends string = number> { } // error
interface i06<T extends string, U extends number = T> { } // error
interface i07<T, U extends number = T> { } // error
interface i08<T, U extends T = number> { } // error
interface i09<T, U, V = number> { }
type i09t00 = i09; // error