@@ -451,7 +451,7 @@ checkers do not seem to support this either.
451451``` py
452452from typing_extensions import dataclass_transform, Any
453453
454- def fancy_field (* , init : bool = True , kw_only : bool = False ) -> Any: ...
454+ def fancy_field (* , init : bool = True , kw_only : bool = False , alias : str | None = None ) -> Any: ...
455455@dataclass_transform (field_specifiers = (fancy_field,))
456456def fancy_model[T](cls : type[T]) -> type[T]:
457457 ...
@@ -460,15 +460,15 @@ def fancy_model[T](cls: type[T]) -> type[T]:
460460@fancy_model
461461class Person :
462462 id : int = fancy_field(init = False )
463- name : str = fancy_field()
463+ internal_name : str = fancy_field(alias = " name " )
464464 age: int | None = fancy_field(kw_only = True )
465465
466466reveal_type(Person.__init__ ) # revealed: (self: Person, name: str, *, age: int | None) -> None
467467
468468alice = Person(" Alice" , age = 30 )
469469
470470reveal_type(alice.id) # revealed: int
471- reveal_type(alice.name ) # revealed: str
471+ reveal_type(alice.internal_name ) # revealed: str
472472reveal_type(alice.age) # revealed: int | None
473473```
474474
@@ -477,7 +477,7 @@ reveal_type(alice.age) # revealed: int | None
477477``` py
478478from typing_extensions import dataclass_transform, Any
479479
480- def fancy_field (* , init : bool = True , kw_only : bool = False ) -> Any: ...
480+ def fancy_field (* , init : bool = True , kw_only : bool = False , alias : str | None = None ) -> Any: ...
481481@dataclass_transform (field_specifiers = (fancy_field,))
482482class FancyMeta (type ):
483483 def __new__ (cls , name , bases , namespace ):
@@ -488,15 +488,15 @@ class FancyBase(metaclass=FancyMeta): ...
488488
489489class Person (FancyBase ):
490490 id : int = fancy_field(init = False )
491- name : str = fancy_field()
491+ internal_name : str = fancy_field(alias = " name " )
492492 age: int | None = fancy_field(kw_only = True )
493493
494494reveal_type(Person.__init__ ) # revealed: (self: Person, name: str, *, age: int | None) -> None
495495
496496alice = Person(" Alice" , age = 30 )
497497
498498reveal_type(alice.id) # revealed: int
499- reveal_type(alice.name ) # revealed: str
499+ reveal_type(alice.internal_name ) # revealed: str
500500reveal_type(alice.age) # revealed: int | None
501501```
502502
@@ -505,7 +505,7 @@ reveal_type(alice.age) # revealed: int | None
505505``` py
506506from typing_extensions import dataclass_transform, Any
507507
508- def fancy_field (* , init : bool = True , kw_only : bool = False ) -> Any: ...
508+ def fancy_field (* , init : bool = True , kw_only : bool = False , alias : str | None = None ) -> Any: ...
509509@dataclass_transform (field_specifiers = (fancy_field,))
510510class FancyBase :
511511 def __init_subclass__ (cls ):
@@ -514,15 +514,15 @@ class FancyBase:
514514
515515class Person (FancyBase ):
516516 id : int = fancy_field(init = False )
517- name : str = fancy_field()
517+ internal_name : str = fancy_field(alias = " name " )
518518 age: int | None = fancy_field(kw_only = True )
519519
520520reveal_type(Person.__init__ ) # revealed: (self: Person, name: str, *, age: int | None) -> None
521521
522522alice = Person(" Alice" , age = 30 )
523523
524524reveal_type(alice.id) # revealed: int
525- reveal_type(alice.name ) # revealed: str
525+ reveal_type(alice.internal_name ) # revealed: str
526526reveal_type(alice.age) # revealed: int | None
527527```
528528
@@ -549,6 +549,58 @@ reveal_type(Person.__init__) # revealed: (self: Person, name: str) -> None
549549Person(name = " Alice" )
550550```
551551
552+ ### Support for ` alias `
553+
554+ The ` alias ` parameter in field specifiers allows providing an alternative name for the parameter in
555+ the synthesized ` __init__ ` method.
556+
557+ ``` py
558+ from typing_extensions import dataclass_transform, Any
559+
560+ def field_with_alias (* , alias : str | None = None , kw_only : bool = False ) -> Any: ...
561+ @dataclass_transform (field_specifiers = (field_with_alias,))
562+ def model[T](cls : type[T]) -> type[T]:
563+ return cls
564+
565+ @model
566+ class Person :
567+ internal_name: str = field_with_alias(alias = " name" )
568+ internal_age: int = field_with_alias(alias = " age" , kw_only = True )
569+ ```
570+
571+ The synthesized ` __init__ ` method uses the alias names instead of the actual attribute names:
572+
573+ ``` py
574+ reveal_type(Person.__init__ ) # revealed: (self: Person, name: str, *, age: int) -> None
575+ ```
576+
577+ We can construct instances using the alias names:
578+
579+ ``` py
580+ p = Person(name = " Alice" , age = 30 )
581+ ```
582+
583+ Passing the ` name ` parameter positionally also works:
584+
585+ ``` py
586+ p = Person(" Alice" , age = 30 )
587+ ```
588+
589+ But the attributes are still accessed by their actual names:
590+
591+ ``` py
592+ reveal_type(p.internal_name) # revealed: str
593+ reveal_type(p.internal_age) # revealed: int
594+ ```
595+
596+ Trying to use the actual attribute names in the constructor results in an error:
597+
598+ ``` py
599+ # error: [unknown-argument] "Argument `internal_age` does not match any known parameter"
600+ # error: [missing-argument] "No argument provided for required parameter `age`"
601+ p = Person(name = " Alice" , internal_age = 30 )
602+ ```
603+
552604### With overloaded field specifiers
553605
554606``` py
0 commit comments