Skip to content
Merged
37 changes: 37 additions & 0 deletions web_generator/lib/src/ast/declarations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,40 @@ class EnumMember {

String? dartName;
}

class TypeAliasDeclaration extends NamedDeclaration
implements ExportableDeclaration {
@override
final String name;

final List<GenericType> typeParameters;

final Type type;

@override
final String? dartName;

@override
bool exported;

@override
ID get id => ID(type: 'typealias', name: name);

TypeAliasDeclaration(
{required this.name,
this.typeParameters = const [],
required this.type,
required this.exported})
: dartName = null;

@override
TypeDef emit([DeclarationOptions? options]) {
options ??= DeclarationOptions();

return TypeDef((t) => t
..name = name
..types
.addAll(typeParameters.map((t) => t.emit(options?.toTypeOptions())))
..definition = type.emit(options?.toTypeOptions()));
}
}
63 changes: 55 additions & 8 deletions web_generator/lib/src/interop_gen/transform/transformer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class Transformer {
_transformFunction(node as TSFunctionDeclaration),
TSSyntaxKind.EnumDeclaration =>
_transformEnum(node as TSEnumDeclaration),
TSSyntaxKind.TypeAliasDeclaration =>
_transformTypeAlias(node as TSTypeAliasDeclaration),
_ => throw Exception('Unsupported Declaration Kind: ${node.kind}')
};
// ignore: dead_code This line will not be dead in future decl additions
Expand Down Expand Up @@ -177,6 +179,30 @@ class Transformer {
return declarations?.toDart.first;
}

TypeAliasDeclaration _transformTypeAlias(TSTypeAliasDeclaration typealias) {
final name = typealias.name.text;

final modifiers = typealias.modifiers?.toDart;
final isExported = modifiers?.any((m) {
return m.kind == TSSyntaxKind.ExportKeyword;
}) ??
false;

final typeParams = typealias.typeParameters?.toDart;

final type = typealias.type;

return TypeAliasDeclaration(
name: name,
// TODO: Can we find a way not to make the types be JS types
// by default if possible. Leaving this for now,
// so that using such typealiases in generics does not break
type: _transformType(type),
typeParameters:
typeParams?.map(_transformTypeParamDeclaration).toList() ?? [],
exported: isExported);
}

FunctionDeclaration _transformFunction(TSFunctionDeclaration function) {
final name = function.name.text;

Expand Down Expand Up @@ -227,19 +253,24 @@ class Transformer {
}

GenericType _transformTypeParamDeclaration(
TSTypeParameterDeclaration typeParam) {
TSTypeParameterDeclaration typeParam,
{bool shouldUseJSConstraint = true}) {
final constraint = typeParam.constraint == null
? BuiltinType.anyType
: _transformType(typeParam.constraint!, typeArg: true);
return GenericType(
name: typeParam.name.text,
constraint: typeParam.constraint == null
? BuiltinType.anyType
: _transformType(typeParam.constraint!));
constraint: shouldUseJSConstraint
? getJSTypeAlternative(constraint)
: constraint);
}

