Skip to content

[Feature Request]: Access Static Variables and Methods on an Instance #4302

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
stan-at-work opened this issue Mar 26, 2025 · 11 comments
Open
Labels
feature Proposed language feature that solves one or more problems

Comments

@stan-at-work
Copy link

Currently, in Dart, static variables and methods belong to the class itself and cannot be accessed through an instance of the class. This can sometimes lead to unnecessary boilerplate when attempting to access shared class-level functionality through an instance.

Allow access to static variables and methods on an instance of a class, similar to how some other languages (e.g., JavaScript) allow this behavior. For example:

class ExampleWithARealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyLongName {
  static String staticValue = "Hello, Dart!";
  static void staticMethod() => print("Static method called!");
}

void main() {
  var instance = ExampleWithARealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyLongName ();
  
  // Proposed feature: Access static members via an instance
  print(instance.staticValue); // Expected: "Hello, Dart!"
  instance.staticMethod(); // Expected: "Static method called!"

  // Else we need to do it like this.
  print(ExampleWithARealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyLongName.staticValue); // Expected: "Hello, Dart!"
  ExampleWithARealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyLongName.staticMethod().
}

Benefits

Improves developer experience by reducing friction when accessing static members.
Eliminates unnecessary referencing of the class name, making code more concise.

Possible Concerns

May introduce ambiguity in cases where instance-level properties/methods have similar names to static ones.

Vote: 👍 for yes and 👎 for no


Ref issue

@stan-at-work stan-at-work added the feature Proposed language feature that solves one or more problems label Mar 26, 2025
@stan-at-work stan-at-work marked this as a duplicate of dart-lang/sdk#60407 Mar 26, 2025
@julemand101
Copy link

From previous discussion:

This could be fixed if we put a reserved name in between the instance name and the static var or method. (for example .static)

Only way I can see this working is if .static would only give access to the static members of the statically determined type of the variable similar to how extension methods are working. So no access to parent type static members.

Would still be a breaking change if using .static since that is currently not a reserved word for variables.

Since I don't see any useful scenario of this, this proposal are still getting a thumps down from me.

@mateusfccp
Copy link
Contributor

We don't allow a static member of a type to have the same name as a dynamic member, so this should be trivial to implement.

I would rather allow it, however. I'm pretty sure there's a sub-discussion about this somewhere, but I can't find it now.

@eernstg
Copy link
Member

eernstg commented Mar 26, 2025

One way to provide a similar effect would be to have some general type-level functions (evaluated at compile-time, relying on the static analysis). For example, we could have something which is similar to the C++ feature typeof():

class ExampleWithARealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyLongName {
  static String staticValue = "Hello, Dart!";
  static void staticMethod() => print("Static method called!");
}

void main() {
  var instance = ExampleWithARealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyLongName();
  
  print(typeof(instance).staticValue);
  typeof(instance).staticMethod();
}

When e is an expression, the term typeof(e) would be treated as the same as F, where F is declared to be a type alias whose value is the static type of e in the given scope (erasing intersections in some useful way). This means that it can be used to invoke static members even in the case where the static type of e has actual type arguments. For example, C<int>.staticMethodOfC() is an error, but F.staticMethodOfC() is OK when F is declared as typedef F = C<int>;.

typeof(e) could be allowed as a type annotation and as an actual type argument, and in general as a <type>:

void main() {
  var xs = <int>[];
  typeof(xs) ys;
  ...
  ys = <typeof(xs)?>[null];
  ...
}

The syntax e.typeof could also be considered. However, it seems reasonable to have typeof up front because the form e.typeof (where e may be a complex expression, perhaps several lines of code) seems to imply that e or parts of e must be evaluated before anything associated with typeof can take place—but the reality is that no parts of e will be executed, instead the whole term e.typeof will essentially be replaced by the static type of e at compile time. So I'd prefer typeof(e) rather than e.typeof.

@stan-at-work
Copy link
Author

One way to provide a similar effect would be to have some general type-level functions (evaluated at compile-time, relying on the static analysis). For example, we could have something which is similar to the C++ feature typeof():

class ExampleWithARealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyLongName {
static String staticValue = "Hello, Dart!";
static void staticMethod() => print("Static method called!");
}

