You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
functionhasOwnProperty<Xextends{},YextendsPropertyKey>(obj: X,prop: Y): obj is X&Readonly<Record<Y,unknown>>{returnobj.hasOwnProperty(prop)}constisObject=(val: unknown): val is object=>val!==null&&typeofval==='object'constisString=(val: unknown): val is string=>typeofval==='string'declareconstx: unknownconstisAnObject=isObject(x)consthasValidName=isAnObject&&hasOwnProperty(x,'name')&&isString(x.name)if(hasValidName){constres0=x// object & Readonly<Record<"name", unknown>>constres1=x.name// string}
If it can help, I generated a .types file as well.
See the .types file contents.
=== tests/cases/compiler/_baguette.ts ===
function hasOwnProperty<X extends {}, Y extends PropertyKey>
>hasOwnProperty : <X extends {}, Y extends PropertyKey>(obj: X, prop: Y) => obj is X & Readonly<Record<Y, unknown>>
(obj: X, prop: Y): obj is X & Readonly<Record<Y, unknown>> {
>obj : X
>prop : Y
return obj.hasOwnProperty(prop)
>obj.hasOwnProperty(prop) : boolean
>obj.hasOwnProperty : (v: PropertyKey) => boolean
>obj : X
>hasOwnProperty : (v: PropertyKey) => boolean
>prop : PropertyKey
}
const isObject = (val: unknown): val is object => val !== null && typeof val === 'object'
>isObject : (val: unknown) => val is object
>(val: unknown): val is object => val !== null && typeof val === 'object' : (val: unknown) => val is object
>val : unknown
>val !== null && typeof val === 'object' : boolean
>val !== null : boolean
>val : unknown
>null : null
>typeof val === 'object' : boolean
>typeof val : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>val : unknown
>'object' : "object"
const isString = (val: unknown): val is string => typeof val === 'string'
>isString : (val: unknown) => val is string
>(val: unknown): val is string => typeof val === 'string' : (val: unknown) => val is string
>val : unknown
>typeof val === 'string' : boolean
>typeof val : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>val : unknown
>'string' : "string"
declare const x: unknown
>x : unknown
const isAnObject = isObject(x)
>isAnObject : boolean
>isObject(x) : boolean
>isObject : (val: unknown) => val is object
>x : unknown
const hasValidName = isAnObject && hasOwnProperty(x, 'name') && isString(x.name)
>hasValidName : boolean
>isAnObject && hasOwnProperty(x, 'name') && isString(x.name) : boolean
>isAnObject && hasOwnProperty(x, 'name') : boolean
>isAnObject : boolean
>hasOwnProperty(x, 'name') : boolean
>hasOwnProperty : <X extends {}, Y extends PropertyKey>(obj: X, prop: Y) => obj is X & Readonly<Record<Y, unknown>>
>x : object
>'name' : "name"
>isString(x.name) : boolean
>isString : (val: unknown) => val is string
>x.name : unknown
>x : object & Readonly<Record<"name", unknown>>
>name : unknown
if (hasValidName) {
>hasValidName : boolean
const res0 = x
>res0 : object & Readonly<Record<"name", unknown>>
>x : object & Readonly<Record<"name", unknown>>
const res1 = x.name
>res1 : string
>x.name : string
>x : object & Readonly<Record<"name", unknown>>
>name : string
}
🙁 Actual behavior
The type of the name property of x is inferred as unknown (cf. res0).
However, when accessing the property with x.name (cf. res1), its type is correctly inferred as string.
🙂 Expected behavior
The name property should be inferred as string when checking the properties of x.
For example, as a developer, when typing x., I'm expecting to see name: string in the "auto-completion" list, instead of name: unknown.
The text was updated successfully, but these errors were encountered:
Not related to aliased conditions; inlining the conditions into the if has the same result. When we narrow x.name, we don’t actually incorporate that information back into the type of x; we only see that narrowing when we get the type of a fully-matching reference to x.name. I agree the completion list behavior is confusing—it looks like we’re showing the declared type, not the control flow type, of each property. (Or more likely, we are asking for the control flow type, but because there is no actual referencing node yet, the narrowing logic doesn’t see that there’s a hypothetical matching reference to the property that can be narrowed. This is often a problem with completions, where the checker is ill-adapted to thinking about the type state that will happen if some code were inserted.) But if that’s the case, it would likely be prohibitively expensive to try to do narrowing on every property in completion lists. 😕
Why would it be "prohibitively expensive" to try narrowing the type in such situation? Is it because a property could have a "deep" narrowed type, which would take a significant amount of time to the type checker?
Do you think we could somehow force the type narrowing anyway? Via some editor plugin, or a mystical TS configuration maybe 😛
(also I guess we can close this issue as there isn't any actual bug)
Completions lists need to be really fast since they’re an interactive editor feature that pop up as you type, and control flow narrowing can be expensive. You’d have to repeat the process for every single property in the list, so it’s an extremely hot path. Even if it’s only a little bit more work per item, you’re often multiplying that work by 10x–100x (the size of the list) as the user types.
Bug Report
🔎 Search Terms
type inference, inference, aliased condition, aliased conditional expression
🕗 Version & Regression Information
It started happening when the aliased conditional expressions were introduced in the following pull-request:
This issue is still present as of v4.4.2.
I was unable to test this on prior versions because aliased conditional expressions didn't exist previously.
⏯ Playground Link
Playground link with relevant code.
💻 Code
If it can help, I generated a
.types
file as well.See the .types file contents.
🙁 Actual behavior
The type of the
name
property ofx
is inferred asunknown
(cf. res0).However, when accessing the property with
x.name
(cf. res1), its type is correctly inferred asstring
.🙂 Expected behavior
The
name
property should be inferred asstring
when checking the properties ofx
.For example, as a developer, when typing
x.
, I'm expecting to seename: string
in the "auto-completion" list, instead ofname: unknown
.The text was updated successfully, but these errors were encountered: