https://github.com/dev-cetera/df_type
Simplifies type conversions, inspections, nested data access, sync/async operations and more.
https://github.com/dev-cetera/df_type
dart library package types utilities
Last synced: about 2 hours ago
JSON representation
Simplifies type conversions, inspections, nested data access, sync/async operations and more.
- Host: GitHub
- URL: https://github.com/dev-cetera/df_type
- Owner: dev-cetera
- License: mit
- Created: 2024-08-01T11:03:42.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2026-05-22T11:27:38.000Z (about 1 month ago)
- Last Synced: 2026-05-22T16:34:00.191Z (about 1 month ago)
- Topics: dart, library, package, types, utilities
- Language: Dart
- Homepage: https://pub.dev/packages/df_type
- Size: 288 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
[](https://pub.dev/packages/df_type)
[](https://github.com/dev-cetera/df_type/tree/v0.16.0)
[](https://www.buymeacoffee.com/dev_cetera)
[](https://github.com/sponsors/dev-cetera)
[](https://www.patreon.com/robelator)
[](https://discord.gg/gEQ8y2nfyX)
[](https://www.instagram.com/dev_cetera/)
[](https://raw.githubusercontent.com/dev-cetera/df_type/main/LICENSE)
---
## Summary
Small, focused utilities for runtime type handling, lenient value coercion,
and mixed sync/async flows in Dart. Hardened for **life-critical use**:
silent data corruption, masked errors, and silent saturations are all
defects, not features.
What's in the box:
- **Safe value coercion** — `letOrNull(input)` plus a family of
`let{Int,Double,Bool,Num,Uri,DateTime,String,Iterable,List,Set,Map}OrNull`
helpers that return `null` on any failure instead of throwing. Rejects
silently-unsafe inputs (NaN, infinity, out-of-range doubles) rather than
saturating. `letIntOrNull` uses the runtime-correct safe bound on web
(`±2^53`) versus VM (`±2^63`). `letMapOrNull` rejects coerced-key
collisions instead of letting one entry overwrite another.
- **Type-level inspection** — `isSubtype()`,
`typeEquality()`, and `isNullable()` for generic-level checks
that aren't otherwise expressible in Dart.
- **`FutureOr` orchestration** — `wait`, `waitF`, and the `consec1..consec9`
family run mixed sync/async work in argument order, with `eagerError` and
lifecycle callbacks (`onError`, `onComplete`). Stays synchronous when all
inputs are synchronous. The original error always reaches the caller —
buggy handlers are surfaced through `Zone.handleUncaughtError` but never
mask the incident. `onComplete` runs on every exit path.
- **`Waiter`** — a deferred batch of operations you can build up over
time and then execute together. Operations are stored as immutable
[`WaiterOperation`](#waiteroperation--cross-isolate-friendly) value
objects, which makes the queue auditable and (when callers use top-level
functions) sendable across isolates.
- **`decodeJsonbStrings`** — recursively decodes JSON-shaped strings inside
a value tree. Handy for Postgres `jsonb` columns that may arrive
pre-decoded or as raw JSON depending on the driver. Bounded by a
`maxDepth` parameter (default `64`) so hostile or pathological nesting
can't overflow the stack.
- **Convenience extensions** — `Function.tryCall` (safe `Function.apply`,
but it deliberately does **not** swallow `Error` subtypes like
`StackOverflowError` or `AssertionError`), `Iterable.valueOf`
(case-insensitive enum lookup), and `FutureOrExt` (`isFuture`,
`withMinDuration`, etc.).
## Installation
```sh
dart pub add df_type
# or, for a Flutter project:
flutter pub add df_type
```
## Usage
```dart
import 'package:df_type/df_type.dart';
void main() async {
// Lenient scalar coercion.
letIntOrNull('42'); // 42
letIntOrNull('not a number'); // null
letIntOrNull(double.nan); // null (never throws, never saturates)
// Nested collection coercion from a JSON string.
letMapOrNull('{"a":1,"b":2}'); // {a: 1, b: 2}
// Mixed sync/async, results delivered in the order you passed them in.
final greeting = await consec3(
Future.delayed(const Duration(milliseconds: 10), () => 'hello'),
42,
Future.value('world'),
(a, b, c) => '$a $b $c',
);
print(greeting); // hello 42 world
// Deferred batch of operations via Waiter — `addFn` is the
// closure-friendly shortcut; `add(WaiterOperation(...))` is the
// isolate-portable form.
final waiter = Waiter()
..addFn(() => 'sync result', id: 'a')
..addFn(() async => 'async result', id: 'b');
final results = await waiter.wait();
print(results); // (sync result, async result)
}
```
### `WaiterOperation` — cross-isolate friendly
`Waiter` stores its queue as immutable `WaiterOperation` value objects.
Each carries a `run` function plus an optional `id` for auditing / logging.
When `run` is a top-level or `static` function, the operation (and a list of
them) is safely sendable across an `Isolate` boundary:
```dart
int heavyTask() { /* ... */ }
await Isolate.run(() async {
final w = Waiter(
operations: const [
WaiterOperation(heavyTask, id: 'compute-1'),
WaiterOperation(heavyTask, id: 'compute-2'),
],
);
return (await w.wait()).toList();
});
```
Closures (`() => ...`) capture their enclosing isolate and cannot cross a
`SendPort` — that's a Dart runtime restriction, not something the package
imposes. The value-object wrapper exists precisely so the choice between
"sendable" (top-level/static) and "local-only" (closure) is explicit and
inspectable at call sites.
## Safety guarantees
- **No silent failures.** Misused calls throw `ArgumentError` in every
build mode (no debug-only `assert`s). Coerced-key collisions in maps
cause the whole conversion to fail rather than silently overwriting.
- **The original error always wins.** A buggy `onError` / `onComplete`
handler never replaces the underlying incident; its own failure is
surfaced via `Zone.handleUncaughtError` so it is still observable but
not in the caller's catch block.
- **Cleanup always runs.** `onComplete` fires on every exit path,
including when `onError` itself throws.
- **No critical-`Error` absorption.** `Function.tryCall` swallows
`Exception`, `TypeError`, and `NoSuchMethodError` only — `StackOverflow`,
`OutOfMemory`, `AssertionError`, and `StateError` propagate.
- **Bounded recursion.** `decodeJsonbStrings` enforces a `maxDepth` (default
`64`) so hostile input cannot overflow the stack.
- **No silent saturation of integers.** `letIntOrNull` returns `null`
outside the runtime-appropriate safe bound — `±2^63` on the VM, `±2^53`
on the JS runtime where `int` is double-backed.
## Cross-platform and isolate safety
The library targets the Dart VM, the JS runtime (Flutter web, dart2js,
dartdevc), and **WebAssembly via `dart compile wasm` / `flutter build web
--wasm`**. It has no `dart:io` or `dart:isolate` imports under `lib/`, and
all `lib/` sources are pure Dart with no JS-interop or platform conditional
imports. A minimal program exercising the public surface bundles to roughly
100 KB minified via dart2js, or ~85 KB of `.wasm` + ~13 KB of JS glue via
dart2wasm — both dominated by the SDK runtime rather than this library.
Every top-level binding under `lib/` is `const` or `final` of an immutable
expression — there is **no shared mutable static state**, so multiple
isolates can use the package concurrently without interference. A dedicated
[`test/isolate_safety_test.dart`](test/isolate_safety_test.dart) suite
proves this end-to-end on the VM by sending `Waiter`s and operations
through `Isolate.run`.
---
🔍 For more information, refer to the [API reference](https://pub.dev/documentation/df_type/).
---
## 💬 Contributing and Discussions
This is an open-source project, and we warmly welcome contributions from everyone, regardless of experience level. Whether you're a seasoned developer or just starting out, contributing to this project is a fantastic way to learn, share your knowledge, and make a meaningful impact on the community.
### ☝️ Ways you can contribute
- **Find us on Discord:** Feel free to ask questions and engage with the community here: https://discord.gg/gEQ8y2nfyX.
- **Share your ideas:** Every perspective matters, and your ideas can spark innovation.
- **Help others:** Engage with other users by offering advice, solutions, or troubleshooting assistance.
- **Report bugs:** Help us identify and fix issues to make the project more robust.
- **Suggest improvements or new features:** Your ideas can help shape the future of the project.
- **Help clarify documentation:** Good documentation is key to accessibility. You can make it easier for others to get started by improving or expanding our documentation.
- **Write articles:** Share your knowledge by writing tutorials, guides, or blog posts about your experiences with the project. It's a great way to contribute and help others learn.
No matter how you choose to contribute, your involvement is greatly appreciated and valued!
### ☕ We drink a lot of coffee...
If you're enjoying this package and find it valuable, consider showing your appreciation with a small donation. Every bit helps in supporting future development. You can donate here: https://www.buymeacoffee.com/dev_cetera
## LICENSE
This project is released under the [MIT License](https://raw.githubusercontent.com/dev-cetera/df_type/main/LICENSE). See [LICENSE](https://raw.githubusercontent.com/dev-cetera/df_type/main/LICENSE) for more information.