-
Notifications
You must be signed in to change notification settings - Fork 12.4k
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
Inconsistent type validation: Different result depending on inline defined and executed function and declared and executed function #59560
Comments
Different inferences in the face of different contextual types doesn't seem like a bug. Could you fill out the issue template completely -- is this a regression? And if so, from when? |
I think it all depends on what are our expectations from this library I believe we all agree, both executions are equivalent (in sense that in both cases It feels as either logical flaw or logical limitation in Typescript.
All I know is that it happens in v5.5.4. I'm new to the TypeScript so it's hard for me say, if it's something new, or by design for a long time |
In the first example you pass the function directly to In your second example the function is on its own. The compiler does not know that it's supposed to be used as a This is neither a logical flaw, nor a logical limitation, but working as intended. |
Why can't the compiler infer the same information when the function is passed on its own? The implementation of this function is just as accessible as of one passed directly and the compiler can read through it in the same way. It's clear that both versions work exactly the same, and nothing different can happen in either case. If the compiler is unable to deduce what is evident in these few lines of code, it seems like a limitation on the compiler's side. |
Because This could be somewhere else in the code, and if the type of function foo(arg: readonly ["test"]) {}
foo(resolveArgs())
The second version introduces a new variable, the first one not. It's a major difference. |
The compiler can clearly see that this function is not used anywhere else. However, let's put that aside. I understand that there might be an agreement not to make the compiler work in a way where newly introduced code (in this case, additional function usage) changes how previous code is validated. Now, taking it further, I still don't understand how If I understand correctly, you are stating that in the case of Why is |
I think there are four-ish logical endpoints for this kind of analysis:
Option 3 is not something that's tractable to do with reasonable performance. Option 0 also seems bad; people would complain a lot if obviously-fine code errored for no good reason. So you're asking why we don't do option 2 vs option 1, and I think the description of option 2 explains why |
Thanks, @RyanCavanaugh, for this insight. It sounds very reasonable. I can imagine that aiming for option 2 would be like opening Pandora's box. Agreeing that we're on option 1, I still have trouble understanding why the above fails. Let's group some facts:
Why does introducing a second function that takes |
The bare inference of When an |
Indeed, I missed that in the above reasoning. Technically
This is an actual source of confusion. I think the fact that the given array is not referenced anywhere else shouldn't dismiss that it was declared For example, the following code passes perfectly: const test = (value: string[]): void => { value.push("test2"); };
test(["test"] as const); What's interesting is that it will fail for |
Why? That breaks code like this: declare function foo<T>(arg: readonly T[]): T[];
// p1: string[], but I wanted ("one" | "two")[]
const p1 = foo(["one", "two"]);
// Actual: p2: ("one" | "two")[] ✅
// Proposed: Error here instead, but
// what should the user write instead?
const p2 = foo(["one", "two"] as const);
See above example - a "fresh" |
@RyanCavanaugh Why exactly will this break? I'm not sure if I follow this example. Doesn't I see that |
Sorry, I meant this: declare function foo<T>(arg: T[]): T[];
// p1: string[], but I wanted ("one" | "two")[]
const p1 = foo(["one", "two"]);
// Actual: p2: ("one" | "two")[] ✅
// Proposed: Error here instead, but
// what should the user write instead?
const p2 = foo(["one", "two"] as const); |
This issue has been marked as "Question" and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
@RyanCavanaugh I'm not sure if it makes any real difference. What do I miss? Why exactly there's a room for any error at I read it as: I'm not sure how it explains that in previous example we allow mutation for
?? That's interesting. So if I do not respond to the comment on the issue during the weekend the issue will be autoclosed? :) Where's the work/life balance thing? ;-) |
You said
The proposed behavior is that |
Thanks for responding. I see now that mutability is tied to type declarations. In this case, the input was declared as
I guess Therefore, if there's no convenient syntax to pass this information (as mutable), I assume TypeScript intentionally allows a hack where the user can pass an immutable that can be subtyped into a mutable. |
🔎 Search Terms
"inline function", "type validation"
🕗 Version & Regression Information
⏯ Playground Link
https://www.typescriptlang.org/play/?#code/MYewdgzgLgBFCm0YF4YAoBuBDANgV3gC4ZoAnASzAHMBtAXQEpiMRyATFAPhgG8BfANwAoIQHpRMAOohSAawgwA8rKEJoaDQy4waAIjVRddGFgWhIUBmgYNhYiQDEs5HArQB3ABYv4MclAByBXgARzxybBx4MFgoEBgAM3JSaAYhcyRSRBAcDHgAQVIqBVRrbT0DIxMzcGhhAzQsiBy8wuLrWyEgA
💻 Code
🙁 Actual behavior
The first invocation is accepted as one without issues. The second which is equivalent, is rejected with the:
🙂 Expected behavior
Both should be accepted
Additional information about the issue
No response
The text was updated successfully, but these errors were encountered: