microsoft / microsoft/TypeScript
array filter/find has inconsistent results and incorrect constraint specifications
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- Go
- Star
- 111k
- Fork
- 14.4k
- Merge trung bình
- 1 ngày 19 giờ
- Pull request đã merge (30 ngày)
- 117
Mô tả
🔎 Search Terms
type predicate error 2677
union of arrays type predicate
array filter result
infer return type from constraint
One error noted in this bug report (2345 on the rhs of a1 in the code) is a known problem with design-limitation status, I believe. That is incidental and not the main focus of this bug report.
🕗 Version & Regression Information
- 5.2.2 / 5.3.2
- I was unable to compare the result on prior versions because there were other worse problems in prior versions
⏯ Playground Link
💻 Code
interface Fizz {
id: number;
fizz: string;
}
interface Buzz {
id: number;
buzz: string;
}
declare function isFizz(x: unknown): x is Fizz;
const x1 = ([] as Fizz[]|Buzz[]).find(isFizz);
// ?^ x1: Fizz | undefined
const x2 = ([] as Buzz[]).find(isFizz);
// ?^ x2: Buzz | undefined
const y1 = ([] as Fizz[]|Buzz[]).filter(isFizz);
// ?^ y1: Fizz[]
const y2 = ([] as Buzz[]).filter(isFizz);
// ?^ y2: Buzz[]
// Different results obtained for this non library version
declare function filter2<T,S extends T>(predicate:(t:T)=>t is S, arr:T[]): S[] ;
declare function filter2<T>(predicate:(t:T)=>unknown, arr:T[]): T[] ;
const z1 = filter2(isFizz, ([] as Fizz[]|Buzz[]));
// ?^ z1: Fizz[]
const z2 = filter2(isFizz, ([] as Buzz[]));
// ?^ z2: Fizz[]
// Related: If the constraint "S extends T" is ommitted, an error occurs.
declare function filter3<T,S>(predicate:(t:T)=>t is S, arr:T[]): (T&S)[];
// ~ Type 'S' is not assignable to type 'T'. 2677
// Assuming the object type T is open (i.e., could have other keys, which is the TS default assumption)
// then the filter result should be (T&S)[], so try defining that result explicitly (without an overload, to avoid confusion).
// This should work:
declare function typePredicateFilter4<D, T extends D,S extends D>(predicate:(t:D)=>t is S, arr:T[]): (T&S)[];
const a1 = typePredicateFilter4(isFizz, ([] as Fizz[]|Buzz[])); // error
// ?^ z1: Fizz[] (invalidated by error)
const a2 = typePredicateFilter4(isFizz, ([] as Buzz[]));
// ?^ z2: (Buzz & Fizz)[]
🙁 Actual behavior
-
The types of
y2(Buzz[]) andz2(Fizz) differ. -
As shown in
function3, omitting the constraintS extends Tresults in error 2677. -
The result
a2is(Fizz & Buzz)[]as desired (good), but the result ofa1invalidated because of the error occurring onFizz[]|Buzz[]. SotypePredicateFilter4is not currently usable in the general case.
🙂 Expected behavior
-
The types of
y2andz2match. -
Want to be able to omit the that constraint
S extends Twhich triggers the error.
2.1. One reason we want to omit it is becauseS extends Ttriggers the inference logic we don't need if(T&S)is explicitly provided as the return type.
2.2. The other reason is thatS extends Tunnecessarily constrains the domain of the predicate type function. SeetypePredicateFilter4for the correct constraints -Dis the domain of the predicate function, and bothTandSindependently extendsD. -
typePredicateFilter4should not produce an error whenFizz[]|Buzz[]is the array argument type. This is an already known issue. 1 and 2 can be solved without solving this problem #3.
Additional information about the issue
Some special type inference logic to calculate the return type is triggered by the presence of the type predicate function arg is S as an argument, and that logic uses the constraint S extends T to infer the return type. If the requirement of having a constraint is dropped, and return type was specified explicitly (as in function filter3 and typePredicateFilter4, then that logic would not be required.
Would such a change be good? If (T & S) is what is actually expected to pass the type predicate function, then I think yes.
Moreover, the entire result signature output of typePredicateFilter4is "linear" with respect to it type inputs. This allows faster resolution.
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- 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.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Bắt đầu với bản tái hiện TypeScript Playground được liên kết và so sánh các ví dụ filter/find, đặc biệt là y2 với z2 và các lệnh gọi typePredicateFilter4. Theo dõi việc suy luận vị từ kiểu và kiểm tra ràng buộc đối với các khai báo được hiển thị. Được xem là hoàn thành khi các kiểu kết quả được báo cáo khớp nhau và lệnh gọi Fizz[]|Buzz[] không còn gây lỗi, mà không làm hồi quy hành vi đã nêu.
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
- 25/100