Merge pull request #11931 from Microsoft/release-2.1_fixDeclarationEmitTypeAlaisWithTypeParam

[Release-2.1] Fix declaration emit when using type parameters
This commit is contained in:
Yui 2016-10-31 13:18:32 -07:00 committed by GitHub
commit ad9c148168
20 changed files with 425 additions and 9 deletions

View File

@ -2199,15 +2199,32 @@ namespace ts {
// The specified symbol flags need to be reinterpreted as type flags
buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags);
}
else if (!(flags & TypeFormatFlags.InTypeAlias) && ((getObjectFlags(type) & ObjectFlags.Anonymous && !(<AnonymousType>type).target) || type.flags & TypeFlags.UnionOrIntersection) && type.aliasSymbol &&
else if (!(flags & TypeFormatFlags.InTypeAlias) &&
(getObjectFlags(type) & ObjectFlags.Anonymous && !(<AnonymousType>type).target || type.flags & TypeFlags.UnionOrIntersection) &&
type.aliasSymbol &&
isSymbolAccessible(type.aliasSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) {
// We emit inferred type as type-alias at the current localtion if all the following is true
// the input type is has alias symbol that is accessible
// the input type is a union, intersection or anonymous type that is fully instantiated (if not we want to keep dive into)
// e.g.: export type Bar<X, Y> = () => [X, Y];
// export type Foo<Y> = Bar<any, Y>;
// export const y = (x: Foo<string>) => 1 // we want to emit as ...x: () => [any, string])
const typeArguments = type.aliasTypeArguments;
// We emit inferred type as type-alias if type is not in type-alias declaration, existed accessible alias-symbol, type is anonymous or union or intersection.
// However, if the type is an anonymous type with type arguments, we need to perform additional check.
// 1) No type arguments, just emit type-alias as is
// 2) Existed type arguments, check if the type arguments full fill all type parameters of the alias-symbol by
// checking whether the target's aliasTypeArguments has the same size as type's aliasTypeArguments:
// i.e
// type Foo<T> = {
// foo<U>(): Foo<U>
// };
// function foo() {
// return {} as Foo<number>;
// }
// Should be emitted as
// declare type Foo<T> = {
// foo<U>(): Foo<U>;
// };
// declare function foo(): Foo<number>;
// Otherwise type-alias is point to another generic type-alias then don't write it using alias symbol
// export type Bar<X, Y> = () => [X, Y];
// export type Foo<Y> = Bar<any, Y>;
// export const y = (x: Foo<string>) => 1 // this should be emit as "export declare const y: (x: () => [any, string]) => number;"
const typeArguments = (<AnonymousType>type).aliasTypeArguments;
writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, typeArguments ? typeArguments.length : 0, nextFlags);
}
else if (type.flags & TypeFlags.UnionOrIntersection) {
@ -2330,7 +2347,9 @@ namespace ts {
else if (contains(symbolStack, symbol)) {
// If type is an anonymous type literal in a type alias declaration, use type alias name
const typeAlias = getTypeAliasForTypeLiteral(type);
if (typeAlias) {
// We only want to use type-alias here if the typeAlias is not a generic one. (i.e it doesn't have a target type)
// If it is a generic type-alias just write out "any"
if (typeAlias && !(<AnonymousType>type).target) {
// The specified symbol flags need to be reinterpreted as type flags
buildSymbolDisplay(typeAlias, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags);
}

View File

@ -0,0 +1,17 @@
//// [declarationEmitTypeAliasWithTypeParameters2.ts]
export type Bar<X, Y, Z> = () => [X, Y, Z];
export type Baz<M, N> = Bar<M, string, N>;
export type Baa<Y> = Baz<boolean, Y>;
export const y = (x: Baa<number>) => 1
//// [declarationEmitTypeAliasWithTypeParameters2.js]
"use strict";
exports.y = function (x) { return 1; };
//// [declarationEmitTypeAliasWithTypeParameters2.d.ts]
export declare type Bar<X, Y, Z> = () => [X, Y, Z];
export declare type Baz<M, N> = Bar<M, string, N>;
export declare type Baa<Y> = Baz<boolean, Y>;
export declare const y: (x: () => [boolean, string, number]) => number;

View File

@ -0,0 +1,30 @@
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters2.ts ===
export type Bar<X, Y, Z> = () => [X, Y, Z];
>Bar : Symbol(Bar, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 0, 0))
>X : Symbol(X, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 16))
>Y : Symbol(Y, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 18))
>Z : Symbol(Z, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 21))
>X : Symbol(X, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 16))
>Y : Symbol(Y, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 18))
>Z : Symbol(Z, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 21))
export type Baz<M, N> = Bar<M, string, N>;
>Baz : Symbol(Baz, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 43))
>M : Symbol(M, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 2, 16))
>N : Symbol(N, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 2, 18))
>Bar : Symbol(Bar, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 0, 0))
>M : Symbol(M, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 2, 16))
>N : Symbol(N, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 2, 18))
export type Baa<Y> = Baz<boolean, Y>;
>Baa : Symbol(Baa, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 2, 42))
>Y : Symbol(Y, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 3, 16))
>Baz : Symbol(Baz, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 43))
>Y : Symbol(Y, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 3, 16))
export const y = (x: Baa<number>) => 1
>y : Symbol(y, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 4, 12))
>x : Symbol(x, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 4, 18))
>Baa : Symbol(Baa, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 2, 42))

