Skip to content

Generic object can be widened unsafely #45335

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 Aug 5, 2021 · 7 comments
Closed

Generic object can be widened unsafely #45335

awerlogus opened this issue Aug 5, 2021 · 7 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@awerlogus
Copy link

Bug Report

🔎 Search Terms generic type object wide

🕗 Version & Regression Information 4.4.0-dev.20210805

💻 Code

const obj = { prop: 5, field: '' }


// Expected error: other properties of 'data' can have types of not a number
// Currently: no error
const func1 = <T extends { prop: number }>(data: T): Record<string, number> => data

// No error
const result1 = func1(obj)

// Got: number | undefined
// Real value: string
const field  = result1.field


// Type checker must make us rewrite func1 into
const func2 = <T extends { prop: number } & Record<string, number>>(data: T): Record<string, number> => data

// Error as expected: Type '{ prop: number; field: string; }' is not assignable to type 'Record<string, number>'.
const result2 = func2(obj)

🙁 Actual behavior

T extends { prop: number } is assignable to Record<string, number>

🙂 Expected behavior

T extends { prop: number } is not assignable to Record<string, number>

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Aug 5, 2021
@RyanCavanaugh
Copy link
Member

Record<string, number> has the same soundness hole as optional properties. I don't see how we could change this without breaking a huge amount of working code.

@awerlogus
Copy link
Author

How about adding some new tsconfig option?

@fatcerberus
Copy link

I don’t think such an option would be feasible. T extends X means precisely “T is assignable to X”. If { prop: number } were not assignable to Record<string, number> then you also couldn’t do this:

const rec: Record<string, number> = {
    prop: 42
};

Or for that matter, even initializing it with {} would have to be an error (because every object is assignable to {}).

@awerlogus
Copy link
Author

@fatcerberus it's incorrect to say that if A is assignable to B and B is assignable to C, then A is always assignable to C.

Counterexample:
A: { a: number, b: string }
B: { a: number }
C: Record<string, number>

@fatcerberus
Copy link

As Ryan pointed out, the same kind of unsoundness exists for all types with optional properties, so this isn’t unique to Record.

@awerlogus
Copy link
Author

Yes, and this doesn't mean that we can leave everything as it is because of it. It just means that the problem, needs to be solved, simply takes on a larger outline.

@awerlogus
Copy link
Author

Duplicate of #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

3 participants