mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 11:35:42 -06:00
Fix emit inferred type which is a generic type-alias both fully and partially fill type parameters
This commit is contained in:
parent
467f252583
commit
2869f9cb05
@ -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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user