View File

@ -0,0 +1,32 @@
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters2.ts ===
export type Bar<X, Y, Z> = () => [X, Y, Z];
>Bar : Bar<X, Y, Z>
>X : X
>Y : Y
>Z : Z
>X : X
>Y : Y
>Z : Z
export type Baz<M, N> = Bar<M, string, N>;
>Baz : () => [M, string, N]
>M : M
>N : N
>Bar : Bar<X, Y, Z>
>M : M
>N : N
export type Baa<Y> = Baz<boolean, Y>;
>Baa : () => [boolean, string, Y]
>Y : Y
>Baz : () => [M, string, N]
>Y : Y
export const y = (x: Baa<number>) => 1
>y : (x: () => [boolean, string, number]) => number
>(x: Baa<number>) => 1 : (x: () => [boolean, string, number]) => number
>x : () => [boolean, string, number]
>Baa : () => [boolean, string, Y]
>1 : 1

View File

@ -0,0 +1,23 @@
//// [declarationEmitTypeAliasWithTypeParameters3.ts]
type Foo<T> = {
foo<U>(): Foo<U>
};
function bar() {
return {} as Foo<number>;
}
//// [declarationEmitTypeAliasWithTypeParameters3.js]
function bar() {
return {};
}
//// [declarationEmitTypeAliasWithTypeParameters3.d.ts]
declare type Foo<T> = {
foo<U>(): Foo<U>;
};
declare function bar(): {
foo<U>(): any;
};

View File

@ -0,0 +1,20 @@
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters3.ts ===
type Foo<T> = {
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 0, 0))
>T : Symbol(T, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 1, 9))
foo<U>(): Foo<U>
>foo : Symbol(foo, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 1, 15))
>U : Symbol(U, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 2, 8))
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 0, 0))
>U : Symbol(U, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 2, 8))
};
function bar() {
>bar : Symbol(bar, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 3, 2))
return {} as Foo<number>;
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 0, 0))
}

View File

@ -0,0 +1,22 @@
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters3.ts ===
type Foo<T> = {
>Foo : Foo<T>
>T : T
foo<U>(): Foo<U>
>foo : <U>() => { foo<U>(): any; }
>U : U
>Foo : Foo<T>
>U : U
};
function bar() {
>bar : () => { foo<U>(): any; }
return {} as Foo<number>;
>{} as Foo<number> : { foo<U>(): any; }
>{} : {}
>Foo : Foo<T>
}

View File

@ -0,0 +1,26 @@
//// [declarationEmitTypeAliasWithTypeParameters4.ts]
type Foo<T, Y> = {
foo<U, J>(): Foo<U, J>
};
type SubFoo<R> = Foo<string, R>;
function foo() {
return {} as SubFoo<number>;
}
//// [declarationEmitTypeAliasWithTypeParameters4.js]
function foo() {
return {};
}
//// [declarationEmitTypeAliasWithTypeParameters4.d.ts]
declare type Foo<T, Y> = {
foo<U, J>(): Foo<U, J>;
};
declare type SubFoo<R> = Foo<string, R>;
declare function foo(): {
foo<U, J>(): any;
};

View File

@ -0,0 +1,29 @@
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters4.ts ===
type Foo<T, Y> = {
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 0, 0))
>T : Symbol(T, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 1, 9))
>Y : Symbol(Y, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 1, 11))
foo<U, J>(): Foo<U, J>
>foo : Symbol(foo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 1, 18))
>U : Symbol(U, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 2, 8))
>J : Symbol(J, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 2, 10))
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 0, 0))
>U : Symbol(U, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 2, 8))
>J : Symbol(J, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 2, 10))
};
type SubFoo<R> = Foo<string, R>;
>SubFoo : Symbol(SubFoo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 3, 2))
>R : Symbol(R, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 4, 12))
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 0, 0))
>R : Symbol(R, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 4, 12))
function foo() {
>foo : Symbol(foo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 4, 32))
return {} as SubFoo<number>;
>SubFoo : Symbol(SubFoo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 3, 2))
}

View File

@ -0,0 +1,31 @@
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters4.ts ===
type Foo<T, Y> = {
>Foo : Foo<T, Y>
>T : T
>Y : Y
foo<U, J>(): Foo<U, J>
>foo : <U, J>() => { foo<U, J>(): any; }
>U : U
>J : J
>Foo : Foo<T, Y>
>U : U
>J : J
};
type SubFoo<R> = Foo<string, R>;
>SubFoo : { foo<U, J>(): any; }
>R : R
>Foo : Foo<T, Y>
>R : R
function foo() {
>foo : () => { foo<U, J>(): any; }
return {} as SubFoo<number>;
>{} as SubFoo<number> : { foo<U, J>(): any; }
>{} : {}
>SubFoo : { foo<U, J>(): any; }
}

