Skip to content

Commit ad9c148

Browse files
authored
Merge pull request #11931 from Microsoft/release-2.1_fixDeclarationEmitTypeAlaisWithTypeParam
[Release-2.1] Fix declaration emit when using type parameters
2 parents 467f252 + f9a317e commit ad9c148

20 files changed

+425
-9
lines changed

src/compiler/checker.ts

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,15 +2199,32 @@ namespace ts {
21992199
// The specified symbol flags need to be reinterpreted as type flags
22002200
buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags);
22012201
}
2202-
else if (!(flags & TypeFormatFlags.InTypeAlias) && ((getObjectFlags(type) & ObjectFlags.Anonymous && !(<AnonymousType>type).target) || type.flags & TypeFlags.UnionOrIntersection) && type.aliasSymbol &&
2202+
else if (!(flags & TypeFormatFlags.InTypeAlias) &&
2203+
(getObjectFlags(type) & ObjectFlags.Anonymous && !(<AnonymousType>type).target || type.flags & TypeFlags.UnionOrIntersection) &&
2204+
type.aliasSymbol &&
22032205
isSymbolAccessible(type.aliasSymbol, enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) {
2204-
// We emit inferred type as type-alias at the current localtion if all the following is true
2205-
// the input type is has alias symbol that is accessible
2206-
// the input type is a union, intersection or anonymous type that is fully instantiated (if not we want to keep dive into)
2207-
// e.g.: export type Bar<X, Y> = () => [X, Y];
2208-
// export type Foo<Y> = Bar<any, Y>;
2209-
// export const y = (x: Foo<string>) => 1 // we want to emit as ...x: () => [any, string])
2210-
const typeArguments = type.aliasTypeArguments;
2206+
// 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.
2207+
// However, if the type is an anonymous type with type arguments, we need to perform additional check.
2208+
// 1) No type arguments, just emit type-alias as is
2209+
// 2) Existed type arguments, check if the type arguments full fill all type parameters of the alias-symbol by
2210+
// checking whether the target's aliasTypeArguments has the same size as type's aliasTypeArguments:
2211+
// i.e
2212+
// type Foo<T> = {
2213+
// foo<U>(): Foo<U>
2214+
// };
2215+
// function foo() {
2216+
// return {} as Foo<number>;
2217+
// }
2218+
// Should be emitted as
2219+
// declare type Foo<T> = {
2220+
// foo<U>(): Foo<U>;
2221+
// };
2222+
// declare function foo(): Foo<number>;
2223+
// Otherwise type-alias is point to another generic type-alias then don't write it using alias symbol
2224+
// export type Bar<X, Y> = () => [X, Y];
2225+
// export type Foo<Y> = Bar<any, Y>;
2226+
// export const y = (x: Foo<string>) => 1 // this should be emit as "export declare const y: (x: () => [any, string]) => number;"
2227+
const typeArguments = (<AnonymousType>type).aliasTypeArguments;
22112228
writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, typeArguments ? typeArguments.length : 0, nextFlags);
22122229
}
22132230
else if (type.flags & TypeFlags.UnionOrIntersection) {
@@ -2330,7 +2347,9 @@ namespace ts {
23302347
else if (contains(symbolStack, symbol)) {
23312348
// If type is an anonymous type literal in a type alias declaration, use type alias name
23322349
const typeAlias = getTypeAliasForTypeLiteral(type);
2333-
if (typeAlias) {
2350+
// 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)
2351+
// If it is a generic type-alias just write out "any"
2352+
if (typeAlias && !(<AnonymousType>type).target) {
23342353
// The specified symbol flags need to be reinterpreted as type flags
23352354
buildSymbolDisplay(typeAlias, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags);
23362355
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [declarationEmitTypeAliasWithTypeParameters2.ts]
2+
3+
export type Bar<X, Y, Z> = () => [X, Y, Z];
4+
export type Baz<M, N> = Bar<M, string, N>;
5+
export type Baa<Y> = Baz<boolean, Y>;
6+
export const y = (x: Baa<number>) => 1
7+
8+
//// [declarationEmitTypeAliasWithTypeParameters2.js]
9+
"use strict";
10+
exports.y = function (x) { return 1; };
11+
12+
13+
//// [declarationEmitTypeAliasWithTypeParameters2.d.ts]
14+
export declare type Bar<X, Y, Z> = () => [X, Y, Z];
15+
export declare type Baz<M, N> = Bar<M, string, N>;
16+
export declare type Baa<Y> = Baz<boolean, Y>;
17+
export declare const y: (x: () => [boolean, string, number]) => number;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters2.ts ===
2+
3+
export type Bar<X, Y, Z> = () => [X, Y, Z];
4+
>Bar : Symbol(Bar, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 0, 0))
5+
>X : Symbol(X, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 16))
6+
>Y : Symbol(Y, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 18))
7+
>Z : Symbol(Z, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 21))
8+
>X : Symbol(X, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 16))
9+
>Y : Symbol(Y, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 18))
10+
>Z : Symbol(Z, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 21))
11+
12+
export type Baz<M, N> = Bar<M, string, N>;
13+
>Baz : Symbol(Baz, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 43))
14+
>M : Symbol(M, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 2, 16))
15+
>N : Symbol(N, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 2, 18))
16+
>Bar : Symbol(Bar, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 0, 0))
17+
>M : Symbol(M, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 2, 16))
18+
>N : Symbol(N, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 2, 18))
19+
20+
export type Baa<Y> = Baz<boolean, Y>;
21+
>Baa : Symbol(Baa, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 2, 42))
22+
>Y : Symbol(Y, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 3, 16))
23+
>Baz : Symbol(Baz, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 1, 43))
24+
>Y : Symbol(Y, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 3, 16))
25+
26+
export const y = (x: Baa<number>) => 1
27+
>y : Symbol(y, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 4, 12))
28+
>x : Symbol(x, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 4, 18))
29+
>Baa : Symbol(Baa, Decl(declarationEmitTypeAliasWithTypeParameters2.ts, 2, 42))
30+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters2.ts ===
2+
3+
export type Bar<X, Y, Z> = () => [X, Y, Z];
4+
>Bar : Bar<X, Y, Z>
5+
>X : X
6+
>Y : Y
7+
>Z : Z
8+
>X : X
9+
>Y : Y
10+
>Z : Z
11+
12+
export type Baz<M, N> = Bar<M, string, N>;
13+
>Baz : () => [M, string, N]
14+
>M : M
15+
>N : N
16+
>Bar : Bar<X, Y, Z>
17+
>M : M
18+
>N : N
19+
20+
export type Baa<Y> = Baz<boolean, Y>;
21+
>Baa : () => [boolean, string, Y]
22+
>Y : Y
23+
>Baz : () => [M, string, N]
24+
>Y : Y
25+
26+
export const y = (x: Baa<number>) => 1
27+
>y : (x: () => [boolean, string, number]) => number
28+
>(x: Baa<number>) => 1 : (x: () => [boolean, string, number]) => number
29+
>x : () => [boolean, string, number]
30+
>Baa : () => [boolean, string, Y]
31+
>1 : 1
32+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//// [declarationEmitTypeAliasWithTypeParameters3.ts]
2+
3+
type Foo<T> = {
4+
foo<U>(): Foo<U>
5+
};
6+
function bar() {
7+
return {} as Foo<number>;
8+
}
9+
10+
11+
//// [declarationEmitTypeAliasWithTypeParameters3.js]
12+
function bar() {
13+
return {};
14+
}
15+
16+
17+
//// [declarationEmitTypeAliasWithTypeParameters3.d.ts]
18+
declare type Foo<T> = {
19+
foo<U>(): Foo<U>;
20+
};
21+
declare function bar(): {
22+
foo<U>(): any;
23+
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters3.ts ===
2+
3+
type Foo<T> = {
4+
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 0, 0))
5+
>T : Symbol(T, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 1, 9))
6+
7+
foo<U>(): Foo<U>
8+
>foo : Symbol(foo, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 1, 15))
9+
>U : Symbol(U, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 2, 8))
10+
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 0, 0))
11+
>U : Symbol(U, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 2, 8))
12+
13+
};
14+
function bar() {
15+
>bar : Symbol(bar, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 3, 2))
16+
17+
return {} as Foo<number>;
18+
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters3.ts, 0, 0))
19+
}
20+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters3.ts ===
2+
3+
type Foo<T> = {
4+
>Foo : Foo<T>
5+
>T : T
6+
7+
foo<U>(): Foo<U>
8+
>foo : <U>() => { foo<U>(): any; }
9+
>U : U
10+
>Foo : Foo<T>
11+
>U : U
12+
13+
};
14+
function bar() {
15+
>bar : () => { foo<U>(): any; }
16+
17+
return {} as Foo<number>;
18+
>{} as Foo<number> : { foo<U>(): any; }
19+
>{} : {}
20+
>Foo : Foo<T>
21+
}
22+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//// [declarationEmitTypeAliasWithTypeParameters4.ts]
2+
3+
type Foo<T, Y> = {
4+
foo<U, J>(): Foo<U, J>
5+
};
6+
type SubFoo<R> = Foo<string, R>;
7+
8+
function foo() {
9+
return {} as SubFoo<number>;
10+
}
11+
12+
13+
//// [declarationEmitTypeAliasWithTypeParameters4.js]
14+
function foo() {
15+
return {};
16+
}
17+
18+
19+
//// [declarationEmitTypeAliasWithTypeParameters4.d.ts]
20+
declare type Foo<T, Y> = {
21+
foo<U, J>(): Foo<U, J>;
22+
};
23+
declare type SubFoo<R> = Foo<string, R>;
24+
declare function foo(): {
25+
foo<U, J>(): any;
26+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters4.ts ===
2+
3+
type Foo<T, Y> = {
4+
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 0, 0))
5+
>T : Symbol(T, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 1, 9))
6+
>Y : Symbol(Y, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 1, 11))
7+
8+
foo<U, J>(): Foo<U, J>
9+
>foo : Symbol(foo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 1, 18))
10+
>U : Symbol(U, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 2, 8))
11+
>J : Symbol(J, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 2, 10))
12+
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 0, 0))
13+
>U : Symbol(U, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 2, 8))
14+
>J : Symbol(J, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 2, 10))
15+
16+
};
17+
type SubFoo<R> = Foo<string, R>;
18+
>SubFoo : Symbol(SubFoo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 3, 2))
19+
>R : Symbol(R, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 4, 12))
20+
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 0, 0))
21+
>R : Symbol(R, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 4, 12))
22+
23+
function foo() {
24+
>foo : Symbol(foo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 4, 32))
25+
26+
return {} as SubFoo<number>;
27+
>SubFoo : Symbol(SubFoo, Decl(declarationEmitTypeAliasWithTypeParameters4.ts, 3, 2))
28+
}
29+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters4.ts ===
2+
3+
type Foo<T, Y> = {
4+
>Foo : Foo<T, Y>
5+
>T : T
6+
>Y : Y
7+
8+
foo<U, J>(): Foo<U, J>
9+
>foo : <U, J>() => { foo<U, J>(): any; }
10+
>U : U
11+
>J : J
12+
>Foo : Foo<T, Y>
13+
>U : U
14+
>J : J
15+
16+
};
17+
type SubFoo<R> = Foo<string, R>;
18+
>SubFoo : { foo<U, J>(): any; }
19+
>R : R
20+
>Foo : Foo<T, Y>
21+
>R : R
22+
23+
function foo() {
24+
>foo : () => { foo<U, J>(): any; }
25+
26+
return {} as SubFoo<number>;
27+
>{} as SubFoo<number> : { foo<U, J>(): any; }
28+
>{} : {}
29+
>SubFoo : { foo<U, J>(): any; }
30+
}
31+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters5.ts(5,25): error TS4081: Exported type alias 'SubFoo' has or is using private name 'Foo'.
2+
3+
4+
==== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters5.ts (1 errors) ====
5+
6+
type Foo<T, Y> = {
7+
foo<U, J>(): Foo<U, J>
8+
};
9+
export type SubFoo<R> = Foo<string, R>;
10+
~~~
11+
!!! error TS4081: Exported type alias 'SubFoo' has or is using private name 'Foo'.
12+
13+
function foo() {
14+
return {} as SubFoo<number>;
15+
}
16+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [declarationEmitTypeAliasWithTypeParameters5.ts]
2+
3+
type Foo<T, Y> = {
4+
foo<U, J>(): Foo<U, J>
5+
};
6+
export type SubFoo<R> = Foo<string, R>;
7+
8+
function foo() {
9+
return {} as SubFoo<number>;
10+
}
11+
12+
13+
//// [declarationEmitTypeAliasWithTypeParameters5.js]
14+
"use strict";
15+
function foo() {
16+
return {};
17+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//// [declarationEmitTypeAliasWithTypeParameters6.ts]
2+
3+
type Foo<T, Y> = {
4+
foo<U, J>(): Foo<U, J>
5+
};
6+
type SubFoo<R, S> = Foo<S, R>;
7+
8+
function foo() {
9+
return {} as SubFoo<number, string>;
10+
}
11+
12+
13+
//// [declarationEmitTypeAliasWithTypeParameters6.js]
14+
function foo() {
15+
return {};
16+
}
17+
18+
19+
//// [declarationEmitTypeAliasWithTypeParameters6.d.ts]
20+
declare type Foo<T, Y> = {
21+
foo<U, J>(): Foo<U, J>;
22+
};
23+
declare type SubFoo<R, S> = Foo<S, R>;
24+
declare function foo(): {
25+
foo<U, J>(): any;
26+
};
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
=== tests/cases/compiler/declarationEmitTypeAliasWithTypeParameters6.ts ===
2+
3+
type Foo<T, Y> = {
4+
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 0, 0))
5+
>T : Symbol(T, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 1, 9))
6+
>Y : Symbol(Y, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 1, 11))
7+
8+
foo<U, J>(): Foo<U, J>
9+
>foo : Symbol(foo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 1, 18))
10+
>U : Symbol(U, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 2, 8))
11+
>J : Symbol(J, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 2, 10))
12+
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 0, 0))
13+
>U : Symbol(U, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 2, 8))
14+
>J : Symbol(J, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 2, 10))
15+
16+
};
17+
type SubFoo<R, S> = Foo<S, R>;
18+
>SubFoo : Symbol(SubFoo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 3, 2))
19+
>R : Symbol(R, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 4, 12))
20+
>S : Symbol(S, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 4, 14))
21+
>Foo : Symbol(Foo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 0, 0))
22+
>S : Symbol(S, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 4, 14))
23+
>R : Symbol(R, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 4, 12))
24+
25+
function foo() {
26+
>foo : Symbol(foo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 4, 30))
27+
28+
return {} as SubFoo<number, string>;
29+
>SubFoo : Symbol(SubFoo, Decl(declarationEmitTypeAliasWithTypeParameters6.ts, 3, 2))
30+
}
31+

0 commit comments

Comments
 (0)