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
Type constraint is not respected when using nested generic types B<A> where A is a property type of B #27427
typeTest<Textendsstring=T>={value: T}// Why is this allowed?letzz: Test={foo: "abc"};// Type is Test<{}> ?!?
Something is not right here. We're allowing a type parameter to be its own default value (I'm not sure what that even means), but we seem to completely ignore the constraint when the type parameter isn't specified.
The assignment also works without the default (I only left the default in my example above to show that TypeScript actually inferred the type 'unknown' instead of 'number'):
exportinterfaceDocumentRef<Textendsstring>{type: T;}exporttypePartialDocument<RextendsDocumentRef<R['type']>>={[PinkeyofR]?: R[P];}consttest: PartialDocument<{type: number}>;// Works, but should give error: type 'number' is not assignable to type 'string'
However, if you add the extends constraint of the DocumentRef generic as an intersection type to the property declaration bearing the generic type, it works as I would expect:
exportinterfaceDocumentRef<Textendsstring>{type: T&string;// <-- note the intersection type}exporttypePartialDocument<RextendsDocumentRef<R['type']>>={[PinkeyofR]?: R[P];}consttest: PartialDocument<{type: number}>;// Error: type 'number' is not assignable to type 'string'
Anders Hejlsberg (@ahejlsberg) your issue is different than timargra 's, for sure. I have a fix ready for yours (its small - #28222), however the root cause' of timargra 's is hard to fix. When we check R extends DocumentRef<R['type']>, we validate that for all R, R['type'] is assignable to the constraint string. (Because of the circular definition, it clearly is - a DocumentRef's type member must always be some T and that T is constrained to string). Unfortunately, when checking the instantiation of PartialDocument, we instantiate the type parameters with the arguments provided - so R becomes {type: number}, so R['type'] is number. {type: number} trivially extends a DocumentRef<number>. Nowehre do we actually check that it is actually safe to instantiate the type parameters with the arguments, because we're in the middle of performing that validation! The first thing we do is assume it's true, and check if the arguments hold if it is; but in doing so we miss that an inner constraint has been violated by the instantiation.
Activity
ahejlsberg commentedon Sep 28, 2018
Smaller repro:
Something is not right here. We're allowing a type parameter to be its own default value (I'm not sure what that even means), but we seem to completely ignore the constraint when the type parameter isn't specified.
timargra commentedon Oct 1, 2018
The assignment also works without the default (I only left the default in my example above to show that TypeScript actually inferred the type 'unknown' instead of 'number'):
However, if you add the extends constraint of the DocumentRef generic as an intersection type to the property declaration bearing the generic type, it works as I would expect:
weswigham commentedon Oct 30, 2018
Anders Hejlsberg (@ahejlsberg) your issue is different than timargra 's, for sure. I have a fix ready for yours (its small - #28222), however the root cause' of timargra 's is hard to fix. When we check
R extends DocumentRef<R['type']>, we validate that for allR,R['type']is assignable to the constraintstring. (Because of the circular definition, it clearly is - aDocumentRef's type member must always be someTand thatTis constrained tostring). Unfortunately, when checking the instantiation ofPartialDocument, we instantiate the type parameters with the arguments provided - soRbecomes{type: number}, soR['type']is number.{type: number}trivially extends aDocumentRef<number>. Nowehre do we actually check that it is actually safe to instantiate the type parameters with the arguments, because we're in the middle of performing that validation! The first thing we do is assume it's true, and check if the arguments hold if it is; but in doing so we miss that an inner constraint has been violated by the instantiation.jcalz commentedon May 28, 2022
Looks like this got fixed sometime, not sure where or when