@@ -146,6 +146,41 @@ class UnionType extends DeclarationType {
146146 }
147147}
148148
149+ class IntersectionType extends DeclarationType {
150+ final List <Type > types;
151+
152+ @override
153+ bool isNullable = false ;
154+
155+ @override
156+ String declarationName;
157+
158+ IntersectionType ({required this .types, required String name})
159+ : declarationName = name;
160+
161+ @override
162+ ID get id => ID (type: 'type' , name: types.map ((t) => t.id.name).join ('&' ));
163+
164+ @override
165+ Declaration get declaration =>
166+ _IntersectionDeclaration (name: declarationName, types: types);
167+
168+ @override
169+ Reference emit ([TypeOptions ? options]) {
170+ return TypeReference ((t) => t
171+ ..symbol = declarationName
172+ ..isNullable = (options? .nullable ?? false ) || isNullable);
173+ }
174+
175+ @override
176+ int get hashCode => Object .hashAllUnordered (types);
177+
178+ @override
179+ bool operator == (Object other) {
180+ return other is TupleType && other.types.every (types.contains);
181+ }
182+ }
183+
149184class HomogenousEnumType <T extends LiteralType , D extends Declaration >
150185 extends UnionType implements DeclarationType {
151186 final List <T > _types;
@@ -557,25 +592,27 @@ class _ConstructorDeclaration extends CallableDeclaration
557592 }
558593}
559594
560- // TODO: Merge properties/methods of related types
561- class _UnionDeclaration extends NamedDeclaration
595+ sealed class _UnionOrIntersectionDeclaration extends NamedDeclaration
562596 implements ExportableDeclaration {
563597 @override
564598 bool get exported => true ;
565599
566600 @override
567- ID get id => ID (type: 'union' , name: name);
568-
569- bool isNullable;
601+ ID get id;
570602
571603 List <Type > types;
572604
573605 List <GenericType > typeParameters;
574606
575- _UnionDeclaration (
607+ @override
608+ String name;
609+
610+ @override
611+ String ? dartName;
612+
613+ _UnionOrIntersectionDeclaration (
576614 {required this .name,
577615 this .types = const [],
578- this .isNullable = false ,
579616 List <GenericType >? typeParams})
580617 : typeParameters = typeParams ?? [] {
581618 if (typeParams == null ) {
@@ -588,26 +625,42 @@ class _UnionDeclaration extends NamedDeclaration
588625 }
589626 }
590627
591- @override
592- String ? dartName;
593-
594- @override
595- String name;
596-
597- @override
598- Spec emit ([covariant DeclarationOptions ? options]) {
628+ Spec _emit (
629+ {covariant DeclarationOptions ? options,
630+ bool extendTypes = false ,
631+ bool isNullable = false }) {
599632 options ?? = DeclarationOptions ();
600633
601634 final repType =
602635 getLowestCommonAncestorOfTypes (types, isNullable: isNullable);
603636
637+ final extendees = < Type > [];
638+ if (extendTypes) {
639+ // check if any types are primitive
640+ // TODO: We can be much smarter about this, but this works best so far
641+ if (types.any ((t) {
642+ final jsAltType = getJSTypeAlternative (t);
643+ return jsAltType is BuiltinType &&
644+ _nonObjectRepTypeTypes.contains (jsAltType.name);
645+ }) ||
646+ (repType is BuiltinType && repType.name == 'JSAny' )) {
647+ extendees.add (
648+ BuiltinType .primitiveType (PrimitiveType .any, isNullable: false ));
649+ } else {
650+ extendees.addAll (types.map (getJSTypeAlternative));
651+ }
652+ } else {
653+ extendees.add (repType);
654+ }
655+
604656 return ExtensionType ((e) => e
605657 ..name = name
606658 ..primaryConstructorName = '_'
607659 ..representationDeclaration = RepresentationDeclaration ((r) => r
608660 ..name = '_'
609661 ..declaredRepresentationType = repType.emit (options? .toTypeOptions ()))
610- ..implements .addAll ([repType.emit (options? .toTypeOptions ())])
662+ ..implements
663+ .addAll (extendees.map ((e) => e.emit (options? .toTypeOptions ())))
611664 ..types
612665 .addAll (typeParameters.map ((t) => t.emit (options? .toTypeOptions ())))
613666 ..methods.addAll (types.map ((t) {
@@ -719,3 +772,45 @@ class _EnumObjDeclaration extends NamedDeclaration
719772 @override
720773 ID get id => ID (type: 'enum-rep' , name: name);
721774}
775+
776+ List <String > _nonObjectRepTypeTypes = [
777+ 'JSAny' ,
778+ 'JSString' ,
779+ 'JSBoolean' ,
780+ 'JSNumber' ,
781+ 'JSSymbol' ,
782+ 'JSBigInt'
783+ ];
784+
785+ class _IntersectionDeclaration extends _UnionOrIntersectionDeclaration {
786+ @override
787+ bool get exported => true ;
788+
789+ @override
790+ ID get id => ID (type: 'intersection' , name: name);
791+
792+ _IntersectionDeclaration ({required super .name, super .types}) : super ();
793+
794+ @override
795+ Spec emit ([covariant DeclarationOptions ? options]) {
796+ return super ._emit (options: options, extendTypes: true );
797+ }
798+ }
799+
800+ class _UnionDeclaration extends _UnionOrIntersectionDeclaration {
801+ @override
802+ bool get exported => true ;
803+
804+ @override
805+ ID get id => ID (type: 'union' , name: name);
806+
807+ bool isNullable;
808+
809+ _UnionDeclaration ({required super .name, super .types, this .isNullable = false })
810+ : super ();
811+
812+ @override
813+ Spec emit ([covariant DeclarationOptions ? options]) {
814+ return super ._emit (options: options, isNullable: isNullable);
815+ }
816+ }
0 commit comments