You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Issue deriving tuple types on overridden methodΒ #57387
The spec method on Bar failed to typecheck, despite unambiguously implementing the same type signature as spec on Foo.
Annotating Bar's spec method to return a StringFirstTuple typechecks properly.
The error message seems to indicate that the return type of spec in Bar has been generalized to Array<string | {something: string}> before checking the overridden type.
π Expected behavior
A function that returns an array that is hard-coded to match a tuple type should be substitutable for a function that is specified to return that tuple type.
Methods don't inherit the types of the methods they're overriding, so the type of your Bar.spec method is inferred from the return value. #32082 matches this the most.
I suppose that's fair, though from an ergonomics perspective it feels odd to me that TS would mark a type mismatch between Bar.spec and Foo.spec, but never actually check whether Bar.spec and Foo.spec could be described equivalently.
In a TypeScript-forward project, where relying on implicit types is generally discouraged, the current behavior may be completely reasonable; I'm running into this in a Javascript project with JSDoc types, and the lack of inference is an unfortunate hurdle for most consumers (especially as the tuple type gets pushed deeper inside a nested object).
You can't really remove this inconsistency, only change where it appears. If you had written this, for example, m is unambiguously inferred as string[] and the code still fails
Ryan Cavanaugh (@RyanCavanaugh) I'm not sure I follow your argument. In your example, m will always be inferred as Array<string | {something: string}> (unless I'm really missing something), and no, neither string[] nor Array<string | {something: string}> will satisfy [string, ...any].
On the other hand, [string, ...any] describes an array of any length which has a string at index 0, which absolutely can be satisfied by a (edit: non-empty) string[]. And while it's similarly correct to say that ["goodbye", {something: "else}] is an Array<string | {something: string}>, it is also a [string, ...any] (and a [string, object], and many other things).
To someone unfamiliar with the implementation of TypeScript, it definitely feels like a defect to say that a hard-coded value like ["goodbye"] is not a satisfactory "array that begins with a string" unless it is also explicitly described as such.
Pieter van de Bruggen (@pvande) the vibes might be off, but the "fix" is that the method return type inference algorithm gets more complex (and very much more non-local in this case), and "the type system is too complicated to understand" is already a problem that people talk about. Everything can be made more complicated, but complexity itself is also understood to be a defect. It's not infrequent for people to claim that the inference system should be "worse" (less likely to understand implicit intent) for the sake of being "simpler". I'm not saying this is an absolute must-not-change, but it's also not a bug in the sense of producing unsound behavior. And even in the presence of a fix, m is also "wrong", so it's not like a fix here relieves you of the burden of understanding the concept that the inferred type of an expression might be wider than its tightest possible bound.
β¦ the "fix" is that the method return type inference algorithm gets more complexβ¦
That does seem likely.
It's not infrequent for people to claim that the inference system should be "worse" (less likely to understand implicit intent) for the sake of being "simpler".
Not being a regular member in this community, that's not something I've been aware of, though it also seems reasonable.
β¦it's also not a bug in the sense of producing unsound behavior.
Completely agree. At worst, it's a spurious error in VS Code for folks leveraging a typed API in a typeless Javascript project.
And even in the presence of a fix, m is also "wrong", so it's not like a fix here relieves you of the burden of understanding the concept that the inferred type of an expression might be wider than its tightest possible bound.
I can imagine a world in which implicitly typed literals are lazily evaluated and "cast" at point-of-use. In such a world, all of the following seem reasonable:
I can imagine a world in which implicitly typed literals are lazily evaluated and "cast" at point-of-use. In such a world, all of the following seem reasonable:
This is an interesting thought and not something I personally ever considered. One of the fundamental tradeoffs you make with a typed language is that you give up the ability to speak of individual values in favor of giving the compiler (and potentially, the programmer) an easier job by being able to reason about more general types (i.e. constraints) of values instead. But I could definitely imagine a world too where e.g. object and array literals (which might satisfy several different constraints thanks to contextual typing) have type inference "deferred" until they're actually assigned to something with an explicit type annotation.
That said, deferral brings with it its own problems. See, e.g. conditional types in a generic context.
Activity
MartinJohns commentedon Feb 12, 2024
Methods don't inherit the types of the methods they're overriding, so the type of your
Bar.specmethod is inferred from the return value. #32082 matches this the most.pvande commentedon Feb 12, 2024
I suppose that's fair, though from an ergonomics perspective it feels odd to me that TS would mark a type mismatch between
Bar.specandFoo.spec, but never actually check whetherBar.specandFoo.speccould be described equivalently.In a TypeScript-forward project, where relying on implicit types is generally discouraged, the current behavior may be completely reasonable; I'm running into this in a Javascript project with JSDoc types, and the lack of inference is an unfortunate hurdle for most consumers (especially as the tuple type gets pushed deeper inside a nested object).
RyanCavanaugh commentedon Feb 12, 2024
You can't really remove this inconsistency, only change where it appears. If you had written this, for example,
mis unambiguously inferred asstring[]and the code still failspvande commentedon Feb 12, 2024
Ryan Cavanaugh (@RyanCavanaugh) I'm not sure I follow your argument. In your example,
mwill always be inferred asArray<string | {something: string}>(unless I'm really missing something), and no, neitherstring[]norArray<string | {something: string}>will satisfy[string, ...any].On the other hand,
[string, ...any]describes an array of any length which has astringat index 0, which absolutely can be satisfied by a (edit: non-empty)string[]. And while it's similarly correct to say that["goodbye", {something: "else}]is anArray<string | {something: string}>, it is also a[string, ...any](and a[string, object], and many other things).To someone unfamiliar with the implementation of TypeScript, it definitely feels like a defect to say that a hard-coded value like
["goodbye"]is not a satisfactory "array that begins with a string" unless it is also explicitly described as such.MartinJohns commentedon Feb 12, 2024
Arrays can be empty, not having a
stringat index 0.pvande commentedon Feb 12, 2024
Fair point.
RyanCavanaugh commentedon Feb 13, 2024
Pieter van de Bruggen (@pvande) the vibes might be off, but the "fix" is that the method return type inference algorithm gets more complex (and very much more non-local in this case), and "the type system is too complicated to understand" is already a problem that people talk about. Everything can be made more complicated, but complexity itself is also understood to be a defect. It's not infrequent for people to claim that the inference system should be "worse" (less likely to understand implicit intent) for the sake of being "simpler". I'm not saying this is an absolute must-not-change, but it's also not a bug in the sense of producing unsound behavior. And even in the presence of a fix,
mis also "wrong", so it's not like a fix here relieves you of the burden of understanding the concept that the inferred type of an expression might be wider than its tightest possible bound.pvande commentedon Feb 13, 2024
Ryan Cavanaugh (@RyanCavanaugh) Thanks for your patience with me about this.
That does seem likely.
Not being a regular member in this community, that's not something I've been aware of, though it also seems reasonable.
Completely agree. At worst, it's a spurious error in VS Code for folks leveraging a typed API in a typeless Javascript project.
I can imagine a world in which implicitly typed literals are lazily evaluated and "cast" at point-of-use. In such a world, all of the following seem reasonable:
What I don't know offhand is what the practicalities and drawbacks of such an implementation are, though I'm confident they exist.
That said, my only goal in opening this issue was to call attention to a sharp edge that my team encountered, to raise awareness and discussion.
fatcerberus commentedon Feb 14, 2024
This is an interesting thought and not something I personally ever considered. One of the fundamental tradeoffs you make with a typed language is that you give up the ability to speak of individual values in favor of giving the compiler (and potentially, the programmer) an easier job by being able to reason about more general types (i.e. constraints) of values instead. But I could definitely imagine a world too where e.g. object and array literals (which might satisfy several different constraints thanks to contextual typing) have type inference "deferred" until they're actually assigned to something with an explicit type annotation.
That said, deferral brings with it its own problems. See, e.g. conditional types in a generic context.
typescript-bot commentedon Feb 17, 2024
This issue has been marked as "Not a Defect" and has seen no recent activity. It has been automatically closed for house-keeping purposes.