Skip to content

1.17.0 regression: dict literals incompatible with Sequence[TypedDict] #19483

@LefterisJP

Description

@LefterisJP

Bug Report

(yes I used claude code to sum the issue up)

Summary

mypy 1.17.0 incorrectly reports type errors when using dict literals that should be compatible with Sequence[TypedDict] fields. This worked correctly in mypy 1.16.1 and represents a regression in TypedDict structural typing.

Expected Behavior

Dict literals with the correct structure should be accepted when assigned to fields expecting Sequence[TypedDict], following structural typing principles that TypedDict is supposed to provide.

Actual Behavior

mypy 1.17.0 reports:

error: Incompatible types (expression has type "list[dict[str, str]]", TypedDict item "inputs" has type "Sequence[Component]")  [typeddict-item]

Environment

  • mypy version: 1.17.0 (fails) vs 1.16.1 (works)
  • Python version: 3.11.9
  • Platform: Linux
  • Dependencies: None (pure typing module)

Minimal Reproducible Example

Save as test_regression.py:

class Component(TypedDict):
    """Simulates ABIComponent from eth_typing"""
    type: str
    name: NotRequired[str]
    components: NotRequired[Sequence['Component']]

class Function(TypedDict):
    """Simulates ABIFunction from eth_typing"""
    type: Literal['function']
    name: str
    stateMutability: Literal['pure', 'view', 'nonpayable', 'payable']
    inputs: NotRequired[Sequence[Component]]
    outputs: NotRequired[Sequence[Component]]

# This should work according to TypedDict structural typing rules
# Works in mypy 1.16.1, fails in mypy 1.17.0

# Simple case
simple_function: Function = {
    'type': 'function',
    'name': 'test',
    'stateMutability': 'view',
    'inputs': [{'type': 'uint256', 'name': 'value'}],  # Error in 1.17.0
    'outputs': [{'type': 'bool'}]  # Error in 1.17.0
}

# Nested case (components within components)
complex_function: Function = {
    'type': 'function', 
    'name': 'complex',
    'stateMutability': 'view',
    'inputs': [
        {'type': 'tuple', 'components': [  # Error in 1.17.0
            {'type': 'uint256', 'name': 'amount'},
            {'type': 'address', 'name': 'recipient'}
        ]}
    ]
}

# Direct assignment to Sequence should also fail
components_list: Sequence[Component] = [
    {'type': 'uint256', 'name': 'value'},  # Error in 1.17.0
    {'type': 'address'}  # Error in 1.17.0  
]

# List of functions
function_list: list[Function] = [
    {
        'type': 'function',
        'name': 'func1', 
        'stateMutability': 'pure',
        'inputs': [{'type': 'uint256'}]  # Error in 1.17.0
    }
]

Steps to Reproduce

  1. Save the code above as test_regression.py
  2. Run with mypy 1.16.1: mypy test_regression.py → ✅ Success
  3. Run with mypy 1.17.0: mypy test_regression.py → ❌ Fails

Error Output (mypy 1.17.0)

standalone_bug_test.py:43: error: Incompatible types (expression has type "list[dict[str, str]]", TypedDict item "components" has type "Sequence[Component]")  [typeddict-item]
standalone_bug_test.py:62: error: Incompatible types (expression has type "list[dict[str, str]]", TypedDict item "inputs" has type "Sequence[Component]")  [typeddict-item]

Analysis

This is a clear regression where mypy 1.17.0 is no longer properly recognizing dict literals as structurally compatible with TypedDict types when used in Sequence[TypedDict] contexts. The fundamental principle of TypedDict structural typing is broken.

Root Cause

The issue appears to be in mypy's type inference logic where:

  1. Dict literals inside lists are inferred as dict[str, str] instead of the contextually expected TypedDict
  2. This inference happens before structural compatibility is checked
  3. The resulting list[dict[str, str]] is then (correctly) rejected as incompatible with Sequence[TypedDict]

Expected Fix

Dict literals should be contextually typed when the expected type is Sequence[TypedDict], allowing structural compatibility to work as intended.

Workarounds

  • Use # type: ignore[typeddict-item] or # type: ignore[assignment] comments
  • Use explicit cast(list[Component], [...])
  • Use explicit TypedDict construction: Component(type='uint256', name='value')
  • Downgrade to mypy 1.16.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions