Skip to content

Hint and/or lint for changing List/Set/Map from to of #58359

Open

Description

@rakudrama

Many uses of List.from can be changed to List.of.
The reasons to do so:

  • List.of is nicer to use because the extra constraint helps type inference and editor suggestions
  • List.of is more efficient
  • There is an education problem that since List.of is newer, there are lots of poor examples of List.from that could have been migrated to List.of.

Examples that could be hints (i.e. no false positives):

  • The explicit or inferred type argument E in List<E>.from(iterable) matches the static type of the iterable argument
  • The explicit type argument E in List<E>.from(iterable) is a supertype of the static type of the iterable argument

More complicated is where dynamic creeps in.
I have seen examples like:

void foo(Map<String, String> map) {
  for (final key in List.from(map.keys)) {
    print(key.isEmpty);
...

If we change List.from to List.of, the inferred type of key moves from dynamic to String. This would be helpful to the developer for completion suggestion, since key.isE• can now only complete to key.isEmpty, and not key.isEven.
We would want to suggest the replacement if the new inferred types do not lead to new or different errors or warnings or a different resolution of elements (e.g. a dynamic instance method call becoming a static extension method call).

Activity

  1. transferred this issue fromdart-lang/sdkon Mar 23, 2021
  2. pq commented on Mar 24, 2021

    @pq
    Member

    Does the same advice extend to Set.of?

  3. a14n commented on Mar 24, 2021

    @a14n
    Contributor

    Currious : is List<E>.of(iterableOfE) better than iterableOfE.toList() ?

    If you look at https://api.dart.dev/stable/2.8.4/dart-core/Iterable/toList.html you can see that the implementation relies on List.from. Should it be changed to use List.of ?

  4. a14n commented on Mar 24, 2021

    @a14n
    Contributor

    Does the same advice extend to Set.of?

    And Map.of?

  5. rakudrama commented on Mar 24, 2021

    @rakudrama
    MemberAuthor

    The advice extends to Set.of and Map.of.

    @a14n: Yes, we should change that List.from to List.of. That code dates back to Dart 1 before there was a List.of, and the proposed hint would help find it.


    @a14n: Regarding iterableOfE, less clear cut, so I would not suggest a similar hint.

    They are different in that the run time type value E for toList comes from the object, and for List<E>.of from the context of the call.
    I would use List.of if I wanted to update an element of the list; for lists that are not modified, toList is fine.

    Reasons to use iterableOfE.toList():

    • It is more concise
    • Plays well with type infererence
    • It is in the right place when thinking of a chain like where-map-where-toList.
    • It can be a custom implementation, bound to the receiver, which can be more efficient that List.of. For this reason sometimes I wish List.of was implemented in terms of toList, but we can't because toList is less trustworthy (see below).

    Reasons to use List.of:

    • You want to modify the list (e.g. assign a new element). toList might return a list with too narrow a type.
    • The compilers can do a more consistent good job with List.of because there is one implementation that is known to the compiler and the compiler knows the return type is one of the system lists, and how that depends on growable.
    • There are many implementations of toList, and some of them might be confusing for the compilers
      • The return type of toList has unconstrained variance, so it can return a subtype, e.g. => <Never>[];
      • The compilers can't always tell the exact implementation type(s) returned by toList, making handling of the returned list potentially polymorphic and less efficient.
  6. pq commented on Mar 24, 2021

    @pq
    Member
  7. Levi-Lesches commented on Jul 13, 2021

    @Levi-Lesches

    Isn't one of the main benefits of using .from that you can cast the types more precisely?

    void parse(Map<String, dynamic> json) {
      for (final MapEntry<String, dynamic> entry in json.entries) {
        print("${entry.key}: ${entry.value}");
      }
    }
    
    void main() {
      final Map json= {"hello": 1, "world": true};  // typed as Map<dynamic, dynamic>
      parse(Map<String, dynamic>.of(json));  // Error: Map<dynamic, dynamic> is not Map<String, dynamic>
      parse(Map<String, dynamic>.from(json));  // okay
    }
  8. jamesderlin commented on Jul 17, 2021

    @jamesderlin
    Contributor

    Seems like a duplicate of #58149?

  9. Levi-Lesches commented on Jul 18, 2021

    @Levi-Lesches

    Well that was created later than this, and the discussion is here, so technically #58149 is a duplicate of this

  10. jamesderlin commented on Jul 18, 2021

    @jamesderlin
    Contributor

    Well that was created later than this

    Huh? This issue seems to have been created in March 2021 (unless transferring the issue modified the timestamp?). #58149 was filed last year. =/

  11. Levi-Lesches commented on Jul 18, 2021

    @Levi-Lesches

    Oh sorry, I didn't see the years, just "March" and "April"

  12. dnfield commented on Dec 7, 2021

    @dnfield
    Contributor

    I'm seeing this issue in Flutter framework code - using from where we reallys hould use of because of extra type checking that is incurred by from.

  13. srawlins commented on Aug 3, 2022

    @srawlins
    Member

    And Queue.from => Queue.of.

  14. 1 remaining item

  15. added
    P3A lower priority bug or feature request
    on Nov 20, 2024
  16. added
    area-devexpFor issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages.
    and removed on Feb 21, 2025
  17. srawlins commented on Apr 26, 2025

    @srawlins
    Member

    See also #57106

  18. changed the title [-]Hint and/or lint for changing `List.from` to `List.of`[/-] [+]Hint and/or lint for changing List/Set/Map `from` to `of`[/+] on Apr 26, 2025
  19. added a commit that references this issue on Jul 21, 2025
  20. added a commit that references this issue on Jun 12, 2026
  21. added a commit that references this issue on Sep 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3A lower priority bug or feature requestarea-devexpFor issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages.devexp-linterIssues with the analyzer's support for the linter packagelinter-lint-proposaltype-enhancementA request for a change that isn't a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions