Description
Dart allows accessing static members and constructors of classes (class/mixin/enum declarations) through type aliases, but only if the type alias doesn't "expand to a type variable" (and then only if it explicitly denotes a class.)
A type alias expands to a type variable if its RHS is a type variable, or it's Q<TypeArgsOpt>
where Q
is a (possibly qualified) identifier denoting a type alias which expands to a type variable.
(We don't care what the type arguments are, we just follow the name at the head of the type until a non-type-alias is found.)
So a type alias explicitly denotes a non-alias declaration C
if its RHS is = Q<TypeArgsOpt>;
where Q
is an identifier or qualified identifier directly denoting C
, or transitively if Q
denotes another type alias declaration which then explicitly denotes C
.
Further, accessing statics on instantiated type expressions (Q<TypeArgs>
) is always an error. If Q<TypeArgs>
is a type expression, then the only continuations are constructor tear-offs or invocations, (args)
, .new(args)
, .name(args)
, .new
, or .name
. Any other follow-up selector is a compile-time error.
Which means you can't do typedef A<T> = T; A<int>.parse("1");
for two reasons: parse
is not a constructor, so it can't be invoked on an instantiated type expression at all, and A
doesn't denote a class.
Example:
void main() {
A<C>.named;
A<C>.stat;
A<C>.inst;
C2<int>.stat;
}
extension on Type {
get inst => 42;
}
class C {
C();
C.named();
static get stat => 0;
}
typedef A<T> = T;
typedef C2<T> = C;
The front end gives the following errors (from dartpad.dev).:
lib/main.dart:3:3:
Error: Can't use a typedef denoting a type variable as a constructor, nor for a static member access.
A<C>.named;
^
lib/main.dart:19:11:
Info: This is the type variable ultimately denoted.
typedef A<T> = T;
^
lib/main.dart:4:3:
Error: Can't use a typedef denoting a type variable as a constructor, nor for a static member access.
A<C>.stat;
^
lib/main.dart:19:11:
Info: This is the type variable ultimately denoted.
typedef A<T> = T;
^
lib/main.dart:5:3:
Error: Can't use a typedef denoting a type variable as a constructor, nor for a static member access.
A<C>.inst;
^
lib/main.dart:19:11:
Info: This is the type variable ultimately denoted.
typedef A<T> = T;
^
lib/main.dart:7:11:
Error: Cannot access static member on an instantiated generic class.
C2<int>.stat;
^^^^
lib/main.dart:3:8:
Error: The getter 'named' isn't defined for the class 'Type'.
- 'Type' is from 'dart:core'.
A<C>.named;
^^^^^
lib/main.dart:4:8:
Error: The getter 'stat' isn't defined for the class 'Type'.
- 'Type' is from 'dart:core'.
A<C>.stat;
^^^^
Error: Compilation failed.
which is one error per place the term A<C>
is used as receiver for a member access, and one where C2<int>
is used.
(Plus two spurious errors where it seems to try doing the access on a Type
object instead, which shouldn't happen.)
The analyzer gives only one error:
error
line 7 • The static member 'stat' can't be accessed on a class instantiation.
Try removing the type arguments from the class name, or changing the member name to the name of a constructor.
info
line 11 • The declaration 'inst' isn't referenced.
Try removing the declaration of 'inst'.
(and one info, saying that the extension method inst
isn't used, which is doubly weird).
Something is going wrong with errors about accessing members through type aliases which expands to a type variable.
The analyzer correctly recognizes that C2<int>.stat
is not allowed, but not that A<C>.stat
is not allowed for the same reason, and for another reason too.