diff --git a/console.ts b/console.ts index 908ccdc4da57de..29fc2d0de6fe07 100644 --- a/console.ts +++ b/console.ts @@ -1,6 +1,17 @@ // tslint:disable-next-line:no-any type ConsoleContext = Set; +// tslint:disable-next-line:no-any +function getClassInstanceName(instance: any): string { + if (typeof instance !== "object") { + return ""; + } + if (instance && instance.__proto__ && instance.__proto__.constructor) { + return instance.__proto__.constructor.name; // could be "Object" or "Array" + } + return ""; +} + // tslint:disable-next-line:no-any function stringify(ctx: ConsoleContext, value: any): string { switch (typeof value) { @@ -9,8 +20,12 @@ function stringify(ctx: ConsoleContext, value: any): string { case "number": case "boolean": case "undefined": + case "symbol": return String(value); case "function": + if (value.name && value.name !== "anonymous") { // from MDN spec + return `[Function: ${value.name}]`; + } return "[Function]"; case "object": if (value === null) { @@ -22,30 +37,51 @@ function stringify(ctx: ConsoleContext, value: any): string { } ctx.add(value); - const valStrings = []; + let entries: string[] = []; if (Array.isArray(value)) { for (const el of value) { - valStrings.push(stringify(ctx, el)); + entries.push(stringify(ctx, el)); } ctx.delete(value); - if (valStrings.length === 0) { + if (entries.length === 0) { return "[]"; } - return `[ ${valStrings.join(", ")} ]`; + return `[ ${entries.join(", ")} ]`; } else { + let baseString = ""; + + const className = getClassInstanceName(value); + let shouldShowClassName = false; + if (className && className !== "Object" && className !== "anonymous") { + shouldShowClassName = true; + } + for (const key of Object.keys(value)) { - valStrings.push(`${key}: ${stringify(ctx, value[key])}`); + const keyVal = value[key]; + // do not show methods of class instance + if (!shouldShowClassName || typeof keyVal !== "function") { + entries.push(`${key}: ${stringify(ctx, value[key])}`); + } } + entries = entries.filter(e => !!e); // remove empty entries + ctx.delete(value); - if (valStrings.length === 0) { - return "{}"; + if (entries.length === 0) { + baseString = "{}"; + } else { + baseString = `{ ${entries.join(", ")} }`; } - return `{ ${valStrings.join(", ")} }`; + + if (shouldShowClassName) { + baseString = `${className} ${baseString}`; + } + + return baseString; } default: return "[Not Implemented]"; diff --git a/tests.ts b/tests.ts index 0b250227bddc01..b3521d40ec9122 100644 --- a/tests.ts +++ b/tests.ts @@ -69,6 +69,16 @@ test(function tests_console_assert() { }); test(function tests_console_stringify_circular() { + class Base { + a = 1; + m1() {} + } + + class Extended extends Base { + b = 2; + m2() {} + } + // tslint:disable-next-line:no-any const nestedObj: any = { num: 1, @@ -77,6 +87,8 @@ test(function tests_console_stringify_circular() { method() {}, un: undefined, nu: null, + arrowFunc: () => {}, + extendedClass: new Extended(), }; const circularObj = { @@ -89,6 +101,7 @@ test(function tests_console_stringify_circular() { nested: nestedObj, emptyObj: {}, arr: [1, "s", false, null, nestedObj], + baseClass: new Base(), }; nestedObj.o = circularObj;