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

Spreading object with stringish keys allows incorrect value types for literal keys #56985

Closed
0x24a537r9 opened this issue Jan 8, 2024 · 4 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@0x24a537r9
Copy link

🔎 Search Terms

"spread string key", "spread stringish key", "object spread indexer", "spread non-literal"

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about object spreading

⏯ Playground Link

https://www.typescriptlang.org/play?ts=5.3.3#code/JYOwLgpgTgZghgYwgAgGIHt3IN4ChnJwBcyIArgLYBG0uAvrrgDYRjIAeJGWAvDoSQCMyOgG5GAegnIAygAt0ZJgBNkAOQDyAFWRy4ANxRwQyaFHRRc7ZHzwEAdI-YAafMkf3sA5ACYRrsUlpeUUVZAAJAEEANQBRQhMzCysbHDcPF3THL2JkAHIAZzAoUABzPP96cVwpWQUlVU0dPUME0yhzS2tbLPtMh2zvH2dkKhI89DA5aAq6AOrakIaImPjjds6UnoG+1x2c8aKSkHKRsfzJ6ahZ+aC60MbtXQMjRI7khHQQIuQAawgAJ4AGWAkCgcCYqTycDy4k+3zY+ghZAgakoNCgqR84m6aR2-XcgwA2v9gaDoBCALokJFMFFo6jQSqBGrBephKJxNpJSzwn60lEyYplKFHMqwrZ4wm7XpeEmAkFgqk05EQIXHUrMhZsh4rLnrHm4PmI1UAVRAvxA6AA7iAGRiSGQLVbbVicak7NKCR45aTFRSmNTkAKIObLTa7eimXMqnclhzVtz3ryvvyzc6I+qyo6M66+IVhScJbjPRk9tLfQryeDAyq6aHcyAsyctXH2Y9mi8k5tjX9Ac3SiQxS38zD3dsveWfch5QCB0GQ-bo7dWfdlpy1m9OqMIAg4GQCihQYVkAAHdAFArAKgsZBTOBsUkD5DAAr5GFnMhsZToCAFEB5GAkqlk4U7Ek+halAuqrPjGLKLO2eqbhsFg7nuB5HmAJ7npe163vej79pBL5vtCeSft+v7-oBwG9N64FERq0H1mGLqRoymJwdqa4JvqW6oTQ6GHi+WFvjhV43igBF9nOxGvu+5GjF+yA-n+AFASWdFgZ4M4QUxdYoqxmbEVxba6hu3YCbu+7CceYkXhJ+F6GwUwoKSJEKRRKlUepRqpmww6lK+cgANKAhoVAAFa7mAJAAEq7hYygADyBSMgUAHwet4BYarM45SmWvSBcFYUAhF0UIGA8xAA

💻 Code

Included a number of test cases because I'm not sure if they're all the same issue or slightly different. See the "but doesn't" cases at the bottom for the failure cases

interface Foo {
  a: number
}

let x: Foo = { a: 1 };

// Should NOT have an error
x = {
  ...x,
  ...{ a: 2 },
};

// Should HAVE an error
x = {
  ...x,
  ...{ a: 'string' },
};

// Should NOT have an error
x = {
  ...x,
  ...{ a: 2, b: 'other' },
};

// Should HAVE an error
x = {
  ...x,
  ...{ a: 'string', b: 'other' },
};

// Should NOT have an error
const keyLiteral = 'a';
const valueNumber = 2;
x = {
  ...x,
  ...{ [keyLiteral]: valueNumber },
};

// Should HAVE an error
const valueString = 'string';
x = {
  ...x,
  ...{ [keyLiteral]: valueString },
};

// Should HAVE an error
const valueUnknownNumber: unknown = 2;
x = {
  ...x,
  ...{ [keyLiteral]: valueUnknownNumber },
};

// Should HAVE an error
const valueUnknownString: unknown = 'string';
x = {
  ...x,
  ...{ [keyLiteral]: valueUnknownString },
};

// Should NOT have an error
const keyString: string = 'a';
x = {
  ...x,
  ...{ [keyString]: valueNumber },
};

// Should HAVE an error because it's possible that keyString is 'a', but doesn't
x = {
  ...x,
  ...{ [keyString]: valueString },
};

// Should HAVE an error because it's possible that keyString is 'a', but doesn't
x = {
  ...x,
  ...{ [keyString]: valueUnknownNumber },
};

// Should HAVE an error because it's possible that keyString is 'a', but doesn't
x = {
  ...x,
  ...{ [keyString]: valueUnknownString },
};

// Should HAVE an error because it's possible that the key is 'a', but doesn't
const stringishKeyObject: Record<string, string> = { a: 'string' };
x = {
  ...x,
  ...stringishKeyObject,
};

🙁 Actual behavior

No type error is shown when the key is a non-literal string that could be a

🙂 Expected behavior

I'd have expected a type error that the various string keys could match the literal key a, and thus the key type would need to be narrowed with some sort of conditional guard (e.g. if (keyString === 'a') return;

Additional information about the issue

This may be a duplicate of #56431 or #27273, but they seem slightly different to me and I don't know enough about TypeScript's innards to know if they reduce to the same fundamental cause.

@RyanCavanaugh
Copy link
Member

This is intentional. It's not super practical to error on this because there'd really be no useful way to silence the error, and also aliased spreads without exact types are already unsound in the same way.

@RyanCavanaugh RyanCavanaugh added the Working as Intended The behavior described is the intended behavior; this is not a bug label Jan 8, 2024
@0x24a537r9
Copy link
Author

You could silence it by narrowing the key's type with a type assertion, no? That said, I understand the decision needs to balance pragmatism and soundness at some level—it would potentially be onerous for an object with many keys without some form of a type subtraction like what a naive user might expect is produced by `Exclude<string, 'a'>. Has such a union subtraction feature been proposed? If so, then you could at least specify the type of what's spread as that and get safer behavior.

@RyanCavanaugh
Copy link
Member

Yep, see the issue titled "negated types"

@typescript-bot
Copy link
Collaborator

This issue has been marked as "Working as Intended" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Jan 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants