Three.js: Evaluate TypeScript

Created on 8 Jan 2019  ·  28Comments  ·  Source: mrdoob/three.js

Evaluate TypeScript

I used the template of the Ember.js RFC to document this issue. I think it is a good template to adress the main concerns about a bigger change. For details about their RFC process you can visit their github repo: https://github.com/emberjs/rfcs

Summary

I wanted to move all the TypeScript discussions into one single place, because right now all the pros and cons about TypeScript are scattred around several issues, threads, pull requests and discussions. This makes it very hard to follow and I think there won't be any significant progress if we do not focus. Also I think there is a lot of traction concerning three.js and TypeScript as seen lately in https://github.com/mrdoob/three.js/issues/11552

Motivation

Since TypeScript becomes more and more popular in the frontend community we could start thinking about adoption. Also if you compare the weekly downloads of @types/three and the three package on npm it seems that a lot of people use Three.js already with TypeScript. For the time frame 2019-01-01 to 2019-01-07 it was 56414 downloads of three and 40588 for @types/three (for more details see: https://www.npmjs.com/package/@types/three and https://www.npmjs.com/package/three). Furthermore there is already a lot of work done spread across several projects and repositories. Therefore it would be nice to join forces and maintain the TypeScript stuff in one place.

In my opinon there are more pros than cons for TypeScript (but of course there are a lot of controversial opinions about types in JavaScript and TypeScript in special in the community)

Some of pros I see are:

  • possibility to improve developer experience, also for new contributors and for users of the Three library
  • types could help to explorer the api of the Three library and they could help to develop with less need to read the docs
  • no out dated @types/three definitions anymore
  • room for future transpile optimizations (for example things like tsickle is doing right, I think there will be more tools like this in future). Also experimental tools like AssemblyScript could become an option for certain very performance critic parts of the source code
  • types could help to improve code quality
  • possibility to use new features of the ECMAScript standard and transpile the source to different ECMAScript versions
  • when done right it makes no difference for the users of the Three library if the source code is maintained in TS or JS
  • with the compiler flag declarationDir we are able to generate the d.ts files from our source code so all typings are always up to date

Detailed design

We should finish all the ES6 efforts first since they form the base for TypeScript. Also the transition from ES6 to TypeScript wouldn't be that hard (since TypeScript reads a lot like modern JavaScript with types). To start with TypeScript we just need to add the rollup-plugin-typescript (I would suggest rollup-plugin-typescript2). Then we need to create a tsconfig.json and setup all the TypeScript-compiler settings to our needs. Maybe we should add tslint as well (there is also a rollup plugin for that, it's called rollup-plugin-tslint). After the build works we could incorporate the typings done in @types/three and other projects like three.ts.

How we teach this

At first we would need to document it correclty for new contributors. For the users of Three.js everything stays the same (since TypeScript is transpiled to JavaScript). After some iterations it would make sense to provide the code examples in the docs in TypeScript and JavaScript. A good example how this could be done is the Stripe API docu

Drawbacks

The build pipeline becomes more complicated and more dependencies are added. Furthermore I'm not sure how easy it is to migrate the test suite and the test runner. Also the entry barrier for new contributors (not for users of the library) could become higher since they need to know TypeScript. Very generic code can become very verbose when adding types. With TypeScript we are a little bit more away of "the platform" since there is a transpile step inbetween and we are not in total control of transpilation (of course we could write our own transforms but this is very tedious)

Alternatives

Just stick with pure JavaScript but this would also mean that we neglect all the efforts already done by projects like @types/three. For all the users of Three.js with TypeScript this would mean that they need to rely on an unofficial typing of Three.

Unresolved questions

  • Do we really want this?
  • When to start (right now or after ES6 finalization)?
  • How to start? Should we start at first only with d.ts files or jump fully into TypeScript?
  • Who could do this or let me phrase it differently, who is motivated to start this?

Most helpful comment

Until browsers do not support TypeScript natively I prefer to focus on JavaScript ES6.

I understand it can be compiled to .js, but we're just starting to be able to import from src directly and I think that's going to help the project more than converting to TypeScript.

https://github.com/mrdoob/three.js/blob/dev/examples/webgl2_sandbox.html#L37-L48

Adding side-by-side *.d.ts files sounds good but it'll need someone to own it and keep them up to date.

All 28 comments

From the view of runtime performance, I'm interested in

Also experimental tools like AssemblyScript could become an option for certain very performance critic parts of the source code

I've been doing Three.js core + WASM experimentation.

https://github.com/takahirox/three.wasm-experimental
https://twitter.com/superhoge/status/1071132448426262529

