> Not always possible, as in the case of simple, garden-variety keyword arguments.

// foo is required, bar has default, baz is optional

All of your examples are correctly identified by TS as type errors, because they all have a default argument which will never bind `foo`. True in JS as well as TS. Consider the untyped code, with some access to a required `foo`:

function myFn ({ foo, bar = 3, baz } = {}) {
return bar + (baz ?? foo);
}

myFn(); // NaN

Your function shouldn’t supply a default argument, because if the foo property is required the object containing it has to be too. It should instead supply defaults to the properties in it. This is closer to what you seem to want:

function myFn (pojo: POJO) {
const { foo, bar = 3, baz } = pojo;
// foo is required, bar has default, baz is optional

return bar + (baz ?? foo);
}

myFn(); // Compile error
myFn({}); // Compile error
myFn({ foo: 2 }); // 5
myFn({ foo: 2, bar: 4 }); // 6
myFn({ foo: 2, bar: 4, baz: 6 }); // 10
myFn({ foo: 2, baz: 6 }); // 9

> But... it doesn't even try to infer the type of an untyped destructuring - even if it's a local function used only once!

Yes, it does, if the thing you’re destructuring is typed. It doesn’t infer from usage, and while that sounds nice and some languages have it, it's fairly uncommon.

> oh wait... parameter can't have question mark an initializer

Yeah. It can’t. But if you applied what I suggested, it compiles and has the type you expect without adding undefined:

type Foo = { defaultBar?: Bar }

function main (foo: Foo, bar: Bar = foo.defaultBar) {}

type Main = typeof Main; // function main (foo: Foo, bar?: Bar | undefined) {}

> Probably because it compiles them to a pre-standard, ES5-compatible class implementation based on good ol' `Foo.prototype`. And since they've already handled them one way, they can't become spec-compliant without breaking backwards compatibility.

You’re partly right. I’m on mobile so I can’t dig into the failure but type checker seems to be crashing or getting stuck due to the confusing syntax. If you make it more clear by putting parentheses around the class expression, you still won’t get any compiler errors because it’s constructed with no arguments, and a is implicitly assigned undefined which satisfies any. If you then give the a property a non-any/unknown type you’ll get a compile error because a wasn’t assigned.

It’s weird that even newer compile targets don’t get an assigned a: undefined at runtime, and definitely qualifies as a compiler bug (you should file it! I’ll add what I’ve learned!). It certainly does if you actually assign anything to a during construction.

> Everybody's build tools been compiling that back down to CJS so hard that Node.js 16+ introduced intentional incompatibilities between CJS and ESM modes just to get people to finally switch to the standards-compliant module system.

This is factually false. CJS is fundamentally incompatible with ESM, and has been since day 1. They shipped it incompatible from at least Node 12 because there’s no way to make it compatible. ESM is fundamentally async, and CJS is fundamentally blocking. ESM imports are “live”, CJS are static values at the time you call require. They have fundamentally different module resolution algorithms. All of this has been documented in Node also since at least v12, and has been spec compliant (notwithstanding since fixed bugs) the whole time.

There are definitely valid gripes about how TS has supported ESM though, particularly in terms of file extensions. Thankfully they’re actively working to address that now.