misleading error message on invalid assignment when one type parameter extends another
#60,881 opened on 2024年12月30日
Repository metrics
- Stars
- (48,455 stars)
- PR merge metrics
- (平均マージ 6d 17h) (30d で 9 merged PRs)
説明
🔎 Search Terms
generic, type parameter, extends, arbitrary type which could be unrelated, assignment
🕗 Version & Regression Information
- This changed between versions TS 3.8.3 and TS 3.9.7 (well, the pre-3.9 message isn't perfect, tbh but it only becomes fully wrong after that)
⏯ Playground Link
💻 Code
function parameterExtendsOtherParameter<SuperType, SubType extends SuperType>(superType: SuperType, subType: SubType) {
// TS 3.3: Type 'SuperType' is not assignable to type 'SubType'.
//
// TS 3.5: Type 'SuperType' is not assignable to type 'SubType'.
// 'SuperType' is assignable to the constraint of type 'SubType', but 'SubType' could be instantiated with a different subtype of constraint '{}'.
//
// TS 3.9: Type 'SuperType' is not assignable to type 'SubType'.
// 'SubType' could be instantiated with an arbitrary type which could be unrelated to 'SuperType'.
//
// TS 4.8: Type 'SuperType' is not assignable to type 'SubType'.
// 'SubType' could be instantiated with an arbitrary type which could be unrelated to 'SuperType'.
// input.tsx(1, 41): This type parameter might need an `extends V` constraint.
subType = superType;
}
🙁 Actual behavior
Type 'SuperType' is not assignable to type 'SubType'.
'SubType' could be instantiated with an arbitrary type which could be unrelated to 'SuperType'.
input.tsx(1, 41): This type parameter might need an `extends SubType` constraint.
The misleading thing here is that SubType plainly cannot be unrelated to SuperType. It's guaranteed to be a subtype of SuperType, since it extends SuperType.
🙂 Expected behavior
The TS 3.5 error message was nearly there (just swapped {} for SuperType)...
Type 'SuperType' is not assignable to type 'SubType'.
'SuperType' is assignable to the constraint of type 'SubType', but 'SubType' could be instantiated with a different subtype of constraint 'SuperType'.
I might suggest something like this instead though:
type 'SuperType' is not assignable to type 'SubType'.
'SubType' is constrained to be a subtype of 'SuperType'.
input.tsx: You might have swapped the left and right hand sides of the assignment.
This is nearly a garden-variety instance of assigning a wider type to a narrower type, it just looks tricky because of the presence of generics and extends.
Additional information about the issue
contrast with this example: (playground)
function parameterExtendsOrdinaryType<StringSubType extends string>(s: string, subType: StringSubType) {
// Type 'string' is not assignable to type 'StringSubType'.
// 'string' is assignable to the constraint of type 'StringSubType', but 'StringSubType' could be instantiated with a different subtype of constraint 'string'
subType = s;
}
cc @controversial (discovered in https://github.com/typescript-eslint/typescript-eslint/pull/10461#issuecomment-2524252783)