View File

@ -0,0 +1,16 @@
tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters5.ts(5,25): error TS4081: Exported type alias 'SubFoo' has or is using private name 'Foo'.
==== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters5.ts (1 errors) ====
type Foo<T, Y> = {
foo<U, J>(): Foo<U, J>
};
export type SubFoo<R> = Foo<string, R>;
~~~
!!! error TS4081: Exported type alias 'SubFoo' has or is using private name 'Foo'.
function foo() {
return {} as SubFoo<number>;
}

View File

@ -0,0 +1,17 @@
//// [declarationEmitTypeAliasWithTypeParameters5.ts]
type Foo<T, Y> = {
foo<U, J>(): Foo<U, J>
};
export type SubFoo<R> = Foo<string, R>;
function foo() {
return {} as SubFoo<number>;
}
//// [declarationEmitTypeAliasWithTypeParameters5.js]
"use strict";
function foo() {
return {};
}

View File

@ -0,0 +1,26 @@
//// [declarationEmitTypeAliasWithTypeParameters6.ts]
type Foo<T, Y> = {
foo<U, J>(): Foo<U, J>
};
type SubFoo<R, S> = Foo<S, R>;
function foo() {
return {} as SubFoo<number, string>;
}
//// [declarationEmitTypeAliasWithTypeParameters6.js]
function foo() {
return {};
}
//// [declarationEmitTypeAliasWithTypeParameters6.d.ts]
declare type Foo<T, Y> = {
foo<U, J>(): Foo<U, J>;
};
declare type SubFoo<R, S> = Foo<S, R>;
declare function foo(): {
foo<U, J>(): any;
};

View File

@ -0,0 +1,31 @@
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters6.ts ===
type Foo<T, Y> = {
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 0, 0))
>T : Symbol(T, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 1, 9))
>Y : Symbol(Y, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 1, 11))
foo<U, J>(): Foo<U, J>
>foo : Symbol(foo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 1, 18))
>U : Symbol(U, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 2, 8))
>J : Symbol(J, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 2, 10))
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 0, 0))
>U : Symbol(U, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 2, 8))
>J : Symbol(J, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 2, 10))
};
type SubFoo<R, S> = Foo<S, R>;
>SubFoo : Symbol(SubFoo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 3, 2))
>R : Symbol(R, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 4, 12))
>S : Symbol(S, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 4, 14))
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 0, 0))
>S : Symbol(S, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 4, 14))
>R : Symbol(R, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 4, 12))
function foo() {
>foo : Symbol(foo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 4, 30))
return {} as SubFoo<number, string>;
>SubFoo : Symbol(SubFoo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 3, 2))
}

View File

@ -0,0 +1,33 @@
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters6.ts ===
type Foo<T, Y> = {
>Foo : Foo<T, Y>
>T : T
>Y : Y
foo<U, J>(): Foo<U, J>
>foo : <U, J>() => { foo<U, J>(): any; }
>U : U
>J : J
>Foo : Foo<T, Y>
>U : U
>J : J
};
type SubFoo<R, S> = Foo<S, R>;
>SubFoo : { foo<U, J>(): any; }
>R : R
>S : S
>Foo : Foo<T, Y>
>S : S
>R : R
function foo() {
>foo : () => { foo<U, J>(): any; }
return {} as SubFoo<number, string>;
>{} as SubFoo<number, string> : { foo<U, J>(): any; }
>{} : {}
>SubFoo : { foo<U, J>(): any; }
}

View File

@ -0,0 +1,6 @@
// @declaration: true
export type Bar<X, Y, Z> = () => [X, Y, Z];
export type Baz<M, N> = Bar<M, string, N>;
export type Baa<Y> = Baz<boolean, Y>;
export const y = (x: Baa<number>) => 1

View File

@ -0,0 +1,8 @@
// @declaration: true
type Foo<T> = {
foo<U>(): Foo<U>
};
function bar() {
return {} as Foo<number>;
}

View File

@ -0,0 +1,10 @@
// @declaration: true
type Foo<T, Y> = {
foo<U, J>(): Foo<U, J>
};
type SubFoo<R> = Foo<string, R>;
function foo() {
return {} as SubFoo<number>;
}

View File

@ -0,0 +1,10 @@
// @declaration: true
type Foo<T, Y> = {
foo<U, J>(): Foo<U, J>
};
export type SubFoo<R> = Foo<string, R>;
function foo() {
return {} as SubFoo<number>;
}

View File

@ -0,0 +1,10 @@
// @declaration: true
type Foo<T, Y> = {
foo<U, J>(): Foo<U, J>
};
type SubFoo<R, S> = Foo<S, R>;
function foo() {
return {} as SubFoo<number, string>;
}