Infer from usage type logic

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

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

Đánh giá

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

Hướng nghiên cứu

Bắt đầu với TypeScript/src/services/codefixes/inferFromUsage.ts và so sánh các trường hợp Playground được liên kết với combineAnonymousTypes và combineTypes. Theo dõi logic thuộc tính tùy chọn và bảng mức độ ưu tiên, sau đó xác định hành vi suy luận dự kiến trước khi thay đổi bất kỳ điều gì. Hoàn tất khi các trường hợp suy luận từ cách sử dụng được báo cáo không còn tạo ra lỗi kiểu dữ liệu ngoài dự kiến.

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

Mô tả

Experience Enhancement Help Wanted Suggestion

Bug Report

🔎 Search Terms
  • infer from usage
  • interface higher priority then type on type inference
🕗 Version & Regression Information

I think this has always been the behavior as far as I know. I didn't play around with this much yet, but the behavior exists in 4.0.5 and nightly and it seems odd for it to bounce around a lot.

⏯ Playground Link

playground link

💻 Code


// infers -> : { x: any; } = this makes sense
function inferWorksFine(obj) {
    return obj.x
}

// infers -> Z, this also makes sense
function inferFromFunctionCallTypedWithType(obj) {
    typedWithType(obj)
}
// infers -> Q, also makes sense
function inferFromFunctionCallTypedWithInterface(obj) {
    typedWithInterface(obj)
}

/*
 *  Here is when behavior gets odd
 * 
 */

// infers -> { toplevel?: any; z?: number }
//      I would prefer if toplevel resovled to a non ? type but thats sorta stylistic
//      z is not optional and making it results in a type error
function inferFromUsageAndFunctionCallTypedWithType(obj) {
    typedWithType(obj)
    return obj.toplevel
}
// infers -> Q 
//      This seems wrong to me, interface "types" have priority over 
//      anonymous types, this results in errors because the inferred property 
//      is an anonymous type
function inferFromUsageAndFunctionCallTypeWithInterface(obj) {
    typedWithInterface(obj)
    return obj.toplevel
}
// infers -> Q
//      From what I understand. The statement "type Z = {z: number}" 
//      is a type alias Z to an anonymous type {z: number} so the interface 
//      still has priority over it. 
function inferFromFunctionCallTypedWithTypeAndFunctionCallTypedWithInterface(obj) {
    typedWithType(obj)
    typedWithInterface(obj)
}

// There are more examples you could contrive that look like this/combinations of this
// but I think this is enough for the point




type Z = {z: number}
function typedWithType(z: Z) {

}

interface Q {
    q: number
}
function typedWithInterface(q: Q) {}
🙁 Explanation of Actual behavior

Note: If I reference line numbers or any function I'm referring to the main branch I pulled I think yesterday.

So I sort of already discussed the behavior in the code itself. So I think it makes sense to talk about the code that causes this behavior.
So this file path "TypeScript/src/services/codefixes/inferFromUsage.ts" is where the logic for inferFromUsage lives afaik.

Just to do this in order:

// infers -> { toplevel?: any; z?: number }
//      I would prefer if toplevel resovled to a non ? type but thats sorta stylistic
//      z is not optional and making it results in a type error
function inferFromUsageAndFunctionCallTypedWithType(obj) {
    typedWithType(obj)
    return obj.toplevel
}

The reason these are optional seem to stem from the function combineAnonymousTypes
Specifically

const members = mapEntries(props, (name, types) => {
            const isOptional = types.length < anons.length ? SymbolFlags.Optional : 0;
            const s = checker.createSymbol(SymbolFlags.Property | isOptional, name as __String);
            s.links.type = checker.getUnionType(types);
            return [name, s];
        });

I'm actually not sure what the isOptional is intended to do. From what I understand anons.length is the number of anonymous types that the function is passed while types refers to possible type resolutions of the property with name name. I'm not sure what comparing these means.

Priority of interfaces

Since in the code I mention why I think type aliasing doesn't change anything I'll just show one example.

function inferFromFunctionCallTypedWithTypeAndFunctionCallTypedWithInterface(obj) {
    typedWithType(obj)
    typedWithInterface(obj)
}

So here the type will be resolved to the interface type. In the function combineTypes there is a priority table defined as follows:

const priorities: Priority[] = [
            {
                high: t => t === checker.getStringType() || t === checker.getNumberType(),
                low: t => t === stringNumber
            },
            {
                high: t => !(t.flags & (TypeFlags.Any | TypeFlags.Void)),
                low: t => !!(t.flags & (TypeFlags.Any | TypeFlags.Void))
            },
            {
                high: t => !(t.flags & (TypeFlags.Nullable | TypeFlags.Any | TypeFlags.Void)) && !(getObjectFlags(t) & ObjectFlags.Anonymous),
                low: t => !!(getObjectFlags(t) & ObjectFlags.Anonymous)
            }];

The last entry

{
                high: t => !(t.flags & (TypeFlags.Nullable | TypeFlags.Any | TypeFlags.Void)) && !(getObjectFlags(t) & ObjectFlags.Anonymous),
                low: t => !!(getObjectFlags(t) & ObjectFlags.Anonymous)
            }

gives high priority to non-anonymous types which I think only means interfaces or a type which at some point was mixed with an interface? This would take further investigation. I'm not sure the purpose of this behavior or if its a bug.

🙂 Expected behavior

I want the behavior to be that the generated types don't cause errors when they don't have to, since currently generated types don't satisfy what they need to be.

Deleting these two lines "solves" the behavior, but there are definitely more things to look into. I'm not sure why they were written - like I don't have any intuition on what problem they solve - but I'm pretty sure they are there for important behavior.

Also since I'm listing things, I think this is the other "big one"

There are also more cases like

function f(obj) {
    g(obj)
    return obj.v > 1
}

not being able to resolve v to a number if "g" expects some type (even if the type is unrelated like {q}) and others.

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

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.

Issue khác của microsoft/TypeScript

Tất cả issue của microsoft/TypeScript

Issue tương tự

Thêm issue về Go

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.