jorgebucaran / superfine

Absolutely minimal view layer for building web interfaces
https://git.io/super
MIT License
1.57k stars 78 forks source link

Type Safety for Superfine Source Code Through JSDoc Types. #145

Closed rbiggs closed 6 years ago

rbiggs commented 6 years ago

I made a fork of Superfine with full type safety for Superfine source code using only JSDoc: https://github.com/rbiggs/superfine/tree/JSDoc This gives you full intellisense, code completion, live type checking, etc. in the source code as JavaScript. Just set your user preferences in VSCode to:

"javascript.implicitProjectConfig.checkJs": true

Then examine the source code in VSCode. This lets you write plain ole JavaScript with all the benefits of typed TypeScript.

Because JSDoc comments are valid JavaScript comments, they do not affect the final output of the source code during build time. The JSDoc comments get removed during minification. Using types in the source code does require escaping expando properties with [""], but these also get removed during minification.

This provides custom types for VNode, etc., and type casting to resolve static type checking's inability to force type casting that occurs during runtime in the browser. The cast item requires extra parens around it to signal that it is being cast, but these get removed during minification as well.

jorgebucaran commented 6 years ago

@rbiggs Nice job! Why add jsdoc comments to non-exported functions?

rbiggs commented 6 years ago

Basically, the same reason one would write Superfine in TypeScript--type safety for the entire code base. With that in place, incorrect use of types in the source code will show up immediately. Besides, some people, myself particularly, like to import source files instead of distribution files to work with. I let my build pipe line handle the bundling and minification. An external d.ts file only describes what the external public interface is, but doesn't ensure that anything internal is correctly typed.

In #134 you mentioned a possible rewrite in TypeScript. I'm assuming for type correctness. This gives you the same thing while keeping it JavaScript. No need for a build step to convert it to JavaScript. It's already the JavaScript you want, but with types.

brodycj commented 6 years ago

In #134 you mentioned a possible rewrite in TypeScript. I'm assuming for type correctness. This gives you the same thing while keeping it JavaScript.

+1 (+100)

I would personally favor adding a couple more "scripts" to package.json to check the code even further:

jorgebucaran commented 6 years ago

Thank you, both! JSDoc looks promising, but I'm seriously considering to rewrite Superfine in TS.

rbiggs commented 6 years ago

Having done a lot of TypeScript in the past doing DOM manipulation, converting Superfine, and in particular, patchElement will be a lot more work than you think. TypeScript doesn't handle type coercion well because it can't foresee what the browser will do when the type needs to be coerced. I spent more time fighting to get the types right for TypeScript than writing the code I wanted to.

And TypeScript only supports a small sub set of ES6 features. I'd rather have the fuller set that Babel provides. TypeScript gave me type safety, but made me feel like I was in a straight jacket. JSDoc comments gives me type safety and lets me write the JavaScript I want.

jorgebucaran commented 6 years ago

@rbiggs I remember porting the old picodom to TS and you are right, it took some work, but the result was less alien than I thought. Even though superfine has more code than its predecessor, I am willing to bet it will be simpler because as you've seen, the vnode props are stricter than before, e.g., strings no longer represent TextNodes, etc.

mindplay-dk commented 6 years ago

My honest perspective on JSDoc is it's just Typescript with fewer features and noisy syntax.

I keep hearing arguments like "JSDoc is just standard JS syntax" - to be accurate, it is proprietary syntax embedded in standard JS comments. You will go through the same compile/build-steps to type-check and strip the annotations, which is not very different from what the Typescript compiler does.

The JSDoc comments get removed during minification.

As do the Typescript annotations on compilation.

TypeScript only supports a small sub set of ES6 features.

It supports every ES6 feature, to my knowledge - are you thinking of something in particular?

It even supports a few experimental features and syntax in addition to standard ES6 features.

I'd rather have the fuller set that Babel provides. TypeScript gave me type safety, but made me feel like I was in a straight jacket. JSDoc comments gives me type safety and lets me write the JavaScript I want.

How long ago since you used Typescript? :-)

Granted, Typescript is a freakin' dominatrix at first - until you realize it's teaching you all good habits.

You can also specify in tsconfig.json how much punishment you're in for - personally, I like to turn it all the way up to strict, which is at times a roadblock and a PITA, but it almost always works out for the better in the end ;-)

mindplay-dk commented 6 years ago

I will say though, if you write a lot of pre-minified/mangled code, strict mode won't make your day - mostly the compiler and syntax is optimized for plain, idiomatic, readable code and often does not take kindly to your little hacks or heavily leaning on Javascript quirks.

I am personally happy to forego those habits and take on a few extra bytes. YMMV.

rbiggs commented 6 years ago

Microsoft uses JSDoc comments to create richer intellisense for TypeScript. All the d.ts files they provide with TypeScript have JSDoc comments. Heck, event the superfine.d.ts file has JSDoc comments in it.

I don't have a build step for my JSDoc comments. They get used while coding using the TypeScript language service, just like it does for TypeScript files. It's just a VSCode setting to turn on. Only thing needed is minification with uglify, which automatically strips out JavaScript comments. Once in a while I might need to bundle with Rollup.

As far as the ES6 support between TypeScript and Babel, consult the Kangax compatibility table. You'll notice that for ES6 and ECMAScript2016+, Babel supports more features or more aspects of features than TypeScript does. No doubt, though, TypeScript supports more features than it did the last time I used. Which was the main reason I left it.

Any way, by habit I use type guards in my code to make sure I'm dealing with the types I'm expecting at runtime. No static type checker can prevent runtime type errors. They can only flag problems during the build. If you're using third party libraries, frameworks, plugins, widgets or third party data, only type guards can help catch and deal with type errors.

At this point, if I really wanted a typed language that compiled to JavaScript, I'd go with ReasonML.

jorgebucaran commented 6 years ago

@mindplay-dk

bar=(foo=(a=b).foo).bar

Code like that is not pre-minified or mangled. Uglify will not generate code like that. That is just writing several lines of code on the same line. Codegolf or obfuscation are more applicable terms. I am willing to let go of these bad habits. But one at a time. I'm still a recovering addict you know. 😓

I wanted to clarify that, because you have known on occasion to mistake some of my obfuscated code for code that the minifier will produce, so one may as well use TS or whatever. This is often not the case. I know almost exactly which of my code is going to be mangled by uglify and in what way and still choose a more idiomatic variant, for example using while loops when I feel they are more readable than a half-baked for loop (which uglify will almost unequivocally generate every time).

rbiggs commented 6 years ago

Sorry, but I think you misunderstood what I was saying. I was not criticizing your coding style at all. I was talking about how minifiers handle JSDoc comments.

jorgebucaran commented 6 years ago

@rbiggs No, it's okay, you can criticize it if you want. My comment was directed to @mindplay-dk.

I just wanted to clarify that some of my "deliberately obfuscated" code is how it is to compress total byte size in a way that uglify usually doesn't/can't help (maybe it's time to look into Google Closure Compiler). If uglify did all those things then there would be no point in doing them by hand myself! :)

While there's still several instances of code golf in Superfine, my primary focus has shifted to performance, with an honorable second focus on size.

rbiggs commented 6 years ago

Oh, my bad. 😜Any ways, I'm not gonna criticize your anything. Not my thing. I do really appreciate everything you're done so far with Hyperapp and Superfine. I've managed open source projects in the past dealing with all kinds of users. It's a thankless task. Users have no idea what library authors go through between coming up with the original idea, evolving it over time and dealing with every type of user imaginable. It's a passion like artists or musicians, except we're creating beautiful code that also impacts other people.

rbiggs commented 6 years ago

Closing this now.