Appropriately parenthesize keyof and typeof queries in array types (#17272)

* Appropriately parenthesize keyof and typeof queries when they are array types

* Fix test and then the same bug in the symbol writer
This commit is contained in:
Wesley Wigham 2017-07-18 15:08:53 -07:00 committed by GitHub
parent 08a57d82cd
commit 8075353356
8 changed files with 52 additions and 15 deletions

View file

@ -3302,7 +3302,7 @@ namespace ts {
function writeTypeReference(type: TypeReference, flags: TypeFormatFlags) {
const typeArguments = type.typeArguments || emptyArray;
if (type.target === globalArrayType && !(flags & TypeFormatFlags.WriteArrayAsGenericType)) {
writeType(typeArguments[0], TypeFormatFlags.InElementType);
writeType(typeArguments[0], TypeFormatFlags.InElementType | TypeFormatFlags.InArrayType);
writePunctuation(writer, SyntaxKind.OpenBracketToken);
writePunctuation(writer, SyntaxKind.CloseBracketToken);
}
@ -3427,9 +3427,15 @@ namespace ts {
}
function writeTypeOfSymbol(type: ObjectType, typeFormatFlags?: TypeFormatFlags) {
if (typeFormatFlags & TypeFormatFlags.InArrayType) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
}
writeKeyword(writer, SyntaxKind.TypeOfKeyword);
writeSpace(writer);
buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Value, SymbolFormatFlags.None, typeFormatFlags);
if (typeFormatFlags & TypeFormatFlags.InArrayType) {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
}
function writePropertyWithModifiers(prop: Symbol) {

View file

@ -664,7 +664,7 @@ namespace ts {
export function createArrayTypeNode(elementType: TypeNode) {
const node = createSynthesizedNode(SyntaxKind.ArrayType) as ArrayTypeNode;
node.elementType = parenthesizeElementTypeMember(elementType);
node.elementType = parenthesizeArrayTypeMember(elementType);
return node;
}
@ -3898,6 +3898,15 @@ namespace ts {
return member;
}
export function parenthesizeArrayTypeMember(member: TypeNode) {
switch (member.kind) {
case SyntaxKind.TypeQuery:
case SyntaxKind.TypeOperator:
return createParenthesizedType(member);
}
return parenthesizeElementTypeMember(member);
}
export function parenthesizeElementTypeMembers(members: ReadonlyArray<TypeNode>) {
return createNodeArray(sameMap(members, parenthesizeElementTypeMember));
}

View file

@ -2685,6 +2685,7 @@ namespace ts {
SuppressAnyReturnType = 1 << 12, // If the return type is any-like, don't offer a return type.
AddUndefined = 1 << 13, // Add undefined to types of initialized, non-optional parameters
WriteClassExpressionAsTypeLiteral = 1 << 14, // Write a type literal instead of (Anonymous class)
InArrayType = 1 << 15, // Writing an array element type
}
export const enum SymbolFormatFlags {

View file

@ -18,13 +18,13 @@ interface IHasVisualizationModel {
var xs: IHasVisualizationModel[] = [moduleA];
>xs : IHasVisualizationModel[]
>IHasVisualizationModel : IHasVisualizationModel
>[moduleA] : typeof moduleA[]
>[moduleA] : (typeof moduleA)[]
>moduleA : typeof moduleA
var xs2: typeof moduleA[] = [moduleA];
>xs2 : typeof moduleA[]
>xs2 : (typeof moduleA)[]
>moduleA : typeof moduleA
>[moduleA] : typeof moduleA[]
>[moduleA] : (typeof moduleA)[]
>moduleA : typeof moduleA
=== tests/cases/compiler/aliasUsageInArray_backbone.ts ===

View file

@ -22,8 +22,8 @@ class C {
>foo : string
}
var y = [C, C];
>y : typeof C[]
>[C, C] : typeof C[]
>y : (typeof C)[]
>[C, C] : (typeof C)[]
>C : typeof C
>C : typeof C
@ -31,7 +31,7 @@ var r3 = new y[0]();
>r3 : C
>new y[0]() : C
>y[0] : typeof C
>y : typeof C[]
>y : (typeof C)[]
>0 : 0
var a: { (x: number): number; (x: string): string; };

View file

@ -1,16 +1,16 @@
=== tests/cases/compiler/declarationEmitIndexTypeArray.ts ===
function doSomethingWithKeys<T>(...keys: (keyof T)[]) { }
>doSomethingWithKeys : <T>(...keys: keyof T[]) => void
>doSomethingWithKeys : <T>(...keys: (keyof T)[]) => void
>T : T
>keys : keyof T[]
>keys : (keyof T)[]
>T : T
const utilityFunctions = {
>utilityFunctions : { doSomethingWithKeys: <T>(...keys: keyof T[]) => void; }
>{ doSomethingWithKeys} : { doSomethingWithKeys: <T>(...keys: keyof T[]) => void; }
>utilityFunctions : { doSomethingWithKeys: <T>(...keys: (keyof T)[]) => void; }
>{ doSomethingWithKeys} : { doSomethingWithKeys: <T>(...keys: (keyof T)[]) => void; }
doSomethingWithKeys
>doSomethingWithKeys : <T>(...keys: keyof T[]) => void
>doSomethingWithKeys : <T>(...keys: (keyof T)[]) => void
};

View file

@ -1,4 +1,4 @@
tests/cases/compiler/keyofIsLiteralContexualType.ts(5,9): error TS2322: Type '("a" | "b" | "c")[]' is not assignable to type 'keyof T[]'.
tests/cases/compiler/keyofIsLiteralContexualType.ts(5,9): error TS2322: Type '("a" | "b" | "c")[]' is not assignable to type '(keyof T)[]'.
Type '"a" | "b" | "c"' is not assignable to type 'keyof T'.
Type '"c"' is not assignable to type 'keyof T'.
Type '"c"' is not assignable to type '"a" | "b"'.
@ -12,7 +12,7 @@ tests/cases/compiler/keyofIsLiteralContexualType.ts(13,11): error TS2339: Proper
let a: (keyof T)[] = ["a", "b"];
let b: (keyof T)[] = ["a", "b", "c"];
~
!!! error TS2322: Type '("a" | "b" | "c")[]' is not assignable to type 'keyof T[]'.
!!! error TS2322: Type '("a" | "b" | "c")[]' is not assignable to type '(keyof T)[]'.
!!! error TS2322: Type '"a" | "b" | "c"' is not assignable to type 'keyof T'.
!!! error TS2322: Type '"c"' is not assignable to type 'keyof T'.
!!! error TS2322: Type '"c"' is not assignable to type '"a" | "b"'.

View file

@ -0,0 +1,21 @@
/// <reference path='fourslash.ts'/>
// @Filename: keyof.ts
//// function doSomethingWithKeys<T>(...keys: (keyof T)[]) { }
////
//// const /*1*/utilityFunctions = {
//// doSomethingWithKeys
//// };
// @Filename: typeof.ts
//// class Foo { static a: number; }
//// function doSomethingWithTypes(...statics: (typeof Foo)[]) {}
////
//// const /*2*/utilityFunctions = {
//// doSomethingWithTypes
//// };
verify.quickInfos({
1: "const utilityFunctions: {\n doSomethingWithKeys: <T>(...keys: (keyof T)[]) => void;\n}",
2: "const utilityFunctions: {\n doSomethingWithTypes: (...statics: (typeof Foo)[]) => void;\n}"
});