Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function return type with array #33158

Closed
deser opened this issue Aug 30, 2019 · 9 comments
Closed

Function return type with array #33158

deser opened this issue Aug 30, 2019 · 9 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@deser
Copy link

deser commented Aug 30, 2019

TypeScript Version: 3.6.2

Search Terms:
map\array\return type

Code

const fetchCustomVariables = (): { name?: string; type?: string }[] => {
    const variableList: { name?: string; type: string }[] = [];
    return variableList.map(v => ({ nasme: v.name, type: v.type }));
};

NotificationsForm 2019-08-30 13 12 07
NotificationsForm 2019-08-30 13 14 15

Expected behavior:
Typescript complaints that array of objects generated by map doesn't match function return type.

Actual behavior:
Typescript doesn't complaint that array of objects generated by map doesn't match function return type.

Playground Link:
http://www.typescriptlang.org/play/#code/PTAEGUAsHsFcBsAmoDG0C2AHeBDAlgHYBQaBAzgC6gBmAphSpAMKyUYBqOATnjgEbxaZUAF5QACgCUALlABvUARzpaAflmUeBAOYBuUBQCemNRopbtoAL4BtALqiAfPKKg3qaOSoA3brwG0ADJ4lLIKSirqoJqEegbGtGYW1vaioPa6ru5c9LBcBKC+PPyCwZQAdOg4mOLeThLhOGQqst7lEbQANPEmreVGJtaSkplWmUQgoACCwpC0OSSelDT0jCxs6JzFAWQATGlSYYrKptHmsfoDpzE61vVyWW6ky0X+pSEURx1RN3FXSbEUg4xBlHqAchQ8gVGs1EqAAOQARnh3X+CN28LGRCxQA

Related Issues:
No

@AnyhowStep
Copy link
Contributor

variableList.map((v) : { name?: string; type: string } => (
  { nasme: v.name, type: v.type }
));

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Aug 30, 2019

You're running into this thing called excess property checks for object literals. It isn't applied on non-object literals.

Without the explicit return type annotation on your .map() callback function, basically anything is allowed.

Then, the result of .map() is not an object literal. So, it doesn't perform excess property checks and it allows nasme, the excess property.

@deser
Copy link
Author

deser commented Aug 30, 2019

Yes I saw that, but why typescript can't guess the type? And actually, seems, it already guesses it:
image

@deser
Copy link
Author

deser commented Aug 30, 2019

And... map kind of already knows the type, it knows what array it iterates (not any). Why do I need to repeat that type for map explicitly again?

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Aug 30, 2019

It knows the type. It just isn't applying the excess property checks because it wasn't made to do so.
It's intentional.


One of these may help,

#28464 (comment)

#32537

#7481

Also, try looking up "excess property check" and "object literal".
It might turn up other stuff that explains the motivation for the current behaviour.

Might also include other workarounds you might find more agreeable

@fatcerberus
Copy link

fatcerberus commented Aug 30, 2019

What I believe happens here is that

  1. TS infers the type of the lambda expression v => ({ ... }). Because there's no explicit type annotation on the lambda, it simply infers a structural type based on whatever it actually returns. This is right because map allows you to output anything you want - there's no basis for it to infer any specific contract on the return type at this point.
  2. The object literal has now been fully accounted for, so its "freshness" flag is removed.
  3. TS asks if the function is usable with map. v (the argument) is already the correct type as determined by contextual typing and the return type can be anything at all, so yes, it's fine.
  4. Excess property checks now don't apply because the object literal(s) involved are no longer fresh.
    It simply asks whether { nasme: string | undefined, type: string }[] is structurally compatible with the return type of fetchCustomVariables, and it is.

If you want the excess property checks in the lambda, annotate its return type:

(v): ReturnType => value

@sandersn sandersn added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Sep 3, 2019
@deser
Copy link
Author

deser commented Sep 6, 2019

Excess property checks now don't apply because the object literal(s) involved are no longer fresh.
It simply asks whether { nasme: string | undefined, type: string }[] is structurally compatible with the return type of fetchCustomVariables, and it is.

But typescript as far as I understood already infers return type of callback. It knows exactly what map will return.
I mean it's quite possible to me in such simple cases (when all entities of map will be definitely of the same type) to not return any from map, but infer type and return correct type

@deser
Copy link
Author

deser commented Sep 6, 2019

Even if types are different depending on conditions, it's quite possible to return union type... :)

@thw0rted
Copy link

thw0rted commented Sep 9, 2020

It looks like #40311 fixes this. Consider

[1,2,3].map<{a:1}>(x => ({a:1, b:2}));

We want the excess property b to be flagged.

Current (v4.0.2) Playground -- no error

PR Playground -- gives "may only specify known properties" error. 🎉 🎉 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

6 participants