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
{{ message }}
Repository navigation
this type is not assignable to NonNullable<this> #22934
classX{fn(): void{// Type 'this' is not assignable to type 'NonNullable<this>'.// Type 'X' is not assignable to type 'NonNullable<this>'.constx: NonNullable<this>=this;// ~}}
Expected behavior:
A reference of type this should be assignable to a NonNullable<this> within a method.
I may have whittled down the actual issue I was dealing with too much. Here is an example of the bug I was trying to be succinct with:
exportclassEntry<TextendsTable>{private_table: T|null=null;createSubsetForDirectory(): void{constentry=newEntry<T>();// error TS2345: Argument of type 'Entry<T>' is not assignable to parameter of type 'Entry<NonNullable<T>>'.// Type 'T' is not assignable to type 'NonNullable<T>'.// Type 'Table' is not assignable to type 'NonNullable<T>'.this._table!.fn(entry);// ~~~~~}}exportabstractclassTable{fn(directoryEntry: Entry<this>): this |null{returnnull;}}
strictNullChecks must be enabled for the error to appear.
Yes, I did make a change where distributive conditional types are always deferred if the checked type is generic, but I think it stayed generic before as well.
It may be that that ! operator should do a bit more work, i.e. remove null and undefined that are known to be present (as we used to do) and only return a NonNullable<T> if the resulting T could actually be nullable (which wouldn't be the case in the example above after we remove explicit null and undefined). I'm not sure it is feasible to have the NonNullable<T> type collapse as you suggest.
only return a NonNullable if the resulting T could actually be nullable
I'm pretty sure it'd be correct to (re)build that logic into conditional types in general at that point, since nothing about that logic would be specific to either null or undefined. One of the motivating examples for conditional types is non-null types; it'd be off that ! could do the right thing but a conditional type couldn't.
And again, the original issue doesn't involve ! - the postfix operator is just another way to witness the same problem!
Wesley Wigham (@weswigham) In principle I agree, but devising the exact logic by which this would happen isn't trivial. We currently defer resolution for distributive conditional types when the check type is generic. Even in cases where we know that the check will always go a certain way (e.g. because of a constraint), we still need to defer because the generic type may be instantiated to a union type that we want to distribute over. Only when we can conclude that distributing over a union wouldn't affect the outcome can we actually consider resolving early. I think it would pretty much amount to recognizing Exclude- and Extract-like types. It may be worth doing, but building it into the getNonNullableType function would definitely be simpler.
Activity
aschmois commentedon Mar 27, 2018
That's because a generic is a class definition not an instance. this is an instance. Your code should look like:
RyanCavanaugh commentedon Mar 27, 2018
Nathan Shively-Sanders (@sandersn) seems like a bug but I might be missing something extra-subtle
aschmois commentedon Mar 27, 2018
Why should dynamic objects be allowed as generic definitions?
athasach commentedon Mar 28, 2018
I may have whittled down the actual issue I was dealing with too much. Here is an example of the bug I was trying to be succinct with:
strictNullChecksmust be enabled for the error to appear.Playground Link:
http://www.typescriptlang.org/play/#src=export%20class%20Entry%3CT%20extends%20Table%3E%20%7B%0D%0A%20%20%20%20private%20_table%3A%20T%20%7C%20null%20%3D%20null%3B%0D%0A%20%20%20%20createSubsetForDirectory()%3A%20void%20%7B%0D%0A%20%20%20%20%20%20%20%20const%20entry%20%3D%20new%20Entry%3CT%3E()%3B%0D%0A%20%20%20%20%20%20%20%20this._table!.fn(entry)%3B%0D%0A%20%20%20%20%7D%0D%0A%7D%0D%0A%0D%0A%0D%0Aexport%20abstract%20class%20Table%20%7B%0D%0A%20%20%20%20fn(directoryEntry%3A%20Entry%3Cthis%3E)%3A%20this%20%7C%20null%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20null%3B%0D%0A%20%20%20%20%7D%0D%0A%7D%0D%0A
athasach commentedon Mar 28, 2018
Andres Schmois (@aschmois) In this case, I'm using
thisas a polymorphic this type. See http://www.typescriptlang.org/docs/handbook/advanced-types.html#polymorphic-this-typesRyanCavanaugh commentedon Mar 28, 2018
Kent Wong (@athasach) that's actually a much more serious repro as it's a regression from 2.7. That code worked as far back as 2.0
ahejlsberg commentedon Mar 28, 2018
Pinging Wesley Wigham (@weswigham). I suspect this is related to #22096.
3 remaining items
ahejlsberg commentedon Mar 28, 2018
Yes, I did make a change where distributive conditional types are always deferred if the checked type is generic, but I think it stayed generic before as well.
It may be that that
!operator should do a bit more work, i.e. removenullandundefinedthat are known to be present (as we used to do) and only return aNonNullable<T>if the resultingTcould actually be nullable (which wouldn't be the case in the example above after we remove explicitnullandundefined). I'm not sure it is feasible to have theNonNullable<T>type collapse as you suggest.weswigham commentedon Mar 28, 2018
I'm pretty sure it'd be correct to (re)build that logic into conditional types in general at that point, since nothing about that logic would be specific to either null or undefined. One of the motivating examples for conditional types is non-null types; it'd be off that
!could do the right thing but a conditional type couldn't.And again, the original issue doesn't involve
!- the postfix operator is just another way to witness the same problem!ahejlsberg commentedon Mar 28, 2018
Wesley Wigham (@weswigham) In principle I agree, but devising the exact logic by which this would happen isn't trivial. We currently defer resolution for distributive conditional types when the check type is generic. Even in cases where we know that the check will always go a certain way (e.g. because of a constraint), we still need to defer because the generic type may be instantiated to a union type that we want to distribute over. Only when we can conclude that distributing over a union wouldn't affect the outcome can we actually consider resolving early. I think it would pretty much amount to recognizing Exclude- and Extract-like types. It may be worth doing, but building it into the
getNonNullableTypefunction would definitely be simpler.jakebailey commentedon Jul 11, 2023
This one was fixed by the infamous #49119, as that changed the definition of
NonNullable<T>toT & {}.