Fix emit inferred type which is a generic type-alias both fully and partially fill type parameters

This commit is contained in:
Kanchalai Tanglertsampan 2016-10-28 16:38:53 -07:00
parent 467f252583
commit 2869f9cb05

View File

@ -2180,54 +2180,105 @@ namespace ts {
writer.writeKeyword(!(globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike) && isTypeAny(type)
? "any"
: (<IntrinsicType>type).intrinsicName);
return;
}
else if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) {
if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) {
if (inObjectTypeLiteral) {
writer.reportInaccessibleThisError();
}
writer.writeKeyword("this");
return;
}
else if (getObjectFlags(type) & ObjectFlags.Reference) {
if (getObjectFlags(type) & ObjectFlags.Reference) {
writeTypeReference(<TypeReference>type, nextFlags);
return;
}
else if (type.flags & TypeFlags.EnumLiteral) {
if (type.flags & TypeFlags.EnumLiteral) {
buildSymbolDisplay(getParentOfSymbol(type.symbol), writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags);
writePunctuation(writer, SyntaxKind.DotToken);
appendSymbolNameOnly(type.symbol, writer);
return;
}
else if (getObjectFlags(type) & ObjectFlags.ClassOrInterface || type.flags & (TypeFlags.Enum | TypeFlags.TypeParameter)) {
if (getObjectFlags(type) & ObjectFlags.ClassOrInterface || type.flags & (TypeFlags.Enum | TypeFlags.TypeParameter)) {
// The specified symbol flags need to be reinterpreted as type flags
buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags);
return;
}
else if (!(flags & TypeFormatFlags.InTypeAlias) && ((getObjectFlags(type) & ObjectFlags.Anonymous && !(<AnonymousType>type).target) || type.flags & TypeFlags.UnionOrIntersection) && type.aliasSymbol &&
if (!(flags & TypeFormatFlags.InTypeAlias) &&
(getObjectFlags(type) & ObjectFlags.Anonymous || 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])
// 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>;
const typeArguments = type.aliasTypeArguments;
writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, typeArguments ? typeArguments.length : 0, nextFlags);
if (!(<AnonymousType>type).target) {
// The given type has no target type then just write out its alias and tyep argument.
writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, typeArguments ? typeArguments.length : 0, nextFlags);
return;
}
else {
// The given type has target type. Then check hat the type contains same number of type arguments as its target type indicating that
// the given type has instantiate all type parameter. Then just emit type-alias using the current type argument.
// i.e
// type Foo<T> = { foo<U>(): Foo<U> }
// function bar() { return {} as Foo<number>;
// should be emitted as
// declare type Foo<T> = { foo<U>(): Foo<U>; }
// declare function bar(): Foo<number>
if (typeArguments && typeArguments.length === (<AnonymousType>type).target.aliasTypeArguments.length) {
writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, typeArguments.length, nextFlags);
return;
}
// Otherwise type-alias only partially full fill type parameter (as below example), we will want to serialize the type-alias
// 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;"
}
}
else if (type.flags & TypeFlags.UnionOrIntersection) {
if (type.flags & TypeFlags.UnionOrIntersection) {
writeUnionOrIntersectionType(<UnionOrIntersectionType>type, nextFlags);
return;
}
else if (getObjectFlags(type) & ObjectFlags.Anonymous) {
if (getObjectFlags(type) & ObjectFlags.Anonymous) {
writeAnonymousType(<ObjectType>type, nextFlags);
return;
}
else if (type.flags & TypeFlags.StringOrNumberLiteral) {
if (type.flags & TypeFlags.StringOrNumberLiteral) {
writer.writeStringLiteral(literalTypeToString(<LiteralType>type));
return;
}
else {
// Should never get here
// { ... }
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writeSpace(writer);
writePunctuation(writer, SyntaxKind.DotDotDotToken);
writeSpace(writer);
writePunctuation(writer, SyntaxKind.CloseBraceToken);
}
// Should never get here
// { ... }
writePunctuation(writer, SyntaxKind.OpenBraceToken);
writeSpace(writer);
writePunctuation(writer, SyntaxKind.DotDotDotToken);
writeSpace(writer);
writePunctuation(writer, SyntaxKind.CloseBraceToken);
}
function writeTypeList(types: Type[], delimiter: SyntaxKind) {
@ -2330,7 +2381,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);
}