-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Element access with non-literal constant key should be narrowable #36230
Comments
If I recall correctly, @sandersn implemented control flow like this for literal element access, so at first glance it seems like it could be expanded to constant element access, but I’m not an expert in this area. |
Me neither 😀 |
I recall the cost of control flow analysis being much higher since there are many more element accesses with non-literal types than with literal types. But I could be wrong. It's worth making an experimental PR to see what the performance cost is. |
Yes:
I made that decision two years ago, so maybe something has changed since then. It's more likely that I was simply wrong and the performance hit is small, or worth it to get this feature. |
A particularly compelling case of this is when using const enum X{ key }
interface A { [X.key]: "A"; data_a: number }
interface B { [X.key]: "B"; data_b: number }
interface C { [X.key]: "C"; data_c: number }
// doesn't work
function getData_WITH_CONST_FROM_DECLARATION(thing: A | B | C) {
switch (thing[X.key]) {
case "A": return thing.data_a;
case "B": return thing.data_b;
case "C": return thing.data_c;
}
}
// works for only string or number literals
function getData_WITH_LITERAL(thing: A | B | C) {
switch (thing[0]) {
case "A": return thing.data_a;
case "B": return thing.data_b;
case "C": return thing.data_c;
}
} Also the title should probably be "allow constant but non literal key to be narrowable", non constant keys probably shouldn't work. |
If I'm understanding correctly, the inability to narrow using constant keys is also a significant limitation of symbol properties. These comments from #1863 suggest that using symbols as properties is fully supported, but code such as the following doesn't work: const Parent: unique symbol = Symbol('Parent');
const Name: unique symbol = Symbol('Name');
interface GenericGroup {
[Name]?: string;
}
interface Company extends GenericGroup {
}
interface Division extends GenericGroup {
[Parent]?: Company;
}
interface Team extends GenericGroup {
[Parent]?: Division;
}
type Group = Company | Division | Team;
function getName(group: GenericGroup): string {
if (group[Name] == null) {
return '';
}
// Error - "Type 'string | undefined' is not assignable to type 'string'"
return group[Name];
}
function getParentName(group: Group): string | undefined {
if (!(Parent in group)) {
return undefined;
}
// Error - "Element implicitly has an 'any' type because expression of type
// 'unique symbol' can't be used to index type 'Group'."
return group[Parent]?.[Name];
} |
Should the following behavior fall under the current issue or should it be its own?: const record: Record<string, number | string> = {};
const key = 4;
const constKey = 4 as const;
let stringValue: string;
record[4] = 'hello';
stringValue = record[4];
// @ts-expect-error - should be error because checker doesn't know what `key` is specifically
record[key] = 'hello';
stringValue = record[key];
record[constKey] = 'hello';
// Proposal: allow `record[constKey]` to be narrowed since `constKey` is a `const` key
stringValue = record[constKey];
// Bonus: hopefully implementing above would make the following work for free
if (key === 4) {
record[key] = 'hello';
stringValue = record[key];
} |
@lazytype it's the same; |
Hi, I think I'm running into this bug please let me know if I should post elsewhere:
Curiously if the member variable is a primitive type this works just fine:
|
Yep, that’s this. The second example works because you’re always allowed to compare |
@mbroadst yes that is the same issue, and |
TypeScript Version: 3.7.4 (also tried vNightly on the playground)
Search Terms: exhaustive type check array
Code
Error ts2532:
Workaround, but undesirable:
Expected behavior:
The exhaustive type checking mechanism in Typescript should know that neither
progress
norprogress[index]
are undefined inside the if statement, because I'm doing an explicit check for that.Actual behavior:
On
progress[index] >= 0
you'll get ts2532: Object is possibly 'undefined'Playground Link
Related Issues:
In #34661 the workaround seemed to be to use an intermediate variable, which will fix my code as well, while also making it more hairy.
The text was updated successfully, but these errors were encountered: