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

Suspected breaking change with pushing and assigning a 'derived' value into a 'base' array #59016

Open
adam-marshall opened this issue Jun 25, 2024 · 4 comments
Labels
Help Wanted You can do this Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases
Milestone

Comments

@adam-marshall
Copy link

πŸ”Ž Search Terms

inline assignment array derived base push 'Object literal may only specific known properties'

πŸ•— Version & Regression Information

  • This changed between versions 4.9.5 and 5.0.4

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.0.4#code/JYOwLgpgTgZghgYwgAgEJwM4oN4FgBQyywAJgFzIgCuAtgEbQDcBAvgQaJLIigCLTAAbhBLIIAD0ggSGNJhwEiIODQgUMYKKADmzfG3wEEAexAbikGhgrosAbQC6yALzJHeggBsIYCxBoAjBT8WsIket6+wJYATMECYR740f4BLsjYxOTIAQA0lCpqyADkYAAWwLKVyMYA1shQENpwUCTeGBjFyCx6KVYAdAAOVBhlABR9AQCUSX0YQyPjE7HpmaQUMfnKqhSlFVWyAHIA8gAqNfWgyACs-QAM-QAs+XRUUbJ1xCDIj-0AnP1rl0WFMZkA

πŸ’» Code

interface Base {
  id: number;
}

interface Derived extends Base {
  name: string;
}

const items: Base[] = [];

let item1: Derived;
let item2: Derived;

item1 = { id: 1, name: 'this is ok regardless' };
items.push(item1);

items.push((item2 = { id: 2, name: 'this is NOT ok in 5.0.4, but is ok in 4.9.5' }));

πŸ™ Actual behavior

'Object literal may only specific known properties' error

πŸ™‚ Expected behavior

The code should not produce an error

Additional information about the issue

It is possible that this is a change to the order in which types are inferred, but I have been unable to find any details in the documentation/changelog.

@REDAMANS
Copy link

The documentation addresses this issue :

One final way to get around these checks, which might be a bit surprising, is to assign the object to another variable: Since assigning squareOptions won’t undergo excess property checks, the compiler won’t give you an error:

// Before
let mySquare = createSquare({ colour: "red", width: 100 });
// Object literal may only specify known properties, but 'colour' does not exist in type 'SquareConfig'. Did you mean to write // 'color'?

// This is the workaround
let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);

In your case, it seems like the object is undergoing exess property-checks before being assigned to the item2 variable.
The change of behavior between versions is not mentioned in the changelog. This might be due to the type inference order being inversed, or due to stricter type checking rules that might have been introduced.

@adam-marshall
Copy link
Author

Thanks @REDAMANS

Yes I can see the documentation you quote at https://www.typescriptlang.org/docs/handbook/2/objects.html#excess-property-checks, that gives some really helpful context.

It seems that the excess property checks have been there for a long time, when passing a value inline as an argument. However the difference in the example you give is that, colour does not exist on the type at all.

interface SquareConfig {
  color?: string;
  width?: number;
}

In my example, passing { id, name } isn't plucking properties out of thin air in the same way that colour is, its typed up, its just that its on the derived type instead of the base type.

If I go to 4.9.5, and try and add a property plucked out of thin air, the inline assignment does correctly error

image

so I think there is a difference between

  1. the value matching the base type
  2. the value matching the derived type
  3. the value genuinely having properties which do not exist on either

and in 4.9.5, 2. was allowed when pushing values inline into an array, and now it is not allowed.

@adam-marshall
Copy link
Author

Interestingly, in 4.9.5 the randomGarbage example picks up that it doesn't exist in type Derived.

In 5.0.4, it gives 2 errors, that it doesn't exist in type Derived or Base. So it does still seem to be aware of the fact that there is a Derived type.

image

It is just then on the one above, which does match Derived, it gives 1 out of 2 errors, still erroring for Base.

image

@RyanCavanaugh
Copy link
Member

Bisects to #52282 FWIW

If this can be fixed simply we can take a PR, but assignment expressions in this position are pretty rare and I don't think we'd want to do anything that breaks other more mainline scenarios as a trade-off.

@RyanCavanaugh RyanCavanaugh added Help Wanted You can do this Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases labels Jun 26, 2024
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Jun 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Help Wanted You can do this Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases
Projects
None yet
Development

No branches or pull requests

3 participants