microsoft / microsoft/TypeScript

Misleading error for a generic parameter with a default involving `this`

Đang mở
#61,812 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Domain: This-Typing Help Wanted Possible Improvement
Ngôn ngữ chính
Go
Star
111k
Fork
14.3k
Merge trung bình
2 ngày 4 giờ
Pull request đã merge (30 ngày)
132

Mô tả

🔎 Search Terms

generic type parameter default, assignability, this

🕗 Version & Regression Information
  • This changed in a9ad94ab3c35615b344c51e721f77655c225b524
⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.8.3#code/KYDwDg9gTgLgBAbwL4G4BQaAmwDGAbAQymDnwIGdy4ARCHAVwFtgA7GAHloebYDkDmAPkRo4cFgOAAuGnSasY-ZujHkAliwDmeYDAgtyMmAAs15dEgwaYwKADMCOEgEEWATy7y2I1Ru279QzgTMwsMbDJiUkJKOABRRgAjYExsTABhCDwdHBg1fWpgPBgCADE1Isx2UXidHhhyyoAVNzBgABoagHkAawBGOFAbFkwqT3r2AndhAF5a4HrGvEwWtsGQYdG4Kbc4AH5ZbgV2OLqFJZXW4GEZFmAAN1tOsV6AJnXNqlcPOXq4OfGx1OC3OFWWq2uzzgACEoBAeqwPqwtt9Ad45sDFmDLmshsiqDt9ocvBxMaDmlcbuIHrYagB6OlwCFwADkZLYF2ZeJGBPcRLRpLOHOxEKpd0eUBZcEwEGAVBYEHg5AIeXIdl2JhIOECMCgBGsrNRvwULIAdPTGcyWQKTkKGiLKVKzOJFdtKGpNBJEjpghBgldDe4BWaLWIxBCqBA7HAwHC2rBdiz1FodHoDFKiCQNNrGGAVWpvcBzWIGWGw1abez7RS2oInfLXRR1J6CIXff61my7ZyA9ytoSDpXuw7a3BbjTJebhMggA

💻 Code

Here's what essentially was trying to be written in an understandable way:

type Validator = (data: unknown) => ValidationResult<unknown>;

interface Cloneable {
  data: unknown;
  clone(): this;
}

class ValidationResult<T> implements Cloneable {
  constructor(public data: T) {
    this.isValid = true; // Actual validation logic left off.
  }

  clone(): this {
    return structuredClone(this);
  }

  isValid: boolean;
}

class Collection<V extends Validator, C extends Cloneable = ReturnType<V>> {
// Type 'ReturnType<V>' does not satisfy the constraint 'Cloneable'.
//   Type 'ValidationResult<unknown>' is not assignable to type 'Cloneable'.
//     The types returned by 'clone()' are incompatible between these types.
//       Type 'ValidationResult<unknown>' is not assignable to type 'ReturnType<V>'.
  constructor(validator: V, items: unknown[]) { } // Actual validation logic left off.

  items: C[] = [];
}

Here's the a more distilled way of testing interesting cases:

export {};

declare class Document<DocumentName> {
  name: DocumentName;
  singletons: this;
}

interface AnyDocument {
  singletons: this;
}

declare class EmbeddedCollectionDeltaField<
  ElementFieldType,
  Ok1 extends Document<any> = ElementFieldType extends any ? Document<ElementFieldType> : never,
  Ok2 extends AnyDocument = Document<ElementFieldType>,
  Broken extends AnyDocument = ElementFieldType extends any ? Document<ElementFieldType> : never
  // Type 'ElementFieldType extends any ? Document<ElementFieldType> : never' does not satisfy the constraint 'AnyDocument'.
  //   Type 'Document<ElementFieldType>' is not assignable to type 'AnyDocument'.
  //    Types of property 'singletons' are incompatible.
  //      Type 'Document<ElementFieldType>' is not assignable to type 'ElementFieldType extends any ? Document<ElementFieldType> : never'.
> {}

Finally this playground has the most confusing error.

🙁 Actual behavior

Multiple errors.

🙂 Expected behavior

No error. Or at least a less misleading error message. Specifically the lines Type 'ValidationResult<unknown>' is not assignable to type 'Cloneable'. and Type 'Document<ElementFieldType>' is not assignable to type 'AnyDocument'. are both testably false.

Additional information about the issue

The reason for this misleading error appears to be this snippet:

        if (constraintType && defaultType) {
          checkTypeAssignableTo(defaultType, getTypeWithThisArgument(instantiateType(constraintType, makeUnaryTypeMapper(typeParameter, defaultType)), defaultType), node.default, Diagnostics.Type_0_does_not_satisfy_the_constraint_1);
        }

While the types are displayed as defaultType being assignable to constraintType it's really checking if defaultType is assignable to getTypeWithThisArgument(instantiateType(constraintType, makeUnaryTypeMapper(typeParameter, defaultType)), defaultType).

Specifically this is being substituted with the defaultType and the misleading errors about that ensues where it suddenly switches from talking about Document<ElementFieldType> (a part of defaultType) being compared to AnyDocument (the constraintType) to talking comparing Document<ElementFieldType> to the defaultType because this = defaultType. This is confusing because you don't normally expect to see the defaultType being compared to a part of the defaultType.

I admittedly don't understand the rationale for this specific substitution very well but I find it a bit surprising this is substituted with defaultType instead of constratintType. Regardless of what fix would be necessary to make this work, I can't deduce a way to cause unsoundness if this generic parameter default would be accepted.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Bắt đầu với phần kiểm tra constraint/default xung quanh checkTypeAssignableTo và lệnh gọi getTypeWithThisArgument được nêu trong issue. Tái hiện các trường hợp Playground được liên kết và so sánh các chẩn đoán được báo cáo cho những ví dụ generic đã được rút gọn. Được xem là hoàn tất khi các default hợp lệ không còn tạo ra lỗi, hoặc các chẩn đoán mô tả chính xác lỗi assignability thực tế.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
typescript
Lĩnh vực
compilers
Loại issue
Lỗi
Độ khó
5/5
Thời gian dự kiến
Hơn một tuần
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
30/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.