-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Typescript loses refinement information when overwriting a local variable with its same type #36579
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
Comments
I don't think #27706 is relevant, as this example doesn't require previous narrowings to be saved in order to work. The core problem, probably related to #16976, is that typescript doesn't narrow on arbitrary assignments, so this: item = example(item); does not produce any narrowing. You only get that behavior in certain cases with a union: type A = { a: number }
type B = { b: number }
function f(ab: A | B) {
ab = { a: 4 }
ab; // A. In this special case with a union, narrowing happens
}
function i(a: A) {
a = 0 as any as A & B;
a; // still A, not A & B
}
function p(n: number) {
n = 4;
n; // still number, not 4 or number & 4
} I think this doesn't happen for performance reasons, but I can't find a ticket that mentions it in more detail. |
It's not that TS doesn't narrow on assignment, it's that it appears to re-widen the type. The call |
@ahejlsberg this seems a little off here: interface A { _a: true }
declare function isA(item: any): item is A;
declare function identity<T>(input: T): T;
function exampleUsage<T>(item: T) {
if (isA(item)) {
// ok
item._a;
item = identity(item);
// error
item._a;
}
if (isA(item)) {
const item2 = identity(item);
// ok
item._a;
// ok
item2._a;
}
} |
Well, this one's tricky. The TLDR is that TypeScript, intentionally, has different rules around narrowing and initialization inference. The behavior follows from these rules, none of which are obviously wrong:
I think rules 1 and 2 are fairly self-evident. Rule 3 is trickier and requires thinking about what life would be like if this weren't the case. The intuition is that the inferred type of this function should be function getSomething() {
let x: HTMLElement;
if (Math.random() > 0.5) {
x = getSomeDiv();
} else {
x = getSomeSpan();
}
return x;
} and that the return type of this function should be function getSomethingElse() {
// Initializer has type string | number
let s = getSomeStringOrNumber();
if (typeof s === "number") {
s = s.toFixed();
}
return s;
} |
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
When you refine a variable with a user defined type guard, and then reassign that variable to the result of a function returning the same type, TS loses track of the refined type.
Search Terms:
refinement
Code
Expected behavior:
In the final case, because the function
example
returns the same type as it was provided, TS should keep track of item's refined type.Actual behavior:
TS loses track of the fact that
item
has previously been refined to an instance ofA
.Playground Link: http://www.typescriptlang.org/play/#code/JYOwLgpgTgZghgYwgAgILIN7IPpwFzJhQCuKAvsgFCUID2IAzmMsA+gLzIAUwkAtgTggAngEoCvCHxYM0ydgD5CJCAG5qAEwgIANnCgo6jZjFq1BI9ZRjEQCMMHrIIADzh8ADjogAeACrOLpAgGrIYZAo8IB7EYAR+4sgBWJTIacgGYMRQICzRsepk1DZ2Dk6u7l4QAKoMcADmvgGuwaGYETz88aKYqenAMNysqJ1Soj0YfenpAPQzyADutFAA1gxT08hGDLTeAHQ6tPWjfHu4ourTRRuS0pwVnt4nF9TTA0NszxMb03OLy2sfultrsIAcjicznAXldXv1BjxPrdxr1Nv1+PJApUnsjLmitvQdvtDsdblCYekimQgA
Related Issues: n/a
The text was updated successfully, but these errors were encountered: