Description
--moduleResolution hybrid
- Very Node-like resolution mode that is mostly targeted at bundlers and special resolvers.
- What's it have that's special (beyond the weird
classic
mode)?node_modules
package lookup- Extensionless
- Not in
--moduleResolution node16
/nodenext
- Not in
- Directory index lookup (i.e. look up an
index.*
file)- Not in
--moduleResolution node16
/nodenext
- Not in
exports
look-up frompackage.json
- Not in
--moduleResolution node
- Not in
*.ts
imports- Nothing has this today!
- Only when using
--allowImportingTsExtensions
--allowImportingTsExtensions
- Could potentially be ported to work on other resolution
- Requires
--noEmit
- Why?
-
When we emit, we will not rewrite the import paths. So
import * as foo from "./foo.ts"`
will remain the same in the output
.js
file:import * as foo from "./foo.ts"`
and this will fail in most tools if
foo
was also rewritten to a JavaScript file namedfoo.js
!
-
- Basically some other tool is going to either handle compilation of the files, or resolution of the files directly - so
- Why?
- We keep talking about what a bundler does (in the meeting) - why isn't this just called
--moduleResolution bundler
?- Other loaders/runtimes like ts-node, bun, etc.
- Even if it's not perfect,
bundler
communicates more thanbundler
- What else is controversial other than the name?
- More options than just
allowImportingTsExtensions
allowImportingTsExtensions
(already mentioned)resolvePackageJsonExports
- resolve from theexports
field the way Node.js 12+ does today (which we do under thenode12
/nodenext
flag)resolvePackageJsonImports
- resolve from theimports
field the way Node.js 16+ does today (which we do under thenode12
/nodenext
flag)customConditions
- Does
customConditions
need - We would prefer there were some sort of hierarchy to the flags, but not sure.
- Do we need to ship all the flags?
- Browserify and Rollup don't do certain things by default.
- Rollup might be okay b
- Prefer to ship as is, evaluate over the rest of the release cycle.
Deprecation Plan
--ignoreDeprecations
- Specify
"5.0"
to suppress all the errors that 5.x has deprecated. - When 6.0 hits, using
"ignoreDeprecations": "5.0"
becomes an error.
- Specify
--noSwitchCaseFallthrough
- We want to get out of the syntax linting business, but people likely use it.
- Not a huge cost anyway.
- Feels like we're keeping this.
--out
?- Totally broken?
- No it's not, only broken for
module
- Only if we give a good error message.
- Deprecate.
--charset
?- Doesn't get respected anyway.
- Remove.
--target es3
- Deprecate.
- Complicates our testing infra now.
"prepend": true
in project references?- We did this for ourselves - but is this widely in use?
- We see one or two legit repos using it via GitHub's code search - the rest of the results seem to be duplicates of our repo.
- This complicates our emit quite a bit.
- okay.
- Meta: we have two-and-a-half years to change our minds on this
Using our internal missing type
- Unfortunate that we have so many internal types and different flags!
in
Operator Narrowing from Negative Checks
-
If you have
obj: object
and write code likeif ("foo" in obj && typeof obj.foo === "string") { obj.foo; }
you would expect
obj.foo
to be valid and have the typestring
. -
If you write the negative case and bail our early, you'd expect the same
if (!("foo" in obj) || typeof obj.foo !== "string") { return; } obj.foo;
-
This is the same problem as the fact that
obj
itself is narrowed in a distinct manner fromobj.foo
.obj
is only narrowed to have type{ "foo": unknown } & object
- not compatible with anything that requires{ foo: string }
.- However,
obj.foo
is narrowed to have the typestring
. - Related issues are Nested Tagged Unions #18758, Union not narrowed with
typeof x.y
as a discriminant #32399.
-
It would really be ideal if
obj
was narrowed to{ foo: string } & object
, and narrowingobj.foo
would just "fall out" from narrowingobj
itself. -
Can imagine that we just stack intersections as we learn more
- e.g.
object & { foo: unknown } & { foo: string }
which simplifies toobject & { foo: string }
- e.g.
-
The problem with doing that is you would end up with huge types in some cases - which adds visual noise and impacts performance.
-
Additionally, you can "learn" information by introducing intersections when narrowing types - but when you join from two branches, how do you know which intersections were "learned" from type guards vs. which intersections were already there.
- Feels doable, making this efficient also makes this harder.
- We eagerly normalize intersections which is part of what makes this challenging.
-
There's a silver lining - simplifying narrowing to just narrow the roots of references would make us more efficient in other ways.
- Also, deferring intersections could be faster.
-
Is this compelling?
- Lots of feedback on this issue.
- Spent a lot of time on this (e.g. Type guard by deep property #38839)
- There's also lots of UX weirdness in the language service as well (Display narrowed type of properties in
completionEntryDetails
#51526)