Repository navigation
redux-orm broken by #43624 #43867
Description
Activity
I mean, the difference is that we're relating models by variance now, whereas before it was structural. Technically it's a regression, but I assume the actual issue is that the variance of the first type parameter is incorrectly measured as contravariant.
sandersn commented
on Apr 28, 2021 MemberAuthorMore actionsSo this is a case of fixing a bug, which exposes another bug? =(
So, I minified this down and came to the conclusion... that the error is actually, sort-of, technically correct. The
Modelclass should actually be invariant on itsMClasstype parameter (and any time we measure otherwise, strange edgecase of our typesystem is taking place) - it has agetClassfunction which returns (a conditional whose result changes based on a covariant derivative of) that type variable, and asetfunction which takes (a conditional whose result changes based on a contravariant derivative of) that type variable. Those combine to makethisnot assignable toModel<typeof Model>(the constraint ofRef) becausethisis some-arbitrary-subtype ofModel, which therefore might resolve to differing branches in either of those two other members.Here's a greatly simplified comparison, take this:
type ModelId<M extends ModelSub> = M; // just validates the input matches the `Model` type to issue an error export declare class Model<MClass extends typeof ModelSub = typeof ModelSub> { class: MClass; readonly ref: ModelId<this>; set<K>(value: K extends ModelId<this>['class'] ? number : string): void; } export declare class ModelSub extends Model {}
and compare to
type ModelId<M extends Model> = M; // just validates the input matches the `Model` type to issue an error export declare class Model<MClass extends typeof Model = typeof Model> { class: MClass; readonly ref: ModelId<this>; set<K>(value: K extends ModelId<this>['class'] ? number : string): void; }
(so, the same, but with
SubModeleliminated as a redundant, empty type). TS prior to #43624, we only issued an error on the inline'd (second) version, whereas now we appropriately issue an error on both! So at a minimum, we're certainly more consistent now. The question then becomes: "Is the error in the second version (and now in both versions) correct?" which, as I said above, in this simplified example, it's pretty easy to tell it could be construed to be -classis a covariantMClassusage, whilesethas an identity-requiring one (via the index onthisinside a conditionalextends).Prior to the change, what'd happen is that the conditional in the
setsignature would be resolved on one side of the comparison (since the empty subtype has fully resolved type arguments, so there's no generics and no reason to defer the conditional) to a union of the possible outcomes, while on the source side, signature relating would erase thecheckTypeto any, making the extends type irrelevant in the comparison, and allowing the default constraint to allow the conditionals to be assigned across, even though the branches might not overlap perfectly. Whereas, without the empty subtype (in all versions of TS, old and new), we launch into a variance measurement process for theModeltype, which determinesModelto be invariant onMClass(variance measurement will relate two generic conditionals withinset, whoseextendsclauses don't match, and thus always fail assignment). Now, since the comparison can sometimes maybe work out because theextendsclause is disregarded when thecheckTypeisany, as when relating signatures, structurally, it really should beUnmeasurableor at leastUnreliableand... drum roll... we have an extremely old outstanding PR that already adds that (which makes sense, since I was able to rephrase the root issue into something that behaved the same in old TS, too), and the PR very recently became unblocked: #31277Now... while that gets back the old no-error behavior for
redux-orm(and then some), we could pick a bone about weather we're doing the right thing for signature relationships here, too, since as I said, the error (currentmasterbehavior) is actually pretty reasonable from the real structure - signature relating is just hiding it when a structural comparison is performed. (By erasing the signature-local type parameter, and thus erasing the entire "conditional" part of the conditional.) It would almost make sense to, rather than erase the parameters toany, instantiate them into a well-known type-parameter-assignable-to-all-type-parameters (almost like a reversewildcardType!) so conditionals remain deferred... however the knock on effects of doing so for other signature relation comparisons are not immediately obvious.TL;DR: Three step process where our behavior flip-flops in the middle.
- For now, keep
masteras is, accepting new errors as better. These errors only appear when variance relationships are used, however. - Merge Do not measure variance for a conditional type extendsType #31277 to remove the errors, making no errors appear when either structural or variance based checks occur, but
- Since I feel the errors here are actually more correct (even if we're not getting them for all the right reasons), and we should follow up on Do not measure variance for a conditional type extendsType #31277 by fixing structural signature relationship checking to not un-defer conditionals (and thus greatly weaken structural assignability checks involving conditionals in signatures) quite so haphazardly. Then we'll finally get the current behavior (an error) whenever a structural or variance based comparison is used.
If you agree the new errors are better, we can put off 2 and 3 for awhile if need be (though we should get them both done rapidly so we don't visibly flip-flop behavior). If you disagree, we can scratch 3, and we should probably work on merging #31277 sooner, since that'll remove the errors from the variance based checks, which effectively regains the old behavior. In both cases, #31277 is the next step here.
- For now, keep
- addedFix AvailableA PR has been opened for this issueA PR has been opened for this issue
on Apr 29, 2021 sandersn commented
on Apr 30, 2021 MemberAuthorMore actionsYour recommended steps sound good. (2) and (3) are now combined in #43887, right? Let's do the following:
- Keep
masteras is until we create the RC. - In the meantime, see if there's an easy-[ish] workaround for redux-orm. If not, add it to https://github.com/microsoft/DefinitelyTyped-tools/blob/master/packages/dtslint-runner/expectedFailures.txt and wait for an owner of the types to fix it. It's little used so that may never happen.
- Get Anders Hejlsberg (@ahejlsberg) and Jack Williams (@jack-williams) to review Mark conditional extends as Unmeasurable and use a conditional-opaque wildcard for type erasure #43887 and merge it for 4.4.
- Keep
- added 2 commits that reference this issue
on May 17, 2021 - addedRescheduledThis issue was previously scheduled to an earlier milestoneThis issue was previously scheduled to an earlier milestone
on Jun 18, 2021 - addedDomain: This-TypingThe issue relates to providing types to thisThe issue relates to providing types to this
on Oct 16, 2025 - removedFix AvailableA PR has been opened for this issueA PR has been opened for this issue
on Aug 20, 2026
The nightly dtslint run for 4/28 fails on redux-orm, with new errors on examples like:
Almost certainly a result of #43624, but it could be #42449 or #43835, since they went in on the same day.
If this is an intended result of that PR, can you fix up redux-orm? A naive change to
Ref<M extends Model>doesn't work.