Skip to content

[Release-2.1] Fix declaration emit when using type parameters #11931

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -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))

Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -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;
};
Original file line number Diff line number Diff line change
@@ -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))
}

Original file line number Diff line number Diff line change
@@ -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>
}

Original file line number Diff line number Diff line change
@@ -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;
};
Original file line number Diff line number Diff line change
@@ -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))
}

Original file line number Diff line number Diff line change
@@ -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; }
}

Original file line number Diff line number Diff line change
@@ -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>;
}

Original file line number Diff line number Diff line change
@@ -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 {};
}
Original file line number Diff line number Diff line change
@@ -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;
};
Original file line number Diff line number Diff line change
@@ -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))
}

Loading