Skip to content

Type hole: everything is assignable to element of generic indexable #54160

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

Closed
awerlogus opened this issue May 6, 2023 · 9 comments
Closed

Type hole: everything is assignable to element of generic indexable #54160

awerlogus opened this issue May 6, 2023 · 9 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@awerlogus
Copy link

Bug Report

πŸ”Ž Search Terms

generic tuple element assignable

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

// No error, but expected
const a = <T extends [unknown]>(x: T): T[0] => 123

// string type, but number value
const b = a([''])

πŸ™ Actual behavior

It's possible to return everything

πŸ™‚ Expected behavior

It's possible to return entities which only have T[0] type

@fatcerberus
Copy link

Hmm, it seems like TS is eagerly reducing T[0] to unknown here. Not sure whether that’s intentional or not.

@typeholes
Copy link

Oddly, this does error:
const a = <U extends unknown, T extends [U]>(x: T): T[0] => 123

@fatcerberus
Copy link

@typeholes Username checks out

@Andarist
Copy link
Contributor

Andarist commented May 9, 2023

Essentially the problem is the same as the one here:

// No error, but expected
const a = <T extends { foo: number | string }>(x: T): T["foo"] => 123

// string type, but number value
const b = a({ foo: 'str' })

In other words, it's not limited to tuples. It's actually about indexed accesses with non-generic index constraints that are concrete~ (index signatures are not prone to this) on generic objects. The code responsible for this is here

@awerlogus awerlogus changed the title Type hole: everything is assignable to element of generic tuple Type hole: everything is assignable to element of generic indexable May 9, 2023
@awerlogus
Copy link
Author

Probably related #45335

@fatcerberus
Copy link

fatcerberus commented May 10, 2023

This may be trickier than it first appears. Clearly we can tell that

// Exhibit A
const a = <T extends { foo: number | string }>(x: T): T["foo"] => 123

is unsound and should be an error. But what about this variant?

// Exhibit B
const b = <T extends { foo: number | string }>(x: T): T["foo"] => x["foo"]

It's already established that returning a string | number here is not sound and should be an error. But this particular case is completely safe, and there's almost certainly code in the wild that relies on it not being an error. HOWEVER, then there's this one...

// Exhibit C
const c = <T extends { foo: number | string }, U extends { foo: number | string }>(x: T, y: U): T["foo"] => y["foo"]

...which is again unsound (U could be wider than T). But if you're only looking at the constraints, which is what TS appears to be doing, this case C looks identical to case B above. What to do, what to do...

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label May 10, 2023
@RyanCavanaugh
Copy link
Member

Clearly the only sound behavior in this case is to only allow expressions that are exactly of the form x[0], but x[0] doesn't have the type T[0], it has the type from the constraint. It's not clear there's anything we could do to improve the situation here except to radically rework how indexed access works in generic functions, which I don't think is quite on the table yet given the motivating scenarios apart from this one

@mkantor
Copy link
Contributor

mkantor commented Oct 9, 2023

I found this issue after coming across code like this:

const test = <T extends { a: unknown }>(x: T) => { x.a = 123 }
const x = { a: '' }
test(x)
const notString = x.a
//    ^? const notString: string

I think that's a symptom of the same underlying problem.

EDIT: Or not, because this also (unsurprisingly) has the same issue. I guess writing to properties of inputs is just inherently unsafe.

@awerlogus
Copy link
Author

@mkantor You may be looking for #39915

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