/// Parses the type
///
/// TODO(https://github.com/dart-lang/web/issues/384): Add support for literals (i.e individual booleans and `null`)
/// TODO(https://github.com/dart-lang/web/issues/383): Add support for `typeof` types
Type _transformType(TSTypeNode type, {bool parameter = false}) {
Type _transformType(TSTypeNode type,
{bool parameter = false, bool typeArg = false}) {
switch (type.kind) {
case TSSyntaxKind.TypeReference:
final refType = type as TSTypeReferenceNode;
Expand All @@ -255,7 +286,10 @@ class Transformer {
// for this, and adding support for "supported declarations"
// (also a better name for that)
final supportedType = getSupportedType(
name, (typeArguments ?? []).map(_transformType).toList());
name,
(typeArguments ?? [])
.map((type) => _transformType(type, typeArg: true))
.toList());
if (supportedType != null) {
return supportedType;
}
Expand All @@ -280,8 +314,17 @@ class Transformer {
final firstNode =
declarationsMatching.whereType<NamedDeclaration>().first;

// For Typealiases, we can either return the type itself
// or the JS Alternative (if its underlying type isn't a JS type)
if (firstNode is TypeAliasDeclaration) {
final jsType = getJSTypeAlternative(firstNode.type);
if (jsType != firstNode.type && typeArg) return jsType;
}

return firstNode.asReferredType(
(typeArguments ?? []).map(_transformType).toList(),
(typeArguments ?? [])
.map((type) => _transformType(type, typeArg: true))
.toList(),
);
// TODO: Union types are also anonymous by design
// Unless we are making typedefs for them, we should
Expand Down Expand Up @@ -381,7 +424,8 @@ class Transformer {
'The given type with kind ${type.kind} is not supported yet')
};

return BuiltinType.primitiveType(primitiveType);
return BuiltinType.primitiveType(primitiveType,
shouldEmitJsType: typeArg ? true : null);
}
}

Expand Down Expand Up @@ -445,6 +489,9 @@ class Transformer {
break;
case final EnumDeclaration _:
break;
case final TypeAliasDeclaration t:
if (decl.type is! BuiltinType) filteredDeclarations.add(t.type);
break;
// TODO: We can make (DeclarationAssociatedType) and use that
// rather than individual type names
case final HomogenousEnumType hu:
Expand Down
10 changes: 10 additions & 0 deletions web_generator/lib/src/js/typescript.types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ extension type const TSSyntaxKind._(num _) {
static const TSSyntaxKind InterfaceDeclaration = TSSyntaxKind._(264);
static const TSSyntaxKind FunctionDeclaration = TSSyntaxKind._(262);
static const TSSyntaxKind ExportDeclaration = TSSyntaxKind._(278);
static const TSSyntaxKind TypeAliasDeclaration = TSSyntaxKind._(265);
static const TSSyntaxKind Parameter = TSSyntaxKind._(169);
static const TSSyntaxKind EnumDeclaration = TSSyntaxKind._(266);

Expand Down Expand Up @@ -178,6 +179,15 @@ extension type TSFunctionDeclaration._(JSObject _) implements TSDeclaration {
external TSNodeArray<TSNode> get modifiers;
}

@JS('TypeAliasDeclaration')
extension type TSTypeAliasDeclaration._(JSObject _)
implements TSDeclaration, TSStatement {
external TSNodeArray<TSNode>? get modifiers;
external TSNodeArray<TSTypeParameterDeclaration>? get typeParameters;
external TSIdentifier get name;
external TSTypeNode get type;
}

@JS('ParameterDeclaration')
extension type TSParameterDeclaration._(JSObject _) implements TSDeclaration {
external TSNode get name;
Expand Down
33 changes: 33 additions & 0 deletions web_generator/test/integration/interop_gen/typealias_expected.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// ignore_for_file: constant_identifier_names, non_constant_identifier_names

// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:js_interop' as _i1;

typedef Username = String;
typedef Age = double;
typedef IsActive = bool;
typedef Tags = _i1.JSArray<_i1.JSString>;
typedef List<T extends _i1.JSAny?> = _i1.JSArray<T>;
typedef Box<T extends _i1.JSAny?> = _i1.JSArray<_i1.JSArray<T>>;
typedef PromisedArray<U extends _i1.JSAny?, T extends _i1.JSArray<U>>
= _i1.JSPromise<T>;
typedef Shape2D = String;
typedef PrismFromShape2D<S extends _i1.JSString> = _i1.JSArray<S>;
@_i1.JS()
external Username get username;
@_i1.JS()
external Age get age;
@_i1.JS()
external _i1.JSArray<Tags> get tagArray;
@_i1.JS()
external List<_i1.JSString> get users;
@_i1.JS()
external Box<_i1.JSNumber> get matrix;
@_i1.JS()
external PrismFromShape2D<_i1.JSString> makePrism(Shape2D shape);
@_i1.JS('makePrism')
external PrismFromShape2D<S> makePrism$1<S extends _i1.JSString>(S shape);
@_i1.JS()
external PromisedArray<_i1.JSString, _i1.JSArray<_i1.JSString>> fetchNames();
@_i1.JS()
external String isUserActive(IsActive status);
18 changes: 18 additions & 0 deletions web_generator/test/integration/interop_gen/typealias_input.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export type Username = string;
export type Age = number;
export type IsActive = boolean;
export type Tags = string[];
export type List<T> = T[];
export type Box<T> = Array<Array<T>>;
export type PromisedArray<U, T extends Array<U>> = Promise<T>;
export type Shape2D = string;
export type PrismFromShape2D<S extends Shape2D> = Array<S>;
export declare const username: Username;
export declare const age: Age;
export declare const tagArray: Tags[];
export declare const users: List<Username>;
export declare const matrix: Box<number>;
export declare function makePrism(shape: Shape2D): PrismFromShape2D<Shape2D>;
export declare function makePrism<S extends Shape2D>(shape: S): PrismFromShape2D<S>;
export declare function fetchNames(): PromisedArray<string, string[]>;
export declare function isUserActive(status: IsActive): string;
Loading