4 ms·
You’re right on the first (although classes are out of fashion, so this is low impact) On the second, doesn’t this work?: ‘function foo({ bar = 3 }: { bar?: nu
by purplerabbit 4y ago
You’re right on the first (although classes are out of fashion, so this is low impact)
On the second, doesn’t this work?: ‘function foo({ bar = 3 }: { bar?: number })’
- kuramitropolis 4y ago>You’re right on the first Strictly speaking, it takes exactly one such behavior (that you cannot even disable) for TS to stop being a superset of JS. >although classes are out of fashion, so this is low impact Looking at the TSC codebase, so are keyword arguments. The "in" thing is just to write very very long lines of multiple verbosely named positional arguments instead. That said, "clases are out of fashion" is a complete non-argument. I'm of the functional persuasion, yet I've found that classes are the ony way to write TypeScript that fits on your screen at all. Especially now that classes are being introduced in JavaScript proper, and of course TypeScript does them only slightly differently (handling of default property values and "definedness" differs). >On the second, doesn’t this work?: ‘function foo({ bar = 3 }: { bar?: number })’ You also need a `= {}` there, otherwise you'll need to call `foo({})` - it won't let you call `foo()`. This is also in JS though, so a bad example of TS breaking things (`function foo ({ bar })` still won't work though). There are probably better ones that people encounter, work around, and forget about, because nobody's listening anyway. "The code making sense is not important, what's important is helping the user" lol. Now imagine how the above looks with 5-10 kwargs (because keeping context in the class instance is "out of fashion", so it's either a ton of args per function or a "context" record which is effectively reimplementing classes but with a worse experience), and an aggressive formatter insisting every individual thing has to be in its own line. Here's another: failing to infer the type of `this.constructor`. Sure, the constructor signature may change in a subclass (why not disallow incompatible constructor overrides, given incompatible property/method signatures are already disallowed?); then what about static methods accessed via `this.constructor`? So you end up defining an interface type for the constructor and using `(this.constructor as MyConstructorType).staticMethod` or whatever. Which is just visual noise where the fucking intent of the code was previously clear as day, so clear that TSC should've been able to infer it (yeah the type inference also sucks). Also, ever seen TS2322? It's my pet now. What it do All in all, TS really puts the "Java" back in JavaScript, and then some. EDIT: Also crap like not being able to have a question mark and a default value in positional arguments so you gotta add `|undefined` there. Even the stuff it adds on top of JS is poorly thought out.
- eyelidlessness 4y ago> I'm of the functional persuasion, yet I've found that classes are the ony way to write TypeScript that fits on your screen at all. Huh? I’m of the functional persuasion too, and I use classes in TS too, but for strategic reasons (well defined value objects are easier to reason about than duck typed POJOs, and they perform better too). But I’ve never found them more space-dense than the equivalent function-only code. Often quite the opposite, as so many functions’ return types can be fully inferred. Which of course, this is how you get ~~ants~~ duck typed POJOs, but you can’t have explicit type defs without explicit defining them somewhere, and of course the syntax that collocates the field type and its value is more dense than the syntax which is wholly incompatible with that concept. > handling of default property values and "definedness" differs The only difference is that TS provides a fully optional shorthand for assigning both the type and value in constructor arguments. The actual behavior isn’t any different. This: class Foo { constructor(readonly bar: number) {} } is identical to: class Foo { readonly bar: number; constructor(bar: number) { this.bar = bar; } } is identical to: class Foo { bar; constructor(bar) { this.bar = bar; } } And this type error: class Foo { readonly bar: number; constructor(bar: number) {} } is identical to this type error, just caught sooner: class Foo { bar; constructor(bar: number) {} } const foo = new Foo(); foo.bar.toFixed(2); > Sure, the constructor signature may change in a subclass (why not disallow incompatible constructor overrides, given incompatible property/method signatures are already disallowed?) Because the compatibility is checked on the `super` call which is required both at compile time and runtime, and because many use cases for subclasses are impossible or even invalid without different construction contracts. I know there are many strong feelings about examples like this being “wrong”, but it’s a common enough inheritance example to illustrate the point: class Square extends Rectangle { constructor(/* ? */) {} } You cannot satisfy both Square and Rectangle with the same constructor arguments. This of course bolsters the point that this inheritance model is “wrong”, but it’s exactly right according to the domain, and the equivalent functional code to calculate eg area would similarly have to be either polymorphic over different shapes or expect a single shape constructed with different parameters. > this.constructor You got this one right, and it’s worse than you describe because of the weird rules for where you can’t have type parameters or explicit `this` types. The workaround is to use a static factory method, but it’s a shitty workaround with a lot of ceremony to do something that TS generally does well: model the types of real world JS code. > Also crap like not being able to have a question mark and a default value in positional arguments so you gotta add `|undefined` there. Even the stuff it adds on top of JS is poorly thought out. Ima help you out! Assuming your default satisfies the non-undefined type, you can just skip the union, it’s implied. This: const foo = (bar: Bar = someBarSatisfyingValue) => {}; is identical to this: const foo = (bar: Bar | undefined = someBarSatisfyingValue) => {}; is identical to this: const foo = (bar?: Bar) => { bar = bar ?? someBarSatisfyingValue; };