void main() {
var instance = ExampleWithARealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyLongName();

print(typeof(instance).staticValue);
typeof(instance).staticMethod();
}
When e is an expression, the term typeof(e) would be treated as the same as F, where F is declared to be a type alias whose value is the static type of e in the given scope (erasing intersections in some useful way). This means that it can be used to invoke static members even in the case where the static type of e has actual type arguments. For example, C<int>.staticMethodOfC() is an error, but F.staticMethodOfC() is OK when F is declared as typedef F = C<int>;.

typeof(e) could be allowed as a type annotation and as an actual type argument, and in general as a <type>:

void main() {
var xs = [];
typeof(xs) ys;
...
ys = <typeof(xs)?>[null];
...
}
The syntax e.typeof could also be considered. However, it seems reasonable to have typeof up front because the form e.typeof (where e may be a complex expression, perhaps several lines of code) seems to imply that e or parts of e must be evaluated before anything associated with typeof can take place—but the reality is that no parts of e will be executed, instead the whole term e.typeof will essentially be replaced by the static type of e at compile time. So I'd prefer typeof(e) rather than e.typeof.

Would you be able to create a instance member that points to a static class using typeof(instance) ?

final instance = ExampleWithARealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyLongName();
final staticInstance = typeof(instance);

staticInstance.staticMethod(); // Work fine

@eernstg
Copy link
Member

eernstg commented Mar 26, 2025

typeof(instance) would be a type, not an expression. This means that final staticInstance = typeof(instance); would work exactly like final staticInstance = F;, assuming the following declaration:

typedef F = ExampleWithARealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyLongName;

So staticInstance would be an instance of Type and staticInstance.staticMethod() would be a compile-time error.

(That said, #4200 and #4301 would allow us to make staticInstance more powerful, including the ability to call a static method on the underlying type.)

@rrousselGit
Copy link

More realistically, I think this would be valuable ... for extension methods:

extension on ExampleWithARealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyLongName {
  void myMethod() {
    print(staticValue); // Expected: "Hello, Dart!"
    staticMethod(); // Expected: "Static method called!"
  }
}

@ykmnkmi
Copy link

ykmnkmi commented Mar 26, 2025

Don't forget typedef:

typedef EWRealyLN = ExampleWithARealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyLongName;

class ExampleWithARealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyRealyLongName {
  static String staticValue = "Hello, Dart!";
  static void staticMethod() => print("Static method called!");
}

void main() {
  print(EWRealyLN.staticValue); // Expect: "Hello, Dart!"
  EWRealyLN.staticMethod(); // Print: "Static method called!"
}

@ghost
Copy link

ghost commented Mar 26, 2025

Etude:
suppose the static method in class A returns the instance of the same type A.
Then we can call the static method without mentioning A (using shortcuts).

class A {
  static A staticMethod()=>A();
}
void main() {
 var a=A();
 var aa=([a]..add(.staticMethod()))[1];
}

Maybe a shorter version exists, but I couldn't find it.

@stan-at-work
Copy link
Author

Etude: suppose the static method in class A returns the instance of the same type A. Then we can call the static method without mentioning A (using shortcuts).

class A {
static A staticMethod()=>A();
}
void main() {
var a=A();
var aa=([a]..add(.staticMethod()))[1];
}
Maybe a shorter version exists, but I couldn't find it.

This required every class to have a empty constructor...

@ghost
Copy link

ghost commented Mar 26, 2025

This required every class to have a empty constructor...

I don't understand the connection to empty constructor, sorry.

The etude was supposed to illustrate the interplay between different features. It's an argument in favor of adding A.static.staticMethod() to the language—the feature that doesn't require jumping through the hoops.

@lrhn
Copy link
Member

lrhn commented Mar 27, 2025

We don't allow a static member of a type to have the same name as a dynamic member, so this should be trivial to implement.

I expect that to change at some point. With extensions, and even more if allowing static extension members (#723), you can already have something that looks like the same name as both a static member and an instance member.
Might as well allow you to do it properly.

For this request, there is nothing it allows which you can't write without this feature. The type that the static is called on is statically known, so you could just write it.
Unless you cannot write it, because it's a privately named class, whose type has leaked out of an API. In that case, you probably also shouldn't be able to access static members either.
Unless there is a publicly named type alias for the class, which you could use to access the static members through, and that public alias is deliberately made accessible (not just inside lib/src/ of some other package) - but that's not something we can expect a compiler to be able to understand.

The names of types, which you use to access static members, are also access controls. Allowing you to access statics without writing the name bypasses that access control. That's true for dot-notation too, so that ship has probably sailed. Just don't expose your private types in your public API, and you should be fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems
Projects
None yet
Development

No branches or pull requests

7 participants