From the experimentation I realized porting the entire core to WASM can improve the runtime performance, 1.5x faster on my example, while partially porting the small parts, for example just Math codes(Vector3, Matrix4, ...), is no benefit because of big JS-WASM overhead.

But I didn't think it's a good idea to rewrite the entire Three.js core into C++ or Rust for contributors because of difficulty of maintenance so I've been looking for alternative ways. I'm interested in if TypeScript + AssemblyScript works fine for Three.js.

How to start? Should we start at first only with d.ts files or jump fully into TypeScript?

We will be submitting a PR that adds *.d.ts files to Three.JS, based primarily on @types/three (thus reusing that work.) That is a great starting point and lets us continue in JS for now. It can also be done incrementally.

@takahirox nice work :-) I'm always fascinated how much innovative work happens around Three.js. It's a pity that these proof-of-concepts get so less attention. I also think that rewriting everything in C++ or Rust is not an option. Maybe AssemblyScript is but I didn't gave AssemblyScript a try so I can just speak about what I read about AssemblyScript. But maybe we should consider AssemblyScript as well because as I understand it's more or less a subset of TypeScript

@bhouston I'm not sure if moving the d.ts files from @types/three to the three repo makes a lot of sense. Especially because these d.ts files could be generated from the TypeScript files and then they are always in sync. Is there a way to assure that the d.ts files are always in sync with the js-files in an automated way? If yes then I would think putting the d.ts files into the three repo could be beneficial. Also I think it depends on the decision of the maintainers. If they do not want to adopt TypeScript, the d.tsfiles could be an option as well. Also if they decided to add TypeScript in some years we could bridge this time with d.ts files. Otherwise I'm afraid that there is less benefit and more work. But maybe I'm just overlooking something

@bhouston I'm not sure if moving the d.ts files from @types/three to the three repo makes a lot of sense. Especially because these d.ts files could be generated from the TypeScript files and then they are always in sync.

If we move directly to TypeScript then there would be no need for *.d.ts files. The issue is that that currently the @types/three project is always slighlty out of date with Three.JS because it is separately maintained. Also it mirrors the built structure of three.js rather than individual files and thus can not support tree shaking and other forms of optimization. Thus adding in *.d.ts files greatly improves the project from its current state, but it isn't quite as good as moving towards *.ts files directly.

If we can move towards *.ts files directly, that is the better option. I was not sure there was support for that.

I understand Three.JS likes to do things incrementally, thus this was an incremental option rather than a big bang.

@bhouston I totally agree with you. And I also think that a big bang rewrite is not possible so we need to adopt incrementally. But if I read the TypeScript docs it seems like we could have ts and js files side by side. So we could start converting single files over to ts. For more details, you can read this blog post: https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html

But how to approach TypeScript should be decided by one of the maintainers. I think both options (d.ts files or mixing ts and js files) are feasible. And it's more or less a question of personal preferences and style.

@tschoartschi I'd like to move this issue forward. @mrdoob has approved side-by-side *.d.ts files. I'd like to go there because it doesn't force TypeScript on people immediately and we can back out of it without having to rewrite all contributions during the *.ts phase. And then we can still convert *.js files to *.ts files incrementally mostly my merging them with the side-by-side *.d.ts files.

I think that side-by-side *.d.ts files is a really good first step and it is easy to do. I would prefer to not allow "perfection" to hold us back from making incremental progress.

@bhouston cool 😎 I could also help. I think it would make sense that you start and others and I can then join in. Maybe we could also speak to the maintainers of @types/three. Should we create a Slack channel in the Three.js workspace? So we can align quicker and do not pollute this issue with chat-like conversations. Nevertheless, I would wait a moment until @mrdoob adds his point of view. Because if he is against TypeScript I think we shouldn't invest time.

I am willing to commit time to the port once this gets the blessing from @mrdoob.

Until browsers do not support TypeScript natively I prefer to focus on JavaScript ES6.

I understand it can be compiled to .js, but we're just starting to be able to import from src directly and I think that's going to help the project more than converting to TypeScript.

https://github.com/mrdoob/three.js/blob/dev/examples/webgl2_sandbox.html#L37-L48

Adding side-by-side *.d.ts files sounds good but it'll need someone to own it and keep them up to date.

@mrdoob I don't think waiting for browsers to support TypeScript is an option. I didn't hear any intent of any browser vendor to implement TypeScript. But I see your concerns, especially with the examples. I didn't have a look at the new examples. It's awesome that it's possible to just import the source files in the examples.

I'm not sure what would be the best solution to author the library in TypeScript and still keep the possibility to import JavaScript from src. Of course, we could transpile the TypeScript files and then have the corresponding JavaScript file side-by-side (which is the standard setting of the TypeScript compiler) but this could complicate things like code-versioning etc. The folder structure could look like

