Closed
Description
What Ends an Instantiation Expression?
- Original issue was that we parse as if the
let
on the next line is an identifier- Why?
let
andinterface
were contextual keywords. - But it's not just those; there are TypeScript-specific contextual keywords.
- Why?
- What about binary operators?
- We considered them expression starts for error recovery cases.
- Kind of just a bug.
- Idea is to say "this is an instantiation expression" if after the
>
something that looks like an expression start is preceded by a newline.- How does this impact JS + Types?
- Technically doesn't. Instantiation expressions would need something like
::<type, args, here>
.
- Technically doesn't. Instantiation expressions would need something like
- How does this impact JS + Types?
- Feeling okay about this, but we have to be careful with call expressions,
new
expressions, decorators(?), JSX expressions(?), and tagged template expressions that are split across lines.
Optional Chaining and Instantiation Expressions
// Should be the same, right?
a?.b<c>.d
a?.b.d
- Looks like an emit bug, but our architecture around optional chaining is cooky.
- We don't mark whether something is an optional chain on its flags.
- Some errors only come up in
- First, does an instantiation expression terminate an optional chain?
- Feels like
<>
should just erase and not do anything.
- Feels like
- We can fix up the helpers.
- Is there a point to allowing this? Instantiation expressions only instantiate signatures.
-
We could just stop parsing after the
<>
- say it totally bails out. -
We usually do still keep things in the AST for error recovery purposes.
-
TECHNICALLY there is a point to allowing this because
this
namespace a { export class b<T> { d(this: T): T; } } a?.b<number>.d()
- But this never worked anyway - the compiler never funneled along the
this
type.
- But this never worked anyway - the compiler never funneled along the
-
But this worked.
namespace a { export class b<T> { <T>(): T; d<T>(this: T): T; } } a?.b<number>.d() // { (): number; d<T>(this: T): T }
-
- Conclusion:
ExpressionWithTypesArguments
cannot be
Referenced Import is Elided (and in JS too)
- User has a
.d.ts
file that exports a value as just a type. - When we emit the JS file (from an input JS file) which is used as a value, then
importsNotUsedAsValues
?preserveValueImports
?- Maybe JS takes a break and gets correct behavior, users can project-wide adopt the old behavior with
preserveValueImports
.
- Maybe JS takes a break and gets correct behavior, users can project-wide adopt the old behavior with
- Success criteria is a file with
// @ts-ignore
that works in JS should continue to work when emitted. - Conclusion
- Keep code the same in JS, never elide.
- In
checkJs
, it is an error to import a thing without a value meaning. - Don't make this conditional on the
preserveValueImports
flag. Try being aggressive.