-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Description
Bug Report
🔎 Search Terms
inconsistent properties order
fields order
🕗 Version & Regression Information
4.4.4
Description
We noticed that the resulting *.d.ts sometimes mention the properties of some types in random order, and this order changes every time a tsc --watch build unfreezes (i.e. it's not stable). Our build system consumes tsc output from multiple monorepo projects and pipes it to other different tools, so a change in a *.d.ts file whilst in practice it should've not been changed triggers some excess rebuilds.
The effect is hard to reproduce (and especially hard to reproduce in a sandbox), so I'll try my best to provide all the info I have collected having low hopes. (But my only hope is that someone from TS engineers has a quick idea on where the reordering might come from.)
So this is the correct order of properties in the inferred type as shown by VSCode (externalID - assignee - completed):
And this is what I see in *.d.ts file during the initial build with --watch (incorrect order, externalID - name - dueAt):
This is what's there after a watch-build unfroze when I changed some unrelated file (another incorrect order, externalID - name - assignee, the 3rd variant):
Here is an extraction from the source code; I can't unfortunately sandbox it, so trying my best (notice IOValidatedFunc):
export default function IO<TInputLax extends object, TOutputLax extends object>(
name: string,
InputSchema: InputStruct<TInputLax>,
OutputSchema: OutputStruct<TOutputLax>
) {
function wrapper<TRest extends any[]>(
func: IOPassedFunc<PartialToUndefined<TInputLax>, TOutputLax, TRest>
): IOValidatedFunc<TInputLax, TOutputLax, TRest> { ... }
...
return wrapper;
}
...
// Superstruct library; it provides the correct TS typing from a validators structure.
UpdateTaskIO = IO(
"UpdateTaskIO",
object({
externalID: string(),
assignee: optional(nullable(string())),
completed: optional(boolean()),
dueAt: optional(nullable(JsonDate())),
memberships: optional(
array(
object({
project: string(),
section: optional(string()),
})
)
),
name: optional(trimmed(string())),
notes: optional(string()),
tags: optional(array(string())),
}),
NodeOutputSchema
);
...
override run = UpdateTaskIO(async (input) => { ... });🙁 Actual behavior
The order of fields in *.d.ts file surprisingly changes when watch-building an unrelated file modification.
🙂 Expected behavior
*.d.ts content keeps unchanged if the source code is unchanged.