three/
├── src/
    ├── cameras
        ├── PerspectiveCamera.ts // authored code
        ├── PerspectiveCamera.js // generated code by TS compiler

Nevertheless this seems a little bit awkward to me since transpiled code should go somewhere into a dist, build or bin folder. But that's just an opinion not a hard fact. Maybe there are some TypeScript geeks who know a nicer solution.

The other option is the d.ts file suggested by @bhouston. But as @mrdoob mentioned it could be tricky to keep the d.ts files up to date. Especially on a long term view. Is it really manageable to keep them up to date for the next years? I could help setting up the d.ts files but I can not garantuee that I'll be involved in updating them all the time. For me it's much harder to commit continuously time than blocking a one-time-slot and getting some things done. I'm still not sure if it's better to support the @types/three project or to add d.ts files directly into the three project. The @types/three project is already working and serves the same needs as the d.ts approach. It also has similar challenges. Which is basically keeping things up to date.

So my conclusion is, that it's not looking too good for TypeScript in Three.js in the mid-term future. For me that's fine although I would love to see more TypeScript adoption.

Just another suggestion:
The Phaser game framework uses its comments to auto generate type definitions. I think the tool is opensourced. So TS definitions could be generated by adding /** comments */ above the functions in the correct manner.

The Phaser game framework uses its comments to auto generate type definitions...

See https://github.com/mrdoob/three.js/issues/11550 and https://github.com/mrdoob/three.js/issues/4725#issuecomment-41833647.

Although, generating documentation from comments in *.d.ts files might be interesting. 🤔

Adding side-by-side *.d.ts files sounds good but it'll need someone to own it and keep them up to date.

If the typings can be checked against source programmatically, I'm fine with this. If not, and if we aren't planning to eventually rewrite the codebase in TypeScript, I don't think it's a good idea to move the typings into the repository. If anything, we're less prepared to maintain them here — at least the current maintainers are using TypeScript themselves.

There is also this project https://github.com/semleti/three.ts
Perhaps worth taking a look and bringing it up to v100 rather than starting a new one?
Because I don't see Three.js being rewritten in TypeScript unless its obvious how much nicer it is to work with types for such a huge project... And I quite understand why it might never happen since it would be relying on a language that is not standard inside the browser.

@schoening I started this discussion because there are some proof-of-concept TypeScript versions of Three.js out there. It's the repo you linked, or the repo done by @flyover (https://github.com/flyover/three.ts, https://github.com/mrdoob/three.js/issues/11552#issuecomment-367026821). The main problem with a forked Three.ts version is to stay up-to-date. All of the repos are some versions behind and I think this will happen to all projects which are not maintained by the "core"-team. If the Three.js project will not support TypeScript I think it's best to help the @types/three project to stay in sync. I think we should join forces there.

If Three.js is going to support TypeScript at some point in the far future it would be great to think how such a transition could succeed. As @donmccurdy mentioned there are several approaches to achieve better TypeScript compatibility. I think JSDoc could be a viable approach. I'm still not convinced by the *.d.ts files since I think it's even harder to keep them up to date than the JSDoc comments. Also I don't think there is a way to checked against source programmatically with *.d.ts files. But maybe @bhouston could outline his approach, especially the concerns around keeping things up-to-date. Maybe there are some smart solutions to this problem.

The best experience for me so far is vscode + d.ts + JSDoc, all in the same project, so it is easy to stay in sync.

The latest vscode version has improved checkJs support (can be enabled in tsconfig.json), which basically is TypeScript compiler checking JavaScript files, using the types from JSDoc plus declaration merging from d.ts for the more "advanced" types, which would be ugly in JSDoc syntax.

Since vscode can derive all types it can detect all kinds of type errors too, so whenever refactoring is done, it is easy to see and edit the "buggy/old" types from d.ts, so it keeps being synced up.

Worst thing about TypeScript is the extra transpile step, which can take a few seconds for every single code change. In short, JSDoc + d.ts in vscode has all the advantages of types but not the downside of the extra slow transpile step.

Only problem is to add 100% proper JSDoc type to everything, so vscode can rely on it (just stuff like @constructor, @enum {number}, @param {number} n Number of steps)

I also wanted to add here that @bhouston and @LuWangThreekit created a PR for *.d.ts files. For more details check out their note: https://github.com/mrdoob/three.js/issues/11552#issuecomment-454881060 and/or their PR https://github.com/mrdoob/three.js/pull/15597

Sidenote: TypeScript in browsers may not be far off if Deno (A TypeScript interpreter built on V8 and Rust with 32,000 stars on GitHub) proves to be worthy.

Since we added type declaration files for the core and the example modules, I think it's okay to close the issue for now. The development experience for TS users should be definitely better than last year.

For anyone interested in TypeScript...

This Three.js-inspired library @bhouston is working on may be what you're looking for:

https://threeify.org/

Threeify from @bhouston seems like a great project 🤩 it has SemVer and uses semantic-release. It's built on top of TypeScript and its core values seem to be things like Tree Shaking, Small Build Files etc. All things which many of us would like to see in Three.js as well. So I really value the work which goes into Threeify 👍

@bhouston @mrdoob But is it really necessary to create a new project? Does it make sense to split the community? Many of the big front-end frameworks managed to do the transition to things like SemVer, TypeScript, Tree Shaking etc without a fork of their code and community.

I think @pailhead wrote an interesting article about working with Three.js, I think it was the following: Working with different three.js versions. So I think there are people in the community who would like to help to adopt some of the things which Threeify tries to implement. I think it would be great to join the forces and improve Three.js instead of creating a new library.

Like many articles on the internet, Pailhead's article is biased. Doesn't take into account other uses of the library.

I don't think it's possible to make a library that caters all types of js developers without disrupting each others development workflow.

We have very similar experiences as @pailhead I do not think his workflow is a niche workflow. I think his workflow is very common for developers working with modern front-end frameworks like Vue, Ember, React, Svelte, Angular...

Maybe we should have a look on how Vue is doing it. It has a user-base ranging from PHP programmers who just want to enhance their website to people who develop sophisticated web apps. It bends the curve between those different types of users very nicely.

Also about upgrading the existing code base without breaking the experience for everyone there are great examples out there. We could have a look at how Ember managed to consistently grow with the web community and it's industry standards.

I wouldn't consider staying up-to-date with modern web development unfeasible. But I totally agree that it's a lot of effort and we need to put a lot of thought into those things. This is why I believe it's better to work together to create a modern experience than to create several new projects.

When you go through this thread there are already two different projects, Three.ts and Threeify and probably there are many more out there. I truly believe that there would be a huge benefit for the whole Three.js community if we would work together instead of creating forks and separate initiatives.

@mrdoob, how about a survey to collect some data? use of typescript is one thing and I'm sure there's other qualitative variables we could try get an estimate on.

@roomle-build can you list the disadvantages of converting the library to TypeScript?

I do not see a real disadvantage of gradually adopting TypeScript for the library. In contrast, I think it would bring a lot of benefits (this is why many project are converting to TypeScript). Nevertheless, there are some challenges of course, for example:

  • how can we provide a nice experience for none-TS-users?
  • how can users without a built-step use the library
  • and of course all other things I didn't think of

But as I said before, those questions where solved in other projects as well and we can copy these solutions from there (like Vue or Ember for example).

But the main thing I wanted to point out is, that I think it's dangerous if the community splits apart and we have several different projects and the fork of a fork of a fork. I'm not a fan of fragmentation and I think it's better to work together than against each other. Of course there are other people who will argue that competition is great and that this would thrive innovation also in the Three.js project but I'm more the believer in collaboration 🙂

I think one of the biggest advantages of three.js is its accessibility. The points @roomle-build has listed would worsen the engine's ease of use (especially in context of beginners). I vote to continuing using JavaScript unless an alternative programming language is natively supported in browsers.

I'm not a fan of fragmentation and I think it's better to work together than against each other.

Having more 3D engines in the web does not mean the developers working against each other. Sometimes it's actually better if different projects focus on different things.

I think one of the biggest advantages of three.js is its accessibility. The points @roomle-build has listed would worsen the engine's ease of use (especially in context of beginners).

I do not see it this way because other projects author their code in TypeScript without any influence on the accessibility. I mentioned Vue and Ember as examples before. Maybe it makes sense to review the stance against TypeScript? We would not need to invent something we would only need to copy the successful approaches from other projects. Furthermore, I think TypeScript would have the benefit that three.js would be more accessible for contributors because the IDE could help them to get an overview of the whole project faster.

I vote to continuing using JavaScript unless an alternative programming language is natively supported in browsers.

I think this will not happen in foreseeable future and therefore I think we should have a more pragmatic view about TypeScript. As I wrote above, I think it's only a problem of how the project and repo are organized and not a trade-off between ease-of-use and dev-experience.

I think we have made our position clear.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

danieljack picture danieljack  ·  3Comments

konijn picture konijn  ·  3Comments

clawconduce picture clawconduce  ·  3Comments

Horray picture Horray  ·  3Comments

filharvey picture filharvey  ·  3Comments