Skip to content

Synthesize typed NamedTuple._replace so extra fields are errors - #11642

Open
Henry Su (hsusul) wants to merge 1 commit into
microsoft:mainfrom
hsusul:fix/namedtuple-replace-extra-fields
Open

Synthesize typed NamedTuple._replace so extra fields are errors#11642
Henry Su (hsusul) wants to merge 1 commit into
microsoft:mainfrom
hsusul:fix/namedtuple-replace-extra-fields

Conversation

@hsusul

Copy link
Copy Markdown
Contributor

Summary

  • NamedTuple ._replace() is typed in typeshed as **kwargs: Any, so extra field names and incompatible values were accepted.
  • Runtime raises TypeError: Got unexpected field names (and type mismatches fail similarly). __replace__ was already synthesized with a keyword-only field signature on 3.13+; _replace now uses the same signature for class-syntax and factory NamedTuples.
  • Dataclass __replace__ behavior is unchanged.

Test plan

  • npx jest typeEvaluator4.test.ts -t DataClassReplace1 --forceExit (from packages/pyright-internal)
  • npx jest typeEvaluator8.test.ts -t NamedTuple --forceExit
  • npx jest checker.test.ts -t Private1 --forceExit
  • Confirm NT(1)._replace(y=2) reports an unknown-parameter error in the language server

…ors.

typeshed exposes _replace as **kwargs: Any, so unknown field names and
incompatible values were accepted even though CPython raises TypeError.
@rchiodo

Rich Chiodo (rchiodo) commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

🔒 Automated review in progress — Rich Chiodo (@rchiodo) is auto-reviewing this PR.

Comment thread packages/pyright-internal/src/analyzer/dataClasses.ts
@rchiodo Rich Chiodo (rchiodo) added the review-auto:changes-requested Automated review: posted blocking findings to address. label Aug 17, 2026
if (addGenericGetAttribute) {
FunctionType.addDefaultParams(replaceType);
} else {
constructorType.shared.parameters.forEach((param) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

Filtering parameters by the names self and cls removes legal NamedTuple fields with those names. For example, NamedTuple("NT", [("self", int)]) will not accept _replace(self=2). Skip only the actual receiver parameter and add regression coverage for both field names.

replaceType,
FunctionParam.create(
param.category,
param._type,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning · Non-blocking recommendation

This derives replacement fields from the synthesized constructor while the class-syntax NamedTuple path builds the corresponding signature in dataClasses.ts. The duplicated policy can drift between class and factory NamedTuples; extract a shared field-signature builder or otherwise centralize this logic.

@rchiodo Rich Chiodo (rchiodo) left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

@rchiodo Rich Chiodo (rchiodo) added review-auto:approved Automated review: no blocking findings (approval posted). and removed review-auto:changes-requested Automated review: posted blocking findings to address. labels Aug 17, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Review Center.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-auto:approved Automated review: no blocking findings (approval posted).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants