Repository navigation
Bloomberg feedback for 7.0 beta #63824
Description
This document summarizes observed tsgo differences compared with tsc in the Bloomberg TypeScript code base. The differences in output are numerous but they are either fixed or they are small enough not to be significant.
There are a small number of type checking differences that we reported previously that still impact the beta as well. These are included for completeness, but they impact a small number of packages so migration should not be held back by these.
All issues affect a small number of packages.
Summary
| # | Area | Severity | Issue | Classification | GitHub ticket |
|---|---|---|---|---|---|
| 1 | Declaration emit | Medium | Extra _N suffix on object-literal method type parameters |
Bug | #3968 |
| 2 | Declaration emit | Medium | Methods are not marked readonly in as const assertions |
Bug | #3959 |
| 3 | Declaration emit | Medium | Emojis are emitted as Unicode escapes | Bug | #3966 |
| 4 | Declaration emit | Medium | JSDoc preserved from removed import | Bug | #3955 |
| 5 | Declaration emit | Medium | JSDoc removed on private async functions | Bug | #3954 |
| 6 | Declaration emit | Low | Expando properties on functions are not merged into a single namespace | Intentional | #3970 |
| 7 | Declaration emit | Low | Parentheses added around typeof access |
Bug | #3961 |
| 8 | Declaration emit | Low | Empty JSDoc /***/ is removed |
Bug | #3956 |
| 9 | Declaration emit | Low | Getters/setters preserved even when they do not need to be | Intentional | #3958 |
| 10 | Declaration emit | Low | Quoted properties are sometimes preserved for one level | Intentional | #3957 |
| 11 | Declaration emit | Low | Removal of undefined from optional fields |
Improvement | |
| 12 | Declaration emit | Low | Removal of trailing comma in destructuring parameters | Improvement | |
| 13 | Declaration emit | Low | String literal delimiters are preserved | Improvement | |
| 14 | JS emit | Medium | Assertion causes JSX spread object properties to emit on one line | Bug | #3971 |
| 15 | JS emit | Low | Parentheses removed around TS instantiation expression emit | Improvement | |
| 16 | JS emit | Low | JSX property names no longer quoted unnecessarily | Improvement | |
| 17 | Source maps | Low | Better mapping of identifiers in some cases | Improvement | |
| 18 | Type checking | Low | ts-ignore et all cause new errors | Won't Fix | |
| 19 | Type checking | Low | New error when inferring return type from similar object types with promises | Won't Fix | #1743 |
| 20 | Type checking | Low | this is no longer any in functions of object literal assigned to prototype |
Won't Fix | #1782 |
| 21 | Type checking | Low | Duplicate identifier error in type with computed property names | Improvement | #1740 |
| 22 | Type checking | Low | Inference fails form unions of arrays of different depths (T[] | T[][]) |
Bug | #1789 |
| 23 | Type checking | Low | Duplicate identifier reported between class decl and function prop assignment | Won't Fix | #1783 |
Declaration: extra _N suffix on object-literal method type parameters
GitHub ticket: #3968
Repro: Playground
In object literals, function type parameters get an extra _N suffix even if they do not need it.
Input:
export const o = {
foo<M extends string>(): void { },
bar<M extends string>(): void { },
};tsc:
export declare const o: {
foo<M extends string>(): void;
bar<M extends string>(): void;
};tsgo:
export declare const o: {
foo<M extends string>(): void;
bar<M_1 extends string>(): void;
};Declaration: methods are not marked readonly in as const assertions
GitHub ticket: #3959
Repro: Playground
Methods are not marked as readonly in as const assertions.
Input:
export const OptionUtil = {
makeTimeout(timeout: number) {
return RspUtil.makeTimeout(timeout);
},
} as const;tsc:
export declare const OptionUtil: {
readonly makeTimeout: (timeout: number) => any;
};tsgo:
export declare const OptionUtil: {
makeTimeout(timeout: number): any;
};Declaration: emojis are emitted as Unicode escapes
GitHub ticket: #3966
Repro: Playground
Emojis are printed as Unicode escapes instead of original characters.
Input:
export const IconEmojis = {
alert_low: "⚠️",
} as consttsc:
export declare const IconEmojis: {
readonly alert_low: "⚠️";
};tsgo:
export declare const IconEmojis: {
readonly alert_low: "\u26A0\uFE0F";
};Declaration: JSDoc preserved from removed import
GitHub ticket: #3955
Repro: Playground
JSDoc is preserved from a removed import statement.
Input:
/**
* Some random docs not related to foo
*/
/* ignore next */
import * as x from './index.js';
export const foo = 1;tsc:
export declare const foo = 1;tsgo:
/**
* Some random docs not related to foo
*/
export declare const foo = 1;Declaration: JSDoc removed on private async functions
GitHub ticket: #3954
Repro: Playground
JSDoc is removed from private async functions.
Input:
export class Test {
/**
* Non Async function
*/
private nonAsyncFunction(): void {
}
/**
* Async function
*/
private async asyncFunction(): Promise<void> {
}
}tsc:
export declare class Test {
/**
* Non Async function
*/
private nonAsyncFunction;
/**
* Async function
*/
private asyncFunction;
}tsgo:
export declare class Test {
/**
* Non Async function
*/
private nonAsyncFunction;
private asyncFunction;
}Declaration: expando properties on functions are not merged into a single namespace
GitHub ticket: #3970
Repro: Playground
Resolution: Intentional
Expando properties on functions are not merged into a single namespace.
Input:
export function not() {}
not.negate = () => {};
not.toString = () => ' ~ ';tsc:
export declare function not(): void;
export declare namespace not {
var negate: () => void;
var toString: () => string;
}tsgo:
export declare function not(): void;
export declare namespace not {
var negate: () => void;
}
export declare namespace not {
var toString: () => string;
}Declaration: parentheses added around typeof access
GitHub ticket: #3961
Repro: Playground
Parentheses are always added around typeof, diverging from source.
Input:
const C = { A: 1 }
export type C = typeof C[keyof typeof C];tsc:
export type C = typeof C[keyof typeof C];tsgo:
export type C = (typeof C)[keyof typeof C];Declaration: empty JSDoc /***/ is removed
GitHub ticket: #3956
Repro: Playground
Empty JSDoc comment (/***/) is removed.
Input:
/***/
export const foo = 1;tsc:
/***/
export declare const foo = 1;tsgo:
export declare const foo = 1;Declaration: getters/setters preserved even when they do not need to be
GitHub ticket: #3958
Repro: Playground
Resolution: Intentional
get/set are preserved even when they do not have to be preserved.
Input:
export const u = {
get foo(): string | null {
return ""
},
set foo(val: string | null) {
},
}tsc:
export declare const u: {
foo: string | null;
};tsgo:
export declare const u: {
get foo(): string | null;
set foo(val: string | null);
};Declaration: quoted properties are sometimes preserved for one level
GitHub ticket: #3957
Repro: Playground
Resolution: Intentional
Quoted properties of inferred types are preserved starting one level down (they are not preserved at the top level of a type).
Input:
function createSlice<T>(o: T) {
return o
}
export const platformSlice = createSlice({
"query": {
"search" : "",
"user": ""
},
});tsc:
export declare const platformSlice: {
query: {
search: string;
user: string;
};
};tsgo:
export declare const platformSlice: {
query: {
"search": string;
"user": string;
};
};Declaration: removal of undefined from optional fields
Repro: N/A
In some reprinted types, undefined is removed from the field type. This seems better since the originating field does not have undefined in the type.
tsc:
sessionId?: string | undefined;tsgo:
sessionId?: string;Declaration: removal of trailing comma in destructuring parameters
Repro: Playground
This seems like an improvement, even though it does diverge from source code a bit. The destructuring is reprinted on a single line anyway, so removing the , makes sense.
Input:
export function fn({
data,
}: {
data: string;
}) {}tsc:
export declare function fn({ data, }: {
data: string;
}): void;tsgo:
export declare function fn({ data }: {
data: string;
}): void;Declaration: string literal delimiters are preserved
Repro: N/A
In inferred types string literals are now more frequently preserved in output with the source delimiter.
Input:
readonly COMPLETE: 'COMPLETE';tsc:
readonly COMPLETE: "COMPLETE";tsgo:
readonly COMPLETE: 'COMPLETE';JS emit: assertion causes JSX spread object properties to emit on one line
GitHub ticket: #3971
Repro: Playground
The presence of an assertion results in properties being emitted on the same line in JSX spread.
Input:
<EntryTextDialog
{...{
foo: 1,
bar: 2 as any,
baz: 3,
}}
/>tsc:
_jsx(EntryTextDialog, { foo: 1,
bar: 2,
baz: 3 });tsgo:
_jsx(EntryTextDialog, { foo: 1, bar: 2, baz: 3 });JS emit: parentheses removed around TS instantiation expression emit
Repro: Playground
Difference: parentheses are removed in JS around a TS instantiation expression.
Input:
const UserListGrid = ManagedListGrid<TriageMember>;tsc:
const UserListGrid = (ManagedListGrid);tsgo:
const UserListGrid = ManagedListGrid;JS emit: JSX property names no longer quoted unnecessarily
Repro: Playground
Improvement: properties are no longer quoted unnecessarily in JSX.
Input:
const c = <Comp $backButton="" ></Comp>tsc:
const c = _jsx(Comp, { "$backButton": "" });tsgo:
const c = _jsx(Comp, { $backButton: "" });Source maps: better mapping of identifiers in some cases
Repro: N/A
We have noticed that in some cases identifiers seem to be mapped correctly now where before they were mapped to empty spans. Hard to create a self contained example.
Type checking: ts-ignore et all cause new errors
Repro: N/A
We have some code bases that migrated to TS by relying heavily on ts-ignore and ts-expect-errror. These code bases now have all sorts of new errors. We didn't report any of these specifically as we generally consider suppression to not reliably work between versions anyway. This are caused by different erorr spans in some cases, but we did not investgate to make sure this is the only reason
Type checking: new error when inferring return type from similar object types with promises
GitHub ticket: #1743
Repro: Playground
Resolution: Won't fix (not planned)
When returning similar object types through nested promises, tsgo can report an error where tsc accepts the code. The issue was marked as a type-ordering difference, and the team closed it as not planned.
Input:
function getInfo(): Promise<{
id?: string;
history?: string[];
}> {
return Promise.resolve().then(() => {
if (Math.random() > 0.5) {
return Promise.resolve().then(() => {
return {
id: "1",
history: ["1"],
};
});
} else {
return Promise.resolve({
history: ["1"],
});
}
});
}tsc:
No error.tsgo:
error TS2345 on the callback passed to Promise.then(...)Type checking: this is no longer any in functions of object literal assigned to prototype
GitHub ticket: #1782
Repro: Playground
Resolution: Won't fix
For object literals assigned to .prototype, tsc has a special-case where this becomes any. tsgo instead types this as the containing object shape, which can surface new property errors.
Input:
function Stream() {
}
Stream.prototype = {
endOfStream: function (): boolean {
return !this.tokens.length;
},
};tsc:
No error (`this` is `any`).tsgo:
Error on `this.tokens` because `this` is typed as `{ endOfStream: () => any; }`.Type checking: duplicate identifier error in type with computed property names
GitHub ticket: #1740
Repro: Playground
Resolution: Improvement
tsgo reports duplicate identifiers for repeated computed property names in type literals, while tsc allows this specific case.
Input:
type X = {
["A"]: string
["A"]: string
}tsc:
No error.tsgo:
Duplicate identifier error.Type checking: inference fails from unions of arrays of different depths (T[] | T[][])
GitHub ticket: #1789
Repro: Playground
Inference from T[] | T[][] can fail in tsgo due to type-ordering behavior when candidates like Value and Value[] compete.
Input:
declare function flat<T>(args: T[] | T[][]): void;
type Value = 1 | 2;
declare const n: Value[] | Value[][];
flat(n);tsc:
Type checks (`T` is inferred as `Value`).tsgo:
error TS2345: Argument of type 'Value[][] | Value[]' is not assignable to parameter of type 'Value[][][] | Value[][]'.Type checking: duplicate identifier between class declaration and expando function property assignment
GitHub ticket: #1783
Repro: Playground
Resolution: Won't fix
tsgo reports a duplicate identifier between a static class member declaration and an expando assignment on a same-named function, while tsc allows it.
Input:
declare class Logger {
static instance(): void;
}
function Logger() { }
Logger.instance = function () { };tsc:
No error.tsgo:
Duplicate identifier 'instance'.Suggested workaround from the issue discussion:
(Logger).instance = function () { };
Activity
RyanCavanaugh commentedon May 19, 2026
Great report! Thanks for all the hard work investigating this. Glad to hear things are going well.