Description
Call-site inference for implicit any
s
This is a demo presentation
- [[Opens up minimatch]]
- "This is the codebase you open up to make yourself feel good about JavaScript"
- Looking at the
charSet
declaration function- We have
noImplicitAny
on - See that it was at some point called with a
string
, and so we change the parameter type. - If we observe a circularity, we fall back to
any
and report an error.
- We have
- Is this TypeScript too?
- It works for either JavaScript or TypeScript
- How bad is it?
- The graphs are confusing, but it's over 4.5x slower to look at how the function itself is called to figure out the parameter types.
- If you only look at use-sites of parameters within the bodies of functions, it'x ~1.8x slower.
- We mostly imagine that this would be a one-time shot tool
- Could also imagine that we'd improve the performance here, but it's decent
- Did we look at the tests at all? You'd figure unit tests would be great for figuring out use-site.
- But that's a problem for stupid tests like "assert that this throws when I pass in a number"
- But you get 40% more type coverage on Webpack.
- That's like half the way there.
- But even that's not worth slowing down the type-checker so dramatically.
- Seems like we should be leveraging this somehow.
Weird behavior for spreading arrayish types
-
We now allow rest parameters to have a generic type (
T
).- The requirement has been that that generic type needs to extend an array (e.g.
T extends any[]
).
- The requirement has been that that generic type needs to extend an array (e.g.
-
But you can come up with several things that actually satisfy arrays.
-
Object subtypes:
interface CoolArray extends Array<string> function foo<T extends any[]>(...args: T): T { throw 0; } foo<CoolArray>();
-
-
Problem: what does it mean to spread in these object subtypes?
- For these object subtypes, any argument list translated to an array or a tuple won't ever satisfy these types.
-
Solution: if you ever have a type parameter constrained to something other than an
Array
orReadonlyArray
, and you are trying to relate to a generic rest parameter, you infer a tuple type from the matching argument and try to infer from that.- If that inference fails, TypeScript falls back to the constraint and relates against that.
-
Union subtypes:
function foo<T extends [] | [number, number]> (...args: T): T { throw 0; } foo<CoolArray>();
Named type arguments, associated types, and partial inference
-
In Named Type Arguments & Partial Type Argument Inference #23696, we realized that making every type parameter nameable would be problematic for existing APIs - it exposes what were thought of as mostly implementation details.
-
What problems were we trying to solve?
- Positional type arguments are problematic because it locks you down.
- Using a type parameter as a default for a temporary/local type.
StrictEventEmitter
- "Is this a real type!?"
- Specialized subclasses:
```ts class Box<T> { x: T } class SpecialBox extends Box<Speical> // How do I specialize this further without making SpecialBox generic? class SpecialSpecialBox extends SpecialBox { } ```
-
At a high level, we want to be able to place types within types.
declare class BoxObserver<T> { type Boxified = { value: T }; get(): Boxified; } let x: BoxObserver<number,
-
Hold up though, that means in this class you can't write the following, since you don't know if
Boxified
will be further specialized.let x: T = this.get().value
-
-
Feels strange that these can appear in the type parameter list.
-
One of the core differences is that we also don't do inference on these types.
-
Also, these must be associated with the instance.
- They must be, because they close over the instance type parameters.
-
If changing the type in the subtype changes the behavior in the supertype, then it must be a type variable.
-
But there are merits to just having this as an alias.
-
Could imagine
declare class Foo<T> with Boxified = { value: T } { get(): Boxified; }
-
-
Also, feels strange that there is some mixing of type parameters and aliases:
type Foo<T> = type X = Foo<T>[] in { // ... };