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 the x couldn't be a Bar.
- 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
- Conclusion
- 👍
- Document that it's a breaking change.
The override keyword
#2000
#13217
- Without flag, feature is not extremely useful.
- What about
.d.ts files?
- Would users need
overrride
- 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.
- 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
#12424
-
New type operator: T extends U ? X : Y
- Breaks
T does to its union constituents (or T itself if not a union), checks if each of those types is assignable to U.
- When assignable, you get
X, otherwise Y.
- 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
Tseems to make the most sense.
-
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 and never?
any gives both branches of the conditional.
never gives never.
-
Question: Should T extends U be a separate type operator from X ? 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 write f < 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.
- Then could deconstruct types anywhere?
-
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.
inoperators as a type guardif, it"s not appropriate to say that thexcouldn't be aBar.neverin the false branch/else branch of the conditional.The
overridekeyword#2000
#13217
.d.tsfiles?overrrideoverridewhen flag is off.override.--strict?Accepting PRslast year.Conditional Types
#12424
New type operator:
T extends U ? X : YTdoes to its union constituents (orTitself if not a union), checks if each of those types is assignable toU.X, otherwiseY.Tis not known, the type remains unevaluated.Tis a union, this distributes over the union.Tseems to make the most sense.Can make some handy types!
How do these interact with
anyandnever?anygives both branches of the conditional.nevergives never.Question: Should
T extends Ube a separate type operator fromX ? Y : Z?Should we have
&&operators as well?`If we did that, it introduces some more syntactical ambiguity.
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?
inferoperator?type ReturnTypeOf<T> = from T infer R in (...args: any[]) => R: RTlike(...args: any[]) => R.Why did we move away from thinking about
matchtypes?Does this give you the
awaitedtype?