Closed
Description
in
operators as a type guard
interface Foo {
abc: number;
}
interface Bar {
def: string;
}
declare let x: Foo | Bar;
if ("abc" in x) {
x // Foo
}
else {
x // Bar
}
- Since types are open, in the true branch/then branch of the
if
, it"s not appropriate to say that thex
couldn't be aBar
.- But we already have this behavior for property access expressions, and most of the time these things are intent-based.
- Introduces a break to RxJS
- https://github.com/ReactiveX/rxjs/blob/efcd922384116b2a45e7d7859e09cf6da34d7371/src/observable/dom/AjaxObservable.ts#L464-L474
- Narrows value to
never
in the false branch/else branch of the conditional. - Declaration doesn't match reality on IE, but IE isn't following standard.
- Technically should have a different declaration for IE.
- Could introduce a type assertion/cast to get around this.
- Should we only do this on unions?
- No, need to be consistent with our narrowing behavior.
- Conclusion
- 👍
- Document that it's a breaking change.
The override
keyword
- Without flag, feature is not extremely useful.
- What about
.d.ts
files?- Would users need
overrride
- Would users need
- Allow users to use
override
when flag is off. - With a flag, it's an error to not use
override
. - Would it go in
--strict
?- Not certain if it feels appropriate.
- Generally, team feels ambivalent about whether the feature pays for its own complexity.
- We're fans of being able to add extra enforcement, but this is extremely tied to inheritance.
- Design time decorators? (Add Support for design-time decorators #2900)
- One day?
- Marked as
Accepting PRs
last year.- Need to be discuss things more in depth before we decide these things.
- Conclusion: can't accept at this time, would like to think more about annotations after decorators reach stage 3.
Conditional Types
-
New type operator:
T extends U ? X : Y
- Breaks
T
does to its union constituents (orT
itself if not a union), checks if each of those types is assignable toU
.- When assignable, you get
X
, otherwiseY
.
- When assignable, you get
- If
T
is not known, the type remains unevaluated. - Have been back and forth as to whether if
T
is a union, this distributes over the union.- In general, distributing over
T
seems to make the most sense.
- In general, distributing over
- Breaks
-
Can make some handy types!
type Diff<T, U> = T extends U ? never : T; type TypeName<T> = T extends string ? "string" : T extends number ? "number" : T extends boolean ? "boolean" : T extends undefined ? "undefined" : T extends Function ? "function" : T extends string ? "string" : "object"; type NonNullable<T> = Diff<T, null | undefined>; type DeepReadonly<T> = T extends object ? { readonly [P in keyof T]: DeepReadonly<T[P]> } : T; // Type: "a" | "d" type T0 = Diff<"a" | "b" | "c" | "d", "b" | "c">; // Type: "string" | "function" type T1 = TypeName<string | (() => void)>; // Type: string | number type T2 = Diff<string | number | (() => void), Function>; function f1<T>(x: T, y: NonNullable<T>) { x = y; y = x; }
-
How do these interact with
any
andnever
?any
gives both branches of the conditional.never
gives never.
-
Question: Should
T extends U
be a separate type operator fromX ? Y : Z
? -
Should we have
&&
operators as well?` -
If we did that, it introduces some more syntactical ambiguity.
- Currently we deal with this by saying "the ambiguities are rare for any useful code.
- But
f<T && U>(5)
, while a strange way to writef < T && U > 5
, feels odd. f<T && U>
-
What about inference for a given branch?
- A new
infer
operator? - Maybe something like
type ReturnTypeOf<T> = from T infer R in (...args: any[]) => R: R
- Note: would need a constraint on
T
like(...args: any[]) => R
.
- Note: would need a constraint on
- Then could deconstruct types anywhere?
- A new
-
Why did we move away from thinking about
match
types?- Kind of a "Swiss army knife" that does many things.
- Has curlies which can confuse folks that might think it ends up being an object type.
- Evaluation order might not be totally obvious to users, whereas conditionals are more explicit about order.
-
Does this give you the
awaited
type?- No.
- Why not?
- Evaluation of type aliases is eager, making them lazy would be difficult.
- But if the type acted on isn't concrete, aren't we deferring evaluation anyway?
- Maybe it does.
- Why not?
- No.