Skip to content

Adapt implicit cast example for type system page #6478

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

Merged
merged 5 commits into from
Mar 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions examples/type_system/lib/strong_analysis.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,32 @@ void _miscDeclAnalyzedButNotTested() {
}
}

{
// #docregion downcast-check
int assumeString(dynamic object) {
// ignore: stable, beta, dev, invalid_assignment
String string = object; // Check at run time that `object` is a `String`.
return string.length;
}
// #enddocregion downcast-check

// #docregion fail-downcast-check
final length = assumeString(1);
// #enddocregion fail-downcast-check
}

{
// #docregion type-inference-1-orig
Map<String, dynamic> arguments = {'argA': 'hello', 'argB': 42};
Map<String, Object?> arguments = {'argA': 'hello', 'argB': 42};
// #enddocregion type-inference-1-orig

// ignore: stable, beta, dev, argument_type_not_assignable
arguments[1] = null;

// #docregion type-inference-2-orig
Map<String, dynamic> message = {
Map<String, Object?> message = {
'method': 'someMethod',
'args': <Map<String, dynamic>>[arguments],
'args': <Map<String, Object?>>[arguments],
};
// #enddocregion type-inference-2-orig
}
Expand Down
11 changes: 11 additions & 0 deletions src/_sass/components/_code.scss
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,14 @@ pre {
padding-left: 1.25rem;
padding-right: 1.25rem;
width: 100%;
display: inline-block;
min-width: 100%;
border-left: 2px solid rgba(0, 0, 0, 0);

&.highlighted-line {
background: color.change($brand-primary, $alpha: 0.1);
min-width: 100%;
border-left-color: $brand-primary;
}
}

Expand Down Expand Up @@ -199,6 +204,12 @@ pre {
&:not([lang="console"]) {
line-height: 1.8;
}

code {
display: block;
min-width: fit-content;
width: 100%;
}
}
}

Expand Down
41 changes: 39 additions & 2 deletions src/content/language/type-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,43 @@ void main() {
}
```

### Implicit downcasts from `dynamic`

Expressions with a static type of `dynamic` can be
implicitly cast to a more specific type.
If the actual type doesn't match, the cast throws an error at run time.
Consider the following `assumeString` method:

<?code-excerpt "lib/strong_analysis.dart (downcast-check)" replace="/string = object/[!$&!]/g"?>
```dart tag=passes-sa
int assumeString(dynamic object) {
String [!string = object!]; // Check at run time that `object` is a `String`.
return string.length;
}
```

In this example, if `object` is a `String`, the cast succeeds.
If it's not a subtype of `String`, such as `int`,
a `TypeError` is thrown:

<?code-excerpt "lib/strong_analysis.dart (fail-downcast-check)" replace="/1/[!$&!]/g"?>
```dart tag=runtime-fail
final length = assumeString([!1!]);
```

:::tip
To prevent implicit downcasts from `dynamic` and avoid this issue,
consider enabling the analyzer's _strict casts_ mode.

```yaml title="analysis_options.yaml" highlightLines=3
analyzer:
language:
strict-casts: true
```

To learn more about customizing the analyzer's behavior,
check out [Customizing static analysis](/tools/analysis).
:::

## Type inference

Expand All @@ -291,9 +328,9 @@ pairs string keys with values of various types.

If you explicitly type the variable, you might write this:

<?code-excerpt "lib/strong_analysis.dart (type-inference-1-orig)" replace="/Map<String, dynamic\x3E/[!$&!]/g"?>
<?code-excerpt "lib/strong_analysis.dart (type-inference-1-orig)" replace="/Map<String, Object\?\x3E/[!$&!]/g"?>
```dart
[!Map<String, dynamic>!] arguments = {'argA': 'hello', 'argB': 42};
[!Map<String, Object?>!] arguments = {'argA': 'hello', 'argB': 42};
```

Alternatively, you can use `var` or `final` and let Dart infer the type:
Expand Down