diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 906074ccc70..3fba12bf66c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -942,10 +942,13 @@ module ts { writeTypeofSymbol(type); } // Use 'typeof T' for types of functions and methods that circularly reference themselves - // TODO(shkamat): correct the usuage of typeof function - always on functions that are visible - else if (type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && typeStack && contains(typeStack, type)) { + else if (shouldWriteTypeOfFunctionSymbol()) { writeTypeofSymbol(type); } + else if (typeStack && contains(typeStack, type)) { + // Recursive usage, use any + writer.write("any"); + } else { if (!typeStack) { typeStack = []; @@ -954,6 +957,23 @@ module ts { writeLiteralType(type, allowFunctionOrConstructorTypeLiteral); typeStack.pop(); } + + function shouldWriteTypeOfFunctionSymbol() { + if (type.symbol) { + var isStaticMethodSymbol = !!(type.symbol.flags & SymbolFlags.Method && // typeof static method + ts.forEach(type.symbol.declarations, declaration => declaration.flags & NodeFlags.Static)); + var isNonLocalFunctionSymbol = !!(type.symbol.flags & SymbolFlags.Function) && + (type.symbol.parent || // is exported function symbol + ts.forEach(type.symbol.declarations, declaration => + declaration.parent.kind === SyntaxKind.SourceFile || declaration.parent.kind === SyntaxKind.ModuleBlock)); + + if (isStaticMethodSymbol || isNonLocalFunctionSymbol) { + // typeof is allowed only for static/non local functions + return !!(flags & TypeFormatFlags.UseTypeOfFunction) || // use typeof if format flags specify it + (typeStack && contains(typeStack, type)); // it is type of the symbol uses itself recursively + } + } + } } function writeTypeofSymbol(type: ObjectType) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 57124e0f026..bb182e9a812 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2160,7 +2160,7 @@ module ts { if (node.constraint && (node.parent.kind !== SyntaxKind.Method || !(node.parent.flags & NodeFlags.Private))) { write(" extends "); getSymbolVisibilityDiagnosticMessage = getTypeParameterConstraintVisibilityError; - resolver.writeTypeAtLocation(node.constraint, enclosingDeclaration, TypeFormatFlags.None, writer); + resolver.writeTypeAtLocation(node.constraint, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer); } } @@ -2179,7 +2179,7 @@ module ts { function emitTypeOfTypeReference(node: Node) { getSymbolVisibilityDiagnosticMessage = getHeritageClauseVisibilityError; - resolver.writeTypeAtLocation(node, enclosingDeclaration, TypeFormatFlags.WriteArrayAsGenericType, writer); + resolver.writeTypeAtLocation(node, enclosingDeclaration, TypeFormatFlags.WriteArrayAsGenericType | TypeFormatFlags.UseTypeOfFunction, writer); function getHeritageClauseVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult) { var diagnosticMessage: DiagnosticMessage; @@ -2304,7 +2304,7 @@ module ts { if (!(node.flags & NodeFlags.Private)) { write(": "); getSymbolVisibilityDiagnosticMessage = getVariableDeclarationTypeVisibilityError; - resolver.writeTypeAtLocation(node, enclosingDeclaration, TypeFormatFlags.None, writer); + resolver.writeTypeAtLocation(node, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer); } } @@ -2362,7 +2362,7 @@ module ts { if (!(node.flags & NodeFlags.Private)) { write(": "); getSymbolVisibilityDiagnosticMessage = getAccessorDeclarationTypeVisibilityError; - resolver.writeTypeAtLocation(node, enclosingDeclaration, TypeFormatFlags.None, writer); + resolver.writeTypeAtLocation(node, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer); } write(";"); writeLine(); @@ -2459,7 +2459,7 @@ module ts { if (node.kind !== SyntaxKind.Constructor && !(node.flags & NodeFlags.Private)) { write(": "); getSymbolVisibilityDiagnosticMessage = getReturnTypeVisibilityError; - resolver.writeReturnTypeOfSignatureDeclaration(node, enclosingDeclaration, TypeFormatFlags.None, writer); + resolver.writeReturnTypeOfSignatureDeclaration(node, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer); } write(";"); writeLine(); @@ -2534,7 +2534,7 @@ module ts { if (!(node.parent.flags & NodeFlags.Private)) { write(": "); getSymbolVisibilityDiagnosticMessage = getParameterDeclarationTypeVisibilityError; - resolver.writeTypeAtLocation(node, enclosingDeclaration, TypeFormatFlags.None, writer); + resolver.writeTypeAtLocation(node, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer); } function getParameterDeclarationTypeVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5e882740f1d..e507567a577 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -626,6 +626,8 @@ module ts { /** writes Array instead T[] */ WriteArrayAsGenericType = 0x00000001, // Declarations + + UseTypeOfFunction = 0x00000002, // instead of writing signature type of function use typeof } export enum SymbolAccessibility { diff --git a/tests/baselines/reference/commentsClassMembers.js b/tests/baselines/reference/commentsClassMembers.js index 23360404b5e..999ba787bd9 100644 --- a/tests/baselines/reference/commentsClassMembers.js +++ b/tests/baselines/reference/commentsClassMembers.js @@ -487,11 +487,11 @@ declare var i1_ncf: (b: number) => number; declare var i1_ncr: number; declare var i1_ncprop: number; declare var i1_s_p: number; -declare var i1_s_f: (b: number) => number; +declare var i1_s_f: typeof c1.s2; declare var i1_s_r: number; declare var i1_s_prop: number; declare var i1_s_nc_p: number; -declare var i1_s_ncf: (b: number) => number; +declare var i1_s_ncf: typeof c1.nc_s2; declare var i1_s_ncr: number; declare var i1_s_ncprop: number; declare var i1_c: typeof c1; diff --git a/tests/baselines/reference/declFileGenericType.js b/tests/baselines/reference/declFileGenericType.js index 2be231c1381..815b8712f15 100644 --- a/tests/baselines/reference/declFileGenericType.js +++ b/tests/baselines/reference/declFileGenericType.js @@ -131,10 +131,10 @@ export declare module C { } } export declare var a: C.A; -export declare var b: (x: T) => C.A; -export declare var c: (x: T) => C.A; -export declare var d: (x: T) => C.A[]; -export declare var e: >(x: T) => C.A[]; +export declare var b: typeof C.F; +export declare var c: typeof C.F2; +export declare var d: typeof C.F3; +export declare var e: typeof C.F4; export declare var x: C.A; export declare function f>(): void; export declare var g: C.A; @@ -142,4 +142,4 @@ export declare class h extends C.A { } export interface i extends C.A { } -export declare var j: >(x: T) => T; +export declare var j: typeof C.F6; diff --git a/tests/baselines/reference/declFileTypeofFunction.js b/tests/baselines/reference/declFileTypeofFunction.js index 66517ca4a94..f488d418f6d 100644 --- a/tests/baselines/reference/declFileTypeofFunction.js +++ b/tests/baselines/reference/declFileTypeofFunction.js @@ -64,39 +64,15 @@ function foo5(x) { //// [declFileTypeofFunction.d.ts] -declare function f(n: { - (n: typeof f): string; - (n: { - (n: typeof g): number; - (n: typeof f): number; - }): string; -}): string; -declare function f(n: { - (n: typeof g): number; - (n: { - (n: typeof f): string; - (n: typeof g): string; - }): number; -}): string; -declare function g(n: { - (n: typeof g): number; - (n: { - (n: typeof f): string; - (n: typeof g): string; - }): number; -}): number; -declare function g(n: { - (n: typeof f): string; - (n: { - (n: typeof g): number; - (n: typeof f): number; - }): string; -}): number; +declare function f(n: typeof f): string; +declare function f(n: typeof g): string; +declare function g(n: typeof g): number; +declare function g(n: typeof f): number; declare var b: any; -declare function b1(): () => typeof b1; -declare function foo(): () => typeof foo; -declare var foo1: () => typeof foo; -declare var foo2: () => typeof foo; +declare function b1(): typeof b1; +declare function foo(): typeof foo; +declare var foo1: typeof foo; +declare var foo2: typeof foo; declare var foo3: any; declare var x: any; declare function foo5(x: number): (x: number) => number; diff --git a/tests/baselines/reference/declarationEmit_nameConflicts.js b/tests/baselines/reference/declarationEmit_nameConflicts.js index 70b14ad66a6..c4070db5a87 100644 --- a/tests/baselines/reference/declarationEmit_nameConflicts.js +++ b/tests/baselines/reference/declarationEmit_nameConflicts.js @@ -28,7 +28,6 @@ export module M.P { export interface I { } } export import im = M.P.f; - // Bug 887180: Invalid .d.ts when an aliased entity is referenced, and a different entity is closer in scope export var a = M.a; // emitted incorrectly as typeof f export var b = M.b; // ok export var c = M.c; // ok @@ -170,10 +169,10 @@ export declare module M.P { } } export import im = M.P.f; - var a: () => void; + var a: typeof M.f; var b: typeof M.C; var c: typeof M.N; - var g: () => void; + var g: typeof c.g; var d: typeof M.d; } export declare module M.Q { diff --git a/tests/baselines/reference/declarationEmit_nameConflicts2.js b/tests/baselines/reference/declarationEmit_nameConflicts2.js index bc8ba2f63e9..79d73add32d 100644 --- a/tests/baselines/reference/declarationEmit_nameConflicts2.js +++ b/tests/baselines/reference/declarationEmit_nameConflicts2.js @@ -9,7 +9,6 @@ module X.Y.base { } module X.Y.base.Z { - // Bug 887180 export var f = X.Y.base.f; // Should be base.f export var C = X.Y.base.C; // Should be base.C export var M = X.Y.base.M; // Should be base.M @@ -72,7 +71,7 @@ declare module X.Y.base { } } declare module X.Y.base.Z { - var f: () => void; + var f: typeof base.f; var C: typeof base.C; var M: typeof base.M; var E: typeof base.E; diff --git a/tests/baselines/reference/declarationEmit_nameConflicts3.js b/tests/baselines/reference/declarationEmit_nameConflicts3.js index cfec6264a77..c7abf129404 100644 --- a/tests/baselines/reference/declarationEmit_nameConflicts3.js +++ b/tests/baselines/reference/declarationEmit_nameConflicts3.js @@ -111,7 +111,7 @@ declare module M.P { f = 0, } var v: M.D; - var w: () => void; - var x: () => void; - var x: () => void; + var w: typeof D.f; + var x: typeof C.f; + var x: typeof C.f; } diff --git a/tests/baselines/reference/funcdecl.js b/tests/baselines/reference/funcdecl.js index 40eb8dde17b..de080873b9f 100644 --- a/tests/baselines/reference/funcdecl.js +++ b/tests/baselines/reference/funcdecl.js @@ -134,29 +134,26 @@ var f2 = function () { //// [funcdecl.d.ts] declare function simpleFunc(): string; -declare var simpleFuncVar: () => string; +declare var simpleFuncVar: typeof simpleFunc; declare function anotherFuncNoReturn(): void; -declare var anotherFuncNoReturnVar: () => void; +declare var anotherFuncNoReturnVar: typeof anotherFuncNoReturn; declare function withReturn(): string; -declare var withReturnVar: () => string; +declare var withReturnVar: typeof withReturn; declare function withParams(a: string): string; -declare var withparamsVar: (a: string) => string; +declare var withparamsVar: typeof withParams; declare function withMultiParams(a: number, b: any, c: Object): number; -declare var withMultiParamsVar: (a: number, b: any, c: Object) => number; +declare var withMultiParamsVar: typeof withMultiParams; declare function withOptionalParams(a?: string): void; -declare var withOptionalParamsVar: (a?: string) => void; +declare var withOptionalParamsVar: typeof withOptionalParams; declare function withInitializedParams(a: string, b0: any, b?: number, c?: string): void; -declare var withInitializedParamsVar: (a: string, b0: any, b?: number, c?: string) => void; +declare var withInitializedParamsVar: typeof withInitializedParams; declare function withOptionalInitializedParams(a: string, c?: string): void; -declare var withOptionalInitializedParamsVar: (a: string, c?: string) => void; +declare var withOptionalInitializedParamsVar: typeof withOptionalInitializedParams; declare function withRestParams(a: string, ...myRestParameter: number[]): number[]; -declare var withRestParamsVar: (a: string, ...myRestParameter: number[]) => number[]; +declare var withRestParamsVar: typeof withRestParams; declare function overload1(n: number): string; declare function overload1(s: string): string; -declare var withOverloadSignature: { - (n: number): string; - (s: string): string; -}; +declare var withOverloadSignature: typeof overload1; declare function f(n: () => void): void; declare module m2 { function foo(n: () => void): void; diff --git a/tests/baselines/reference/functionExpressionReturningItself.errors.txt b/tests/baselines/reference/functionExpressionReturningItself.errors.txt deleted file mode 100644 index 2cfefecf9eb..00000000000 --- a/tests/baselines/reference/functionExpressionReturningItself.errors.txt +++ /dev/null @@ -1,4 +0,0 @@ -==== tests/cases/compiler/functionExpressionReturningItself.ts (1 errors) ==== - var x = function somefn() { return somefn; }; - ~ -!!! Exported variable 'x' has or is using private name 'somefn'. \ No newline at end of file diff --git a/tests/baselines/reference/functionExpressionReturningItself.js b/tests/baselines/reference/functionExpressionReturningItself.js index f80e9325871..8553d2da867 100644 --- a/tests/baselines/reference/functionExpressionReturningItself.js +++ b/tests/baselines/reference/functionExpressionReturningItself.js @@ -5,3 +5,7 @@ var x = function somefn() { return somefn; }; var x = function somefn() { return somefn; }; + + +//// [functionExpressionReturningItself.d.ts] +declare var x: () => any; diff --git a/tests/baselines/reference/functionReturningItself.js b/tests/baselines/reference/functionReturningItself.js index e50215a524b..7d161241899 100644 --- a/tests/baselines/reference/functionReturningItself.js +++ b/tests/baselines/reference/functionReturningItself.js @@ -10,4 +10,4 @@ function somefn() { //// [functionReturningItself.d.ts] -declare function somefn(): () => typeof somefn; +declare function somefn(): typeof somefn; diff --git a/tests/baselines/reference/internalAliasFunction.js b/tests/baselines/reference/internalAliasFunction.js index e46526f1f8b..3930c6cb801 100644 --- a/tests/baselines/reference/internalAliasFunction.js +++ b/tests/baselines/reference/internalAliasFunction.js @@ -33,6 +33,7 @@ declare module a { function foo(x: number): number; } declare module c { + import b = a.foo; var bVal: number; - var bVal2: (x: number) => number; + var bVal2: typeof b; } diff --git a/tests/baselines/reference/internalAliasFunctionInsideLocalModuleWithExport.js b/tests/baselines/reference/internalAliasFunctionInsideLocalModuleWithExport.js index a0a61453735..eb758b913e6 100644 --- a/tests/baselines/reference/internalAliasFunctionInsideLocalModuleWithExport.js +++ b/tests/baselines/reference/internalAliasFunctionInsideLocalModuleWithExport.js @@ -35,5 +35,5 @@ export declare module a { export declare module c { export import b = a.foo; var bVal: number; - var bVal2: (x: number) => number; + var bVal2: typeof b; } diff --git a/tests/baselines/reference/internalAliasFunctionInsideLocalModuleWithoutExport.js b/tests/baselines/reference/internalAliasFunctionInsideLocalModuleWithoutExport.js index 113396205ba..0423a587e84 100644 --- a/tests/baselines/reference/internalAliasFunctionInsideLocalModuleWithoutExport.js +++ b/tests/baselines/reference/internalAliasFunctionInsideLocalModuleWithoutExport.js @@ -33,5 +33,6 @@ export declare module a { function foo(x: number): number; } export declare module c { - var bVal2: (x: number) => number; + import b = a.foo; + var bVal2: typeof b; } diff --git a/tests/baselines/reference/internalAliasFunctionInsideTopLevelModuleWithExport.js b/tests/baselines/reference/internalAliasFunctionInsideTopLevelModuleWithExport.js index 1213bbbbe26..7fe09d1e02b 100644 --- a/tests/baselines/reference/internalAliasFunctionInsideTopLevelModuleWithExport.js +++ b/tests/baselines/reference/internalAliasFunctionInsideTopLevelModuleWithExport.js @@ -31,4 +31,4 @@ export declare module a { } export import b = a.foo; export declare var bVal: number; -export declare var bVal2: (x: number) => number; +export declare var bVal2: typeof b; diff --git a/tests/baselines/reference/internalAliasFunctionInsideTopLevelModuleWithoutExport.js b/tests/baselines/reference/internalAliasFunctionInsideTopLevelModuleWithoutExport.js index af546a20b5b..c049bfcf858 100644 --- a/tests/baselines/reference/internalAliasFunctionInsideTopLevelModuleWithoutExport.js +++ b/tests/baselines/reference/internalAliasFunctionInsideTopLevelModuleWithoutExport.js @@ -27,5 +27,6 @@ exports.bVal2 = b; export declare module a { function foo(x: number): number; } +import b = a.foo; export declare var bVal: number; -export declare var bVal2: (x: number) => number; +export declare var bVal2: typeof b; diff --git a/tests/baselines/reference/privacyCheckTypeOfFunction.errors.txt b/tests/baselines/reference/privacyCheckTypeOfFunction.errors.txt new file mode 100644 index 00000000000..50b9dc0242b --- /dev/null +++ b/tests/baselines/reference/privacyCheckTypeOfFunction.errors.txt @@ -0,0 +1,10 @@ +==== tests/cases/compiler/privacyCheckTypeOfFunction.ts (2 errors) ==== + function foo() { + } + export var x: typeof foo; + ~ +!!! Exported variable 'x' has or is using private name 'foo'. + export var b = foo; + ~ +!!! Exported variable 'b' has or is using private name 'foo'. + \ No newline at end of file diff --git a/tests/baselines/reference/privacyCheckTypeOfFunction.js b/tests/baselines/reference/privacyCheckTypeOfFunction.js index 04063c62991..757416c691b 100644 --- a/tests/baselines/reference/privacyCheckTypeOfFunction.js +++ b/tests/baselines/reference/privacyCheckTypeOfFunction.js @@ -10,8 +10,3 @@ function foo() { } exports.x; exports.b = foo; - - -//// [privacyCheckTypeOfFunction.d.ts] -export declare var x: () => void; -export declare var b: () => void; diff --git a/tests/cases/compiler/declarationEmit_nameConflicts.ts b/tests/cases/compiler/declarationEmit_nameConflicts.ts index 49218d2ef1e..66eb732ddae 100644 --- a/tests/cases/compiler/declarationEmit_nameConflicts.ts +++ b/tests/cases/compiler/declarationEmit_nameConflicts.ts @@ -28,7 +28,6 @@ export module M.P { export interface I { } } export import im = M.P.f; - // Bug 887180: Invalid .d.ts when an aliased entity is referenced, and a different entity is closer in scope export var a = M.a; // emitted incorrectly as typeof f export var b = M.b; // ok export var c = M.c; // ok diff --git a/tests/cases/compiler/declarationEmit_nameConflicts2.ts b/tests/cases/compiler/declarationEmit_nameConflicts2.ts index d7dd5134e96..f0112fa9501 100644 --- a/tests/cases/compiler/declarationEmit_nameConflicts2.ts +++ b/tests/cases/compiler/declarationEmit_nameConflicts2.ts @@ -9,7 +9,6 @@ module X.Y.base { } module X.Y.base.Z { - // Bug 887180 export var f = X.Y.base.f; // Should be base.f export var C = X.Y.base.C; // Should be base.C export var M = X.Y.base.M; // Should be base.M