-
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
Double errors reported with control flow based checking in a loop #8404
Comments
The assignments in the loop body are analyzed twice because of the back branch. Both times we have to evaluate the Now, the problem we run into here is that when we apply the
Not quite sure which is better. (1) better preserves facts we have already deduced, but it seems a bit arbitrary. (2) is more predictable but possibly irritating with more complex types such as |
@mhegazy Opinions? |
BTW, to avoid the error you'd currently have to write: function test<U>(xs: U[], combine: (a: U, b: U) => U) {
let hasValue = false;
let value = <U | undefined>undefined; // Force initial type to be U | undefined
for (const x of xs) {
if (hasValue) {
value = combine(value!, x);
} else {
hasValue = true;
value = x;
}
}
} Or, alternatively: function test<U>(xs: U[], combine: (a: U, b: U) => U) {
let hasValue = false;
let value = <U><any>undefined; // Declared and initial are both U
for (const x of xs) {
if (hasValue) {
value = combine(value, x); // Don't need !
} else {
hasValue = true;
value = x;
}
}
} Neither of these are great, but they illustrate the issue. |
Wouldn't it be better to only show errors from the last pass? I think it can be very confusing to get multiple errors on the same location, with different types mentioned in those errors. |
Option 2 seems too heavy handed. I would think that most o the time narrowing works. it is these cases where we narrow to |
With #8429 you can now write your example as: function test<U>(xs: U[], combine: (a: U, b: U) => U) {
let hasValue = false;
let value: U | undefined;
for (const x of xs) {
if (hasValue) {
value = combine(value!, x); // No error here
} else {
hasValue = true;
value = x;
}
}
} The double errors also go away, but that's mostly a side effect of the declared and initial types being the same. |
I believe this should not be an issue now with #8429. please reopen if not the case. |
It did solve my simplified example, but when I tested in on the real code it still fails 😒. So here is the full code that I use: export interface Storage<TKey, TValue> {
createStore(values: Map<TKey, TValue>, parents?: Store<TKey, TValue>[]): Store<TKey, TValue>;
get(store: Store<TKey, TValue>, key: TKey): TValue;
}
export interface Store<TKey, TValue> {
values: Map<TKey, TValue>;
parents: Store<TKey, TValue>[];
}
export function createStorage<UKey, UValue>(defaultValue: (key: UKey) => UValue, union: (a: UValue, b: UValue) => UValue): Storage<UKey, UValue> {
type UStore = Store<UKey, UValue>;
return { createStore, get };
function createStore(values: Map<UKey, UValue>, parents: Store<UKey, UValue>[] = []): UStore {
return { values, parents };
}
function get(store: UStore, key: UKey) {
const stack = [store];
let hasValue = false;
let currentValue: UValue | undefined = undefined;
while (stack.length !== 0) {
const s = stack.pop()!;
if (s.values.has(key)) {
const value = s.values.get(key)!;
if (hasValue) {
currentValue = union(currentValue!, value);
} else {
hasValue = true;
currentValue = value;
}
} else {
stack.push(...s.parents);
}
}
if (!hasValue) {
currentValue = defaultValue(key);
}
store.values.set(key, currentValue);
return currentValue!;
}
} This shows an error on the line that calls |
the issue is the issue here, is |
Thanks, I got it working now. Still I think it's counterintuitive. This reminds me of the idea to separate type resolving from error reporting. It was mentioned in the control flow checking PR, because getting the type of For this issue, that would mean that the value is resolved to |
In a loop, multiple errors are sometimes reported on the same location, but with slightly different types.
@ahejlsberg Any idea what's going on?
TypeScript Version:
nightly (1.9.0-dev.20160430)
Code
Expected behavior:
Only one error should be reported:
By adding an exclamation mark to that line (
value = combine(value!, x);
), no error should be reported.Actual behavior:
Two errors are reported:
Adding an exclamation mark gives:
The text was updated successfully, but these errors were encountered: