Closed
Description
Triple-Slash Reference Emit/Preservation (motivated by --isolatedDeclarations
)
- One stance we had with
--isolatedDeclarations
is that the flag should not affect emit itself. - One question around this: what do we do with
/// <reference >
directives? - When doing declaration file emit, TypeScript will try to insert
- Let's say you have a statement like
export * as fs from "fs"
.- TypeScript will emit a triple-slash reference to say
/// <reference types="node" />
- TypeScript will emit a triple-slash reference to say
- One idea for
--isolatedDeclarations
is to say that when TypeScript would do this, it should emit an error. In other words, the user has to write it out explicitly. - Problem with this is that TypeScript occasionally removes these directives as well when they're not needed.
- This all feels pretty complicated.
- Suggestion: preserve triple-slash references when they're written, never generate them.
- Thinking we could do this in 6.0.
- The other option would be an attribute on
///
references that allows people to control whether the reference is preserved, elided, or whatever. - Do we know how often we generate these in the top200 repos?
- No
- Until recently, preservation wasn't perfect anyway - so maybe people aren't relying on it?
- One strong preference: what you see is what you get.
- If you inserted one of these references, you had to make sure you had the right types installed. The difference is a UX issue of what makes the error more obvious.
- Can view
///
refs as a holdover from a time long-past. Don't ascribe them more value than they deserve! - What about the non-types references?
/// <reference types="..." />
/// <reference paths="..." />
- Do we synthesize?
- types: currently sometimes yes.
- paths (aka file references): hopefully no?
- Do we elide?
- types: currently also sometimes yes.
- paths (aka file references): hopefully no?
- Do we generate
/// <reference paths="..." />
- So we want to make these changes. But when?
- One idea floated was to make sure that this only kicks in within
--isolatedDeclarations
, and gets turned on for everyone in 6.0
- One idea floated was to make sure that this only kicks in within
- If we're not going to synthesize references, it might be appropriate to tell library authors that they need to preserve that?
- A little gun-shy on including this in 5.5.
--verbatimReferenceDirectives
, require this in--isolatedDeclarations
, change it in 6.0...- And then deprecate
--verbatimReferenceDirectives
in 6.0? 😂
- And then deprecate
- We'd like to see how common this is.
- Nightly?
- Nobody is going to publish on nightly!
- Conclusion: Let's try to create an error when synthesizing, see what breaks on the top 400 repos, and then decide if we need a new flag.
Inferring Type Predicate Signatures from Function Bodies
-
Motivating example.
const arr = [1, 2, "a", "b", undefined]; const numbers = arr.filter(x => typeof x === "number"); const el1: number = numbers[0]
-
Today that errors because the
filter
doesn't work. -
Solution is to explicitly make the signature a type predicate
- const numbers = arr.filter(x => typeof x === "number"); + const numbers = arr.filter((x): x is number => typeof x === "number");
-
PR makes it so these signatures can be automatically infer.
- Functions
- with no explicit return type
- that return a
boolean
- with a single return expression.
- Functions
-
Analysis cost?
- Most repos had no perf cost.
- But occasionally there was a cost.
- Cost to detect the guards.
- Cost to deal with the usage of type guards.
- In experiments, most of the cost comes from detecting if functions are type guards.
- More from the usage in type guards.
-
This has been a long-standing request - people really don't like that
filter
doesn't work. -
What happens when multiple variables get narrowed?
- Seems like it's a bail-out.
- Good, that's what we want.
- Also, the false branch of a
(x is number AND y is number)
can't guarantee that the variables aren't bothnumber
. Kind of hard to reason about.
- Also, the false branch of a
-
What broke?
-
Inability to express comparability bounds:
declare const cat: Cat; declare const arr: Animal[]; const dogs = arr.filter(a => isDog(a)); dogs.indexOf(cat);
-
Inlining in Pyright.
- Notably, not a filter!
-
-
Could argue that so many libraries that have their own
isNotNull
etc. exist only because function expressions can't narrow. -
In some ways this is way better because type predicates are not checked based on how you've narrowed! But inferring is actually way better!
- ...should we make sure that they are?
- We would like to. Gotta find a checking opt-out.
-
What are the downsides?
- Lots of expressions simply won't get a type guard inferred.
- "What about
!!x
?"- No helper for
Truthy
.
- No helper for
- "What about
- Lots of expressions simply won't get a type guard inferred.