TypeScript Version: 3.0.0-dev.20180623
Search Terms: typescript unknown equality guard
Code
let x: unknown; // reproduces with const too
if (x === 5) {
let y = x.toString(10);
}
Expected behavior:
x inside of the if block to be of type 5, y to get type of string, everyone happy.
Actual behavior:
x inside of the if block is still of type unknown, I get an error for x being of type unknown and therefore does not have a property toString.
Playground Link: N/A (dev build)
Related Issues: #24439
Related to #9999 also?
@jcalz Not exactly, that there is a feature request, unknown can be narrowed in other ways. The following is the result on the TypeScript version mentioned at the top of this issue:
let x: unknown; // unknown always considered initialized.
if (Array.isArray(x)) {
x; // any[] (I would expect unknown[], but that's a different issue)
} else if (x instanceof Promise) {
x; // Promise<any> (again, would expect Promise<unknown> but eh)
} else if (x === true) {
x; // unknown, wat.
}
let y = String(x); // y: string, as expected
let z = Number(x); // z: number, as expected
So you see, the only real "wat" here is the direct equality comparison.
It's also worth noting that this behavior is consistent with any (That is, attempts to narrow any with equality are met with any as the narrowed type), but this is still rather unexpected behavior.
@DanielRosenwasser === today doesn't narrow anything other than members from unions or primitives into literals today. unknown is neither a primitive nor a union, so this is expected under our current rules, just like how {} and any won't be narrowed by ===, either.
This is especially helpful for narrowing unknown to a string enum
type Response = 'yes' | 'no' | 'idk';
let validate: (x: unknown) => Response = x => (x === 'yes' || x === 'no') ? x : 'idk'; // Err: type 'unknown' is not assignable to type '"idk"'
Accepting PRs assuming there isn't any big impact to perf or complexity
Attempt at a PR up at #26941
Question: why just unknown, shouldn't any work the same way?
--strict
let a: any = Math.random();
if (a === null) {
// a is null type, not any type
}
Most helpful comment
@jcalz Not exactly, that there is a feature request,
unknowncan be narrowed in other ways. The following is the result on the TypeScript version mentioned at the top of this issue:So you see, the only real "wat" here is the direct equality comparison.
It's also worth noting that this behavior is consistent with
any(That is, attempts to narrowanywith equality are met withanyas the narrowed type), but this is still rather unexpected behavior.