Closed
Description
More errors on never
enum Foo {
a = "A",
b = "B",
}
function fn(obj: { blah: any }) {
obj.blah;
}
function f(x: Foo) {
let obj = {};
switch (x) {
case Foo.a:
case Foo.b:
break;
default:
console.log("??");
x.blah;
obj[x];
fn(x);
}
}
- What is the motivation here?
- Seems like you potentially want to know when you
- "From the user's perspective, a
never
that never is equivalent toany
. - The error on the dot is highly desirable.
- The real problem here is not that the user is using
never
, it's that we don't signal reachability analysis based on control-flow analysis.- We could do this.
- Perhaps when you check the first statement?
- How would you do this while allowing defensive code?
- What about throwing a new error, or using
assertNever
?- What about if we allowed a trailing expression statement that returns
never
? - Ehh, all of this becomes questionable and sketchy.
- What about if we allowed a trailing expression statement that returns
- What about throwing a new error, or using
- When you have
type T = { x: string, y: number };
T['x' | 'y']
isnumber | string
T['x']
isstring
T[never]
isnever
- We have some strange behavior compared to how these operate in the value space.
- Conclusion: we don't believe any of these should be errors.
Assignability for both conditional type branches
- Doing erasure in the
infer
positions is hard because we don't know whether they're actually co- or contra-variant, so we don't know if we're doing the right thing.- It is unsound to erase to
unknown
. - This is technically a bug.
- Instead of erasing, we should run a different sort of inference?
- It's during instantiation that
- It is unsound to erase to
- Slightly uneasy about the higher order cases where you might get instantiated with
never
.
type Foo<T> = T extends number ? "dog" : string;
let x: Foo<number> = "dog";
let y: Foo<never> = "dog";
- Conclusion
- Want to take this change without the
infer
positions - But also want to fix the variants checking.
- Want to take this change without the
Enum literal freshness
enum E {
A, B, C
}
const x = E.B;
- Previously, if we wrote
let y = x
, we would widen toE
, which is expected. - However, if you used an explicit annotation like
let y: E.A = E.A
.
Generalized index signatures
- Today, you can't represent index signatures using symbols - that's the real semantic thing that's missing.
- Beyond this, much of the functionality appears to be syntactic sugar for constructs that are possible to write today.
- You can't represent these all of the cases in higher order.
- Not excited about pouring more into index signatures when mapped types exist.
- It's syntactic sugar, but there's a conceptual gap between what an index signature should be able to represent, and what a mapped type brings to them.
- People often think they can put arbitrary types in index signatures.
- People often think they can use mapped type syntax to achieve that in interfaces, but often don't.
- You can tell people to
extend Record<SomeUnionOfStrings, SomeOutputType>
, but it's not easy to get there.
- Conclusion: let's iterate to see how we can unify these concepts before we add anything new.