From 9f27166299a9d114516df040b58467a1bad978c3 Mon Sep 17 00:00:00 2001 From: Nikechukwu <150845642+nikeokoronkwo@users.noreply.github.com> Date: Fri, 22 Aug 2025 22:56:09 +0000 Subject: [PATCH 1/2] [interop] Add support for the `never` type --- web_generator/lib/src/ast/builtin.dart | 17 ++++++++- web_generator/lib/src/ast/declarations.dart | 38 ++++++++++++++++--- .../interop_gen/transform/transformer.dart | 1 + .../lib/src/js/typescript.types.dart | 1 + .../interop_gen/functions_expected.dart | 8 +++- .../interop_gen/functions_input.d.ts | 5 ++- .../interop_gen/variables_expected.dart | 5 +++ .../interop_gen/variables_input.d.ts | 1 + 8 files changed, 66 insertions(+), 10 deletions(-) diff --git a/web_generator/lib/src/ast/builtin.dart b/web_generator/lib/src/ast/builtin.dart index a46d7e30..87e5806a 100644 --- a/web_generator/lib/src/ast/builtin.dart +++ b/web_generator/lib/src/ast/builtin.dart @@ -24,11 +24,18 @@ class BuiltinType extends NamedType { @override bool isNullable; + /// This denotes a type that has a discardable result + /// + /// These are for types such as `void` or `never` + @override + bool discardable; + BuiltinType( {required this.name, this.typeParams = const [], this.fromDartJSInterop = false, - bool? isNullable}) + bool? isNullable, + this.discardable = false}) : isNullable = isNullable ?? false; @override @@ -82,6 +89,11 @@ class BuiltinType extends NamedType { PrimitiveType.any => (isNullable ?? false) ? anyType : BuiltinType(name: 'JSAny', fromDartJSInterop: true), + PrimitiveType.never => BuiltinType( + name: 'JSAny', + fromDartJSInterop: true, + discardable: true, + isNullable: true), PrimitiveType.unknown => anyType, PrimitiveType.object => BuiltinType( name: 'JSObject', fromDartJSInterop: true, isNullable: isNullable), @@ -185,5 +197,6 @@ enum PrimitiveType { undefined, symbol, array, - bigint + bigint, + never } diff --git a/web_generator/lib/src/ast/declarations.dart b/web_generator/lib/src/ast/declarations.dart index 1ede563c..025d15d5 100644 --- a/web_generator/lib/src/ast/declarations.dart +++ b/web_generator/lib/src/ast/declarations.dart @@ -198,7 +198,11 @@ class VariableDeclaration extends FieldDeclaration if (modifier == VariableModifier.$const) { return Method((m) => m ..docs.addAll([...doc]) - ..annotations.addAll([...annotations]) + ..annotations.addAll([ + ...annotations, + if (_checkIfDiscardable(type)) + refer('doNotStore', 'package:meta/meta.dart') + ]) ..name = name ..type = MethodType.getter ..annotations.add(generateJSAnnotation()) @@ -226,6 +230,14 @@ class VariableDeclaration extends FieldDeclaration } } +bool _checkIfDiscardable(Type type) { + if (type case BuiltinType(discardable: final typeIsDiscardable) + when typeIsDiscardable) { + return true; + } + return false; +} + enum VariableModifier { let, $const, $var } class FunctionDeclaration extends CallableDeclaration @@ -274,7 +286,11 @@ class FunctionDeclaration extends CallableDeclaration return Method((m) => m ..docs.addAll([...doc]) - ..annotations.addAll([...annotations]) + ..annotations.addAll([ + ...annotations, + if (_checkIfDiscardable(returnType)) + refer('doNotStore', 'package:meta/meta.dart') + ]) ..external = true ..name = dartName ?? name ..annotations.add(generateJSAnnotation( @@ -750,7 +766,11 @@ class PropertyDeclaration extends FieldDeclaration if (readonly) { return Method((m) => m ..docs.addAll([...doc]) - ..annotations.addAll([...annotations]) + ..annotations.addAll([ + ...annotations, + if (_checkIfDiscardable(type)) + refer('doNotStore', 'package:meta/meta.dart') + ]) ..external = true ..name = dartName ?? name ..type = MethodType.getter @@ -836,7 +856,11 @@ class MethodDeclaration extends CallableDeclaration if (isNullable) { return Method((m) => m ..docs.addAll([...doc]) - ..annotations.addAll([...annotations]) + ..annotations.addAll([ + ...annotations, + if (_checkIfDiscardable(returnType)) + refer('doNotStore', 'package:meta/meta.dart') + ]) ..external = true ..name = dartName ?? name ..type = MethodType.getter @@ -1015,7 +1039,11 @@ class OperatorDeclaration extends CallableDeclaration return Method((m) => m ..docs.addAll([...doc]) - ..annotations.addAll([...annotations]) + ..annotations.addAll([ + ...annotations, + if (_checkIfDiscardable(returnType)) + refer('doNotStore', 'package:meta/meta.dart') + ]) ..external = true ..name = 'operator $name' ..types diff --git a/web_generator/lib/src/interop_gen/transform/transformer.dart b/web_generator/lib/src/interop_gen/transform/transformer.dart index 167ed26e..e8cde859 100644 --- a/web_generator/lib/src/interop_gen/transform/transformer.dart +++ b/web_generator/lib/src/interop_gen/transform/transformer.dart @@ -1364,6 +1364,7 @@ class Transformer { TSSyntaxKind.VoidKeyword => PrimitiveType.$void, TSSyntaxKind.BigIntKeyword => PrimitiveType.bigint, TSSyntaxKind.SymbolKeyword => PrimitiveType.symbol, + TSSyntaxKind.NeverKeyword => PrimitiveType.never, _ => throw UnsupportedError( 'The given type with kind ${type.kind} is not supported yet') }; diff --git a/web_generator/lib/src/js/typescript.types.dart b/web_generator/lib/src/js/typescript.types.dart index d2c322db..d9fdf8d8 100644 --- a/web_generator/lib/src/js/typescript.types.dart +++ b/web_generator/lib/src/js/typescript.types.dart @@ -81,6 +81,7 @@ extension type const TSSyntaxKind._(num _) { static const TSSyntaxKind VoidKeyword = TSSyntaxKind._(116); static const TSSyntaxKind BigIntKeyword = TSSyntaxKind._(163); static const TSSyntaxKind SymbolKeyword = TSSyntaxKind._(155); + static const TSSyntaxKind NeverKeyword = TSSyntaxKind._(146); // types static const TSSyntaxKind UnionType = TSSyntaxKind._(192); diff --git a/web_generator/test/integration/interop_gen/functions_expected.dart b/web_generator/test/integration/interop_gen/functions_expected.dart index 3cd8ad8a..d2d6d25f 100644 --- a/web_generator/test/integration/interop_gen/functions_expected.dart +++ b/web_generator/test/integration/interop_gen/functions_expected.dart @@ -3,6 +3,8 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:js_interop' as _i1; +import 'package:meta/meta.dart' as _i2; + @_i1.JS() external String greetUser(String name); @_i1.JS() @@ -38,8 +40,12 @@ external _i1.JSObject createUser( ]); @_i1.JS() external T firstElement(_i1.JSArray arr); +@_i2.doNotStore @_i1.JS() -external void throwError(String msg); +external _i1.JSAny? throwError([String? msg]); +@_i2.doNotStore +@_i1.JS('throwError') +external _i1.JSAny? throwError$1(); @_i1.JS() external _i1.JSArray wrapInArray(T value); @_i1.JS() diff --git a/web_generator/test/integration/interop_gen/functions_input.d.ts b/web_generator/test/integration/interop_gen/functions_input.d.ts index 6fc86650..8d5121f4 100644 --- a/web_generator/test/integration/interop_gen/functions_input.d.ts +++ b/web_generator/test/integration/interop_gen/functions_input.d.ts @@ -2,13 +2,14 @@ export declare function greetUser(name: string): string; export declare function logMessages(...messages: string[]): void; export declare function delay(ms: number, returnValue?: U): Promise; export declare function toArray(a: number): number[]; +export declare function toArray(a: string): string[]; export declare function square(a: number): number; export declare function pow(a: number): number; export declare function pow(a: number, power: number): number; -export declare function toArray(a: string): string[]; export declare function createUser(name: string, age?: number, role?: string): object; export declare function firstElement(arr: T[]): T; -export declare function throwError(msg: string): void; +export declare function throwError(msg?: string): never; +export declare function throwError(): never; export declare function wrapInArray(value: T): T[]; export declare function identity(value: T): T; export declare function someFunction(arr: A[]): undefined; diff --git a/web_generator/test/integration/interop_gen/variables_expected.dart b/web_generator/test/integration/interop_gen/variables_expected.dart index 4bf7c0f6..b2af7cc5 100644 --- a/web_generator/test/integration/interop_gen/variables_expected.dart +++ b/web_generator/test/integration/interop_gen/variables_expected.dart @@ -3,6 +3,8 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:js_interop' as _i1; +import 'package:meta/meta.dart' as _i2; + @_i1.JS() external double counter; @_i1.JS() @@ -35,3 +37,6 @@ external _i1.JSAny? get maybeValue; external _i1.JSArray<_i1.JSString> get names; @_i1.JS() external _i1.JSArray<_i1.JSString> get newNames; +@_i2.doNotStore +@_i1.JS() +external _i1.JSAny? get neverUseThis; diff --git a/web_generator/test/integration/interop_gen/variables_input.d.ts b/web_generator/test/integration/interop_gen/variables_input.d.ts index 1bc7d052..975677cf 100644 --- a/web_generator/test/integration/interop_gen/variables_input.d.ts +++ b/web_generator/test/integration/interop_gen/variables_input.d.ts @@ -11,3 +11,4 @@ export declare let something: any; export declare const maybeValue: unknown; export declare const names: string[]; export declare const newNames: Array; +export declare const neverUseThis: never; From 5887345b80c8b0c37e529918494b738344014509 Mon Sep 17 00:00:00 2001 From: Nikechukwu <150845642+nikeokoronkwo@users.noreply.github.com> Date: Fri, 22 Aug 2025 23:02:35 +0000 Subject: [PATCH 2/2] removed `@override` --- web_generator/lib/src/ast/builtin.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/web_generator/lib/src/ast/builtin.dart b/web_generator/lib/src/ast/builtin.dart index 87e5806a..bee45144 100644 --- a/web_generator/lib/src/ast/builtin.dart +++ b/web_generator/lib/src/ast/builtin.dart @@ -27,7 +27,6 @@ class BuiltinType extends NamedType { /// This denotes a type that has a discardable result /// /// These are for types such as `void` or `never` - @override bool discardable; BuiltinType(