Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better compile time polyfill support by splitting lib .d.ts files into smallest segments possible #25567

Closed
4 tasks done
greyepoxy opened this issue Jul 10, 2018 · 8 comments
Closed
4 tasks done
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds

Comments

@greyepoxy
Copy link

greyepoxy commented Jul 10, 2018

Search Terms

lib, polyfill

Suggestion

Split the existing lib .d.ts files into smaller feature segments so each feature can be included selectively as desired.

Use Cases

I work at a company with a large collection of independently shipping web modules/apps.

Today we use the following method to detect at compile time if a polyfill will be required for the browsers we support (IE11+ in our case).

  1. Setup our tsconfig.json lib to only specify "es5" and "dom"
  2. If a newer JS feature is desired selectively add its definition to tsconfig.json lib and polyfill that specific feature

The problem is that the current definitions for tsconfig.json lib are often much larger then we would like.

For example:
If we want to use the feature Array[].findIndex using this above described method we would add "es2015.core" to the tsconfig.json and a corresponding Array[].findIndex polyfill. This works but unintentionally results in a plethora of other features (Array.find, Array.from, ...) that now will compile without problems but are not polyfilled.

If we want to be sure to avoid unintentionally using a "es2015.core" feature we have not polyfilled we would have to polyfill all of the features but would really like to avoid doing that.

Examples

Would be great if the type definitions were split in a similar way to core-js. They do a fantastic job of allowing selective polyfills as well as collections of polyfills.

so for the array methods "es2015.core" would be split into "es2015.array" which would be a composition of independently usable more specific cases (for example "es2015.array.findIndex").

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript / JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. new expression-level syntax)
@kitsonk
Copy link
Contributor

kitsonk commented Jul 11, 2018

My opinion is that it would sort of make things unmanageable. I would think that if you are microfilling in such ways, that declaring the types yourself as part of the microfilled code base would be best. ES2015 is already broken down into core, generator, promise, iterable, proxy, reflect, symbol, and symbol.wellknown. I am not sure breaking them down further would realistically add value.

@mhegazy
Copy link
Contributor

mhegazy commented Jul 11, 2018

One thing to note the library files are not special in any way. they are just regular .d.ts files with declarations in them. Nothing stops you from adding your set of lib files that have the declarations you use and pull them as needed. the JS runtime does not change that much, so it should be a one-time setup cost, and you should be fine.

We have split them in logical units to make it manageable to pull them in/out. we can split them into file-per-function, but there are APIs that have multiple overloads, e.g. ones that have iterable vs array (e.g. Array.From), if you want to be completely accurate, we should have lib.es2015.array.from.d.ts and lib.es2015.array.from.iterable.d.ts, that is a lot of complexity, and i doubt the value added warrants the cost.

@greyepoxy
Copy link
Author

@mhegazy so a little more context on our situation. There are a couple hundred web developers working on (at last count) 150+ web modules (mostly shared packages but there are a couple web apps). So whatever solution we use needs to scale. Your right we could create a "common typings" project and duplicate the exiting deceleration files and split them into smaller segments for our use there. I was hoping to avoid creating a custom solution like this since my thinking was that this is a common enough problem that it made sense to try and do this work in a place that would benefit all TS users.

To be clear I am not advocating for the removal of the exiting logical units, I am just asking that the existing logical units be compositions of smaller units that can be imported on their own.

So using the existing"es2015.core.d.ts" as an example it would become a composition of the following files (or something like this, maybe with kebab casing),

  1. "array.find.d.ts"
  2. "array.findIndex.d.ts"
  3. "array.fill.d.ts"
  4. "array.copyWithin.d.ts"
  5. "array.from.d.ts"
  6. "array.of.d.ts"
  7. "date.constructor.fromDate.d.ts"
  8. "function.name.d.ts"
  9. "math.acosh.d.ts"
  10. "math.asinh.d.ts"
  11. "math.atanh.d.ts"
  12. "math.cbrt.d.ts"
  13. "math.clz32.d.ts"
  14. "math.cosh.d.ts"
  15. "math.expm1.d.ts"
  16. "math.fround.d.ts"
  17. "math.hypot.d.ts"
  18. "math.imul.d.ts"
  19. "math.log1p.d.ts"
  20. "math.log10.d.ts"
  21. "math.log2.d.ts"
  22. "math.sign.d.ts"
  23. "math.sinh.d.ts"
  24. "math.tanh.d.ts"
  25. "math.trunc.d.ts"
  26. "number.epsilon.d.ts"
  27. "number.isFinite.d.ts"
  28. "number.isInteger.d.ts"
  29. "number.isNaN.d.ts"
  30. "number.isSafeInteger.d.ts"
  31. "number.maxSafeInteger.d.ts"
  32. "number.minSafeInteger.d.ts"
  33. "number.parseFloat.d.ts"
  34. "number.parseInt.d.ts"
  35. "object.assign.d.ts"
  36. "object.getOwnPropertySymbols.d.ts"
  37. "object.is.d.ts"
  38. "object.setPrototypeOf.d.ts"
  39. "regexp.constructor.d.ts"
  40. "regexp.flags.d.ts"
  41. "regexp.sticky.d.ts"
  42. "regexp.unicode.d.ts"
  43. "string.codePointAt.d.ts"
  44. "string.includes.d.ts"
  45. "string.endsWith.d.ts"
  46. "string.normalize.d.ts"
  47. "string.repeat.d.ts"
  48. "string.startsWith.d.ts"
  49. "string.anchor.d.ts"
  50. "string.big.d.ts"
  51. "string.blink.d.ts"
  52. "string.bold.d.ts"
  53. "string.fixed.d.ts"
  54. "string.fontcolor.d.ts"
  55. "string.fontsize.d.ts"
  56. "string.italics.d.ts"
  57. "string.link.d.ts"
  58. "string.small.d.ts"
  59. "string.strike.d.ts"
  60. "string.sub.d.ts"
  61. "string.sup.d.ts"
  62. "string.fromCodePoint.d.ts"
  63. "string.raw.d.ts"

You bring up a good point about how their are some functions which require overloads as new features are brought in. I believe we can resolve this by making sure the interfaces for those types are defined independent of the polyfill.

For example for array.from, "array.from.d.ts" would have a dependency on the iterator interface https://github.com/Microsoft/TypeScript/blob/master/src/lib/es2015.iterable.d.ts#L16 (which would be the only thing defined in a new file "iterator.core.d.ts"/"iterator.interface.d.ts"). array.From would then be a combination of all the existing overloads https://github.com/Microsoft/TypeScript/blob/master/src/lib/es2015.iterable.d.ts#L50 and https://github.com/Microsoft/TypeScript/blob/master/src/lib/es2015.core.d.ts#L47. From the polyfill's perspective all of these overloads are supported. It just so happens that unless the user brings in another polyfill (array.keys, array.entries or array.values) their is no way to get an iterator to pass to from.

Unfortunately, this will result in the defined types for existing projects using "es2015.core" or "es2015.iterable" would change slightly. Not as equivalent for existing users as I originally though.

Happy to do the work to get this setup as this is a huge pain point for folks at my company. For those of us who need to support old browsers compile time checking is very useful to make sure we do not miss any polyfills.

When you say your worried about the complexity are you concerned that it will be difficult to reason about from the perspective of a user of TS? Or from the perspective of the developers of TS?

@RyanCavanaugh
Copy link
Member

When you say your worried about the complexity are you concerned that it will be difficult to reason about from the perspective of a user of TS? Or from the perspective of the developers of TS?

I would say both. Complexity is always measured relative to gain, and this isn't a) a broadly-requested feature or b) something you couldn't do today by running with --noLib.

We also have to think about complexity in terms of the future. If we look ahead 15 years, are we going to be looking at loading 600 different files from disk just for "Hello World" because every single function TC39 added gets its own file? That has performance implications and maintainability implications.

@craigkovatch
Copy link

How many times does it need to be requested before it matters? ;)

@mhegazy mhegazy added Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript labels Jul 11, 2018
@greyepoxy
Copy link
Author

@RyanCavanaugh I hear you on trying to be aware of your maintenance and performance cost. Personally I think its a little early to say that this will have any impact on performance or TS's long term maintenance story. For perf hundred's of file reads occur today as part of compile, it is the same order of magnitude of file reads with this proposed change. I find talking about long term theoretical's like this difficult because there is nothing to say that the existing solution won't have the same problem you describe as with this proposed solution.

I guess my value argument essentially boils down to this:
JavaScript/TypeScript is so tied to the environment it runs in (and what is supported in that environment) more fine grained control by default would be highly beneficial.

@kitsonk
Copy link
Contributor

kitsonk commented Jul 12, 2018

How many times does it need to be requested before it matters?

Does because people keep asking for something doesn't always make it the right thing to do. 😉

Personally I think its a little early to say that this will have any impact on performance or TS's long term maintenance story.

I think there is nothing preventing any interesting parties doing the work as a thought experiment and reporting back the impact on performance. It is an open source project after all. There is a question of the core team expending an effort on it though versus working on other things.

@RyanCavanaugh RyanCavanaugh added Too Complex An issue which adding support for may be too complex for the value it adds and removed Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature labels Jun 25, 2021
@RyanCavanaugh
Copy link
Member

I have not seen other requests for this kind of thing, nor scenarios that would benefit from it, and given the large implications of it, this won't be something we take up. --noLib remains as a possibility if there's some specific problem in a use case that it can solve.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Suggestion An idea for TypeScript Too Complex An issue which adding support for may be too complex for the value it adds
Projects
None yet
Development

No branches or pull requests

5 participants