diff --git a/web_generator/lib/src/ast/helpers.dart b/web_generator/lib/src/ast/helpers.dart index dade60cc..fe17b5b0 100644 --- a/web_generator/lib/src/ast/helpers.dart +++ b/web_generator/lib/src/ast/helpers.dart @@ -259,6 +259,8 @@ Type desugarTypeAliases(Type t) { if (t case final ReferredType ref when ref.declaration is TypeAliasDeclaration) { return desugarTypeAliases((ref.declaration as TypeAliasDeclaration).type); + } else if (t case ReferredDeclarationType(type: final actualType)) { + return desugarTypeAliases(actualType); } return t; } diff --git a/web_generator/lib/src/ast/types.dart b/web_generator/lib/src/ast/types.dart index 17ff2537..19b9c586 100644 --- a/web_generator/lib/src/ast/types.dart +++ b/web_generator/lib/src/ast/types.dart @@ -673,48 +673,48 @@ sealed class _UnionOrIntersectionDeclaration extends NamedDeclaration dartTypeName ?? typeName, _ => t.dartName ?? t.id.name }; + final Expression body; + if (desugarTypeAliases(t) == repType) { + body = refer('_'); + } else if (jsTypeAlt.id == t.id) { + body = refer('_').asA(type); + } else { + body = switch (t) { + BuiltinType(name: final n) when n == 'int' => refer('_') + .asA(jsTypeAlt.emit(options?.toTypeOptions())) + .property('toDartInt'), + BuiltinType(name: final n) when n == 'double' || n == 'num' => + refer('_') + .asA(jsTypeAlt.emit(options?.toTypeOptions())) + .property('toDartDouble'), + BuiltinType() => refer('_') + .asA(jsTypeAlt.emit(options?.toTypeOptions())) + .property('toDart'), + ReferredType( + declaration: final decl, + name: final n, + url: final url + ) + when decl is EnumDeclaration => + refer(n, url).property('_').call([ + refer('_') + .asA(jsTypeAlt.emit(options?.toTypeOptions())) + .property(decl.baseType is NamedType + ? switch ((decl.baseType as NamedType).name) { + 'int' => 'toDartInt', + 'num' || 'double' => 'toDartDouble', + _ => 'toDart' + } + : 'toDart') + ]), + _ => refer('_').asA(jsTypeAlt.emit(options?.toTypeOptions())) + }; + } m ..type = MethodType.getter ..name = 'as${uppercaseFirstLetter(word)}' ..returns = type - ..body = jsTypeAlt.id == t.id - ? refer('_').asA(type).code - : switch (t) { - BuiltinType(name: final n) when n == 'int' => refer('_') - .asA(jsTypeAlt.emit(options?.toTypeOptions())) - .property('toDartInt') - .code, - BuiltinType(name: final n) - when n == 'double' || n == 'num' => - refer('_') - .asA(jsTypeAlt.emit(options?.toTypeOptions())) - .property('toDartDouble') - .code, - BuiltinType() => refer('_') - .asA(jsTypeAlt.emit(options?.toTypeOptions())) - .property('toDart') - .code, - ReferredType( - declaration: final decl, - name: final n, - url: final url - ) - when decl is EnumDeclaration => - refer(n, url).property('_').call([ - refer('_') - .asA(jsTypeAlt.emit(options?.toTypeOptions())) - .property(decl.baseType is NamedType - ? switch ((decl.baseType as NamedType).name) { - 'int' => 'toDartInt', - 'num' || 'double' => 'toDartDouble', - _ => 'toDart' - } - : 'toDart') - ]).code, - _ => refer('_') - .asA(jsTypeAlt.emit(options?.toTypeOptions())) - .code - }; + ..body = body.code; }); }))); } diff --git a/web_generator/lib/src/interop_gen/transform/transformer.dart b/web_generator/lib/src/interop_gen/transform/transformer.dart index a5c82801..3237e4ee 100644 --- a/web_generator/lib/src/interop_gen/transform/transformer.dart +++ b/web_generator/lib/src/interop_gen/transform/transformer.dart @@ -1113,6 +1113,42 @@ class Transformer { return _getTypeFromTypeNode(refType, typeArg: typeArg, isNullable: isNullable ?? false); + case TSSyntaxKind.TypePredicate: + // in the future, we can be smarter about this + // but for now, we just have this as a boolean + return BuiltinType.primitiveType(PrimitiveType.boolean, + isNullable: isNullable); + case TSSyntaxKind.ConditionalType: + final conditionalType = type as TSConditionalTypeNode; + final trueType = _transformType(conditionalType.trueType); + final falseType = _transformType(conditionalType.falseType); + + final types = [trueType, falseType] + .sorted((a, b) => a.id.toString().compareTo(b.id.toString())); + + final expectedID = ID(type: 'type', name: types.join('|')); + + if (typeMap.containsKey(expectedID.toString())) { + return (typeMap[expectedID.toString()] as UnionType) + ..isNullable = (isNullable ?? false); + } + + final trueTypeName = trueType is NamedType + ? trueType.name + : trueType.dartName ?? trueType.id.name; + + final falseTypeName = falseType is NamedType + ? falseType.name + : falseType.dartName ?? falseType.id.name; + final conditionalName = '${trueTypeName}Or$falseTypeName'; + + final un = UnionType(types: types, name: conditionalName); + final unType = typeMap.putIfAbsent(expectedID.toString(), () { + namer.markUsed(conditionalName); + return un; + }); + + return unType..isNullable = (isNullable ?? false); case TSSyntaxKind.TypeLiteral: // type literal final typeLiteralNode = type as TSTypeLiteralNode; diff --git a/web_generator/lib/src/js/typescript.types.dart b/web_generator/lib/src/js/typescript.types.dart index 5335c8d0..2a81435a 100644 --- a/web_generator/lib/src/js/typescript.types.dart +++ b/web_generator/lib/src/js/typescript.types.dart @@ -100,6 +100,8 @@ extension type const TSSyntaxKind._(num _) { static const TSSyntaxKind FunctionType = TSSyntaxKind._(184); static const TSSyntaxKind ConstructorType = TSSyntaxKind._(185); static const TSSyntaxKind TypeOperator = TSSyntaxKind._(198); + static const TSSyntaxKind TypePredicate = TSSyntaxKind._(182); + static const TSSyntaxKind ConditionalType = TSSyntaxKind._(194); // Other static const TSSyntaxKind Identifier = TSSyntaxKind._(80); @@ -202,6 +204,24 @@ extension type TSParenthesizedTypeNode._(JSObject _) implements TSTypeNode { external TSTypeNode get type; } +@JS('TypePredicateNode') +extension type TSTypePredicateNode._(JSObject _) implements TSTypeNode { + @redeclare + TSSyntaxKind get kind => TSSyntaxKind.TypePredicate; + external TSIdentifier get parameterName; + external TSTypeNode? get type; +} + +@JS('ConditionalTypeNode') +extension type TSConditionalTypeNode._(JSObject _) implements TSTypeNode { + @redeclare + TSSyntaxKind get kind => TSSyntaxKind.ConditionalType; + external TSTypeNode get checkType; + external TSTypeNode get extendsType; + external TSTypeNode get trueType; + external TSTypeNode get falseType; +} + @JS('TupleTypeNode') extension type TSTupleTypeNode._(JSObject _) implements TSTypeNode { external TSNodeArray get elements; diff --git a/web_generator/test/integration/interop_gen/ts_typing_expected.dart b/web_generator/test/integration/interop_gen/ts_typing_expected.dart index a9d185bd..91048bba 100644 --- a/web_generator/test/integration/interop_gen/ts_typing_expected.dart +++ b/web_generator/test/integration/interop_gen/ts_typing_expected.dart @@ -15,6 +15,15 @@ external String myFunction(String param); @_i1.JS() external String myEnclosingFunction(_i1.JSFunction func); @_i1.JS() +external bool objectIsProduct(_i1.JSObject obj); +@_i1.JS() +external AnonymousType_2194029 get randomNonTypedProduct; +@_i1.JS() +external ProductOrrandomNonTypedProduct objectAsProduct( + _i1.JSObject obj, + bool structured, +); +@_i1.JS() external _i1.JSArray> indexedArray(_i1.JSArray arr); @_i1.JS() @@ -84,8 +93,6 @@ external _i2.JSTuple4<_i1.JSString, _i1.JSNumber, _i1.JSBoolean, _i1.JSSymbol> @_i1.JS() external AnonymousUnion_7503220 get eightOrSixteen; @_i1.JS() -external AnonymousType_2194029 get randomNonTypedProduct; -@_i1.JS() external AnonymousType_1358595 get config; extension type MyProduct._(_i1.JSObject _) implements Product { external MyProduct( @@ -125,6 +132,26 @@ external _i1.JSAny? get someIntersection; external AnonymousIntersection_4895242 get myThirdIntersection; @_i1.JS() external AnonymousIntersection_1711585 get myTypeGymnastic; +extension type AnonymousType_2194029._(_i1.JSObject _) implements _i1.JSObject { + external AnonymousType_2194029({ + double id, + String name, + double price, + }); + + external double id; + + external String name; + + external double price; +} +typedef Product = AnonymousType_2194029; +extension type ProductOrrandomNonTypedProduct._(AnonymousType_2194029 _) + implements AnonymousType_2194029 { + Product get asProduct => _; + + AnonymousType_2194029 get asRandomNonTypedProduct => _; +} extension type AnonymousType_9143117._(_i1.JSObject _) implements _i1.JSObject { external AnonymousType_9143117({ @@ -210,19 +237,6 @@ extension type AnonymousUnion_7503220._(_i1.JSTypedArray _) _i1.JSUint16Array get asJSUint16Array => (_ as _i1.JSUint16Array); } -extension type AnonymousType_2194029._(_i1.JSObject _) implements _i1.JSObject { - external AnonymousType_2194029({ - double id, - String name, - double price, - }); - - external double id; - - external String name; - - external double price; -} extension type AnonymousType_1358595._(_i1.JSObject _) implements _i1.JSObject { external AnonymousType_1358595({ double discountRate, @@ -233,7 +247,6 @@ extension type AnonymousType_1358595._(_i1.JSObject _) implements _i1.JSObject { external double taxRate; } -typedef Product = AnonymousType_2194029; extension type AnonymousType_2773310._(_i1.JSObject _) implements _i1.JSObject { external AnonymousType_2773310({ String id, diff --git a/web_generator/test/integration/interop_gen/ts_typing_input.d.ts b/web_generator/test/integration/interop_gen/ts_typing_input.d.ts index b5697fec..5e2b5b8f 100644 --- a/web_generator/test/integration/interop_gen/ts_typing_input.d.ts +++ b/web_generator/test/integration/interop_gen/ts_typing_input.d.ts @@ -29,7 +29,6 @@ export declare const myEnumValue2: typeof MyEnum; export declare function myFunction(param: string): string; export declare let myFunctionAlias: typeof myFunction; export declare let myFunctionAlias2: typeof myFunctionAlias; -// export declare let myPreClone: typeof myComposedType; export declare function myEnclosingFunction(func: typeof myFunction): string; export declare const myEnclosingFunctionAlias: typeof myEnclosingFunction; export declare const myComposedType: ComposedType; @@ -65,7 +64,9 @@ export declare class MyProduct implements Product { price: number; constructor(id: number, name: string, price: number); } -export function indexedArray(arr: T[]): { id: number, value: T }[]; +export declare function objectIsProduct(obj: object): obj is Product; +export declare function objectAsProduct(obj: object, structured: boolean): (typeof structured) extends true ? Product : typeof randomNonTypedProduct; +export declare function indexedArray(arr: T[]): { id: number, value: T }[]; export const responseObject: { id: string; value: any;