supertypes 0.0.5 copy "supertypes: ^0.0.5" to clipboard
supertypes: ^0.0.5 copied to clipboard

A library for better, super records

Supertypes is a library for generating complex records. This library has been heavily inspired by TypeScript's Utility Types.

Installation #

Add supertypes and it's companion generator to your pubspec.yaml. The easiest way to do this is to use the command line:

For Dart projects:

dart pub add supertypes dev:supertypes_generator dev:build_runner

For Flutter projects:

flutter pub add supertypes dev:supertypes_generator dev:build_runner

To generate the types, run build_runner:

For Dart projects:

dart run build_runner build # Build once
dart run build_runner watch # Build continuously

For Flutter projects:

flutter pub run build_runner build # Build once
flutter pub run build_runner watch # Build continuously

Usage #

Every file containing a supertype must have the following:

It should start with a part directive.

import 'package:supertypes/supertypes.dart';

part 'example.supertypes.dart';

It should contain a typedef with the name of the supertype, prefixed with $ for public generated records, or _$ for private generated records.

// Generates a public record called `Example`
@superType
typedef $Example = ();

// Generates a private record called `_Example`
@superType
typedef _$Example = ();

Index #

Modifier types #

Modifier types are types that take one or more types and return a new type.

Partial #

The Partial modifier type takes a type and makes all of it's fields optional.

import 'package:supertypes/supertypes.dart';

part 'person.supertypes.dart';

typedef Person = ({
  String firstName,
  String lastName,
  int age,
});

// We want to make all the field nullable to support partial updates
@superType
typedef $UpdatePerson = Partial<Person>;

// This generates:
typedef UpdatePerson = ({
  String? firstName,
  String? lastName,
  int? age,
});

WithPartial #

The WithPartial modifier type takes a type and a list of fields to make optional.

import 'package:supertypes/supertypes.dart';

part 'person.supertypes.dart';

typedef Person = ({
  int id,
  String firstName,
  String lastName,
  int age,
});

// We want to make `id` nullable
@superType
typedef $PersonWithNullableId = WithPartial<Person, ({Partial id})>;

// This generates:

/// Generate for [$PersonWithNullableId]
typedef PersonWithNullableId = ({
  int? id,
  String firstName,
  String lastName,
  int age,
});

Required #

The Required modifier type takes a type and makes all of it's fields required.

import 'package:supertypes/supertypes.dart';

part 'person.supertypes.dart';

// Let's say we have a person type with `lastName` nullable, 
// and we can't update the type easily. 
// We can instead create a new type with all fields required.
typedef Person = ({
  String firstName,
  String? lastName,
  int age,
});

// We want to make all the field nullable to support partial updates
@superType
typedef $CreatePerson = Required<Person>;

// This generates:
typedef CreatePerson = ({
  String firstName,
  String lastName,
  int age,
});

WithRequired #

The WithRequired modifier type takes a type and a list of fields to make required.

import 'package:supertypes/supertypes.dart';

part 'person.supertypes.dart';

typedef Person = ({
  String firstName,
  String? lastName,
  int age,
});

// We want to make `lastName` required
@superType
typedef $PersonWithRequiredLastName = WithRequired<Person, ({Required lastName})>;

// This generates:
typedef PersonWithRequiredLastName = ({
  String firstName,
  String lastName,
  int age,
});

Omit #

The Omit modifier type takes a type and a list of fields to omit from the type.

import 'package:supertypes/supertypes.dart';

part 'person.supertypes.dart';

typedef Person = ({
  String firstName,
  String lastName,
  int age,
});

// We want to remove age from the type
@superType
typedef $PersonWithoutAge = Omit<Person, ({Omit age})>;

// This generates:

/// Generate for [$PersonWithoutAge]
typedef PersonWithoutAge = ({
  String firstName,
  String lastName,
});

Omitting positional fields

It's possible to also omit a positional field. For example:

typedef Person = (String name, int age);

// We want to remove the x field from the type
@superType
typedef $Age = Omit<Person, (Omit,)>; // This removes the first positional field

// This removes the second positional field. 
// We use `Pick` as a placeholder for the first positional field.
@superType
typedef $Name = Omit<Person, (Pick, Omit)>; 

// This generates:

/// Generate for [$Age]
typedef Age = (int,);

/// Generate for [$Name]
typedef Name = (String,);

Pick #

The Pick modifier type takes a type and a list of fields to pick from the type and ignores the rest.

import 'package:supertypes/supertypes.dart';

part 'person.supertypes.dart';

typedef Person = ({
  String firstName,
  String lastName,
  int age,
});

// We want to remove age from the type
@superType
typedef $PersonWithoutAge = Pick<Person, ({Pick firstName, Pick lastName})>;

// This generates:

/// Generate for [$PersonWithoutAge]
typedef PersonWithoutAge = ({
  String firstName,
  String lastName,
});

You can also apply a modifier type to the fields you pick:

import 'package:supertypes/supertypes.dart';

part 'person.supertypes.dart';

typedef Person = ({
  String? firstName,
  String? lastName,
  int age,
});

// Let's make age nullable and and pick firstName as a required field
@superType
typedef $PersonWithNullableAge = Pick<Person, ({Required firstName, Partial age})>;

// This generates:

/// Generate for [$PersonWithNullableAge]
typedef PersonWithNullableAge = ({
  int? age,
  String firstName,
});

Picking positional fields

It's possible to also pick a positional field. For example:

typedef Person = (String name, int age);

// We want to remove the x field from the type
@superType
typedef $Name = Pick<Person, (Pick,)>; // This removes the first positional field

// This removes the second positional field. 
// We use `Pick` as a placeholder for the first positional field.
@superType
typedef $Age = Pick<Person, (Omit, Pick)>; 

// This generates:

/// Generate for [$Name]
typedef Name = (String,);

/// Generate for [$Age]
typedef Age = (int,);

Merge #

The Merge modifier type takes two types and merges them into one type.

import 'package:supertypes/supertypes.dart';

part 'person.supertypes.dart';

typedef Person = ({
  String firstName,
  String lastName,
  int age,
});

typedef Address = ({
  String street,
  String city,
  String country,
});

// We want to merge the two types
@superType
typedef $PersonWithAddress = Merge<Person, Address>;

// This generates:

/// Generate for [$PersonWithAddress]
typedef PersonWithAddress = ({
  String firstName,
  String lastName,
  int age,
  String street,
  String city,
  String country,
});

Using Supertypes within another Supertype #

Supertypes can be used within other supertypes. This is useful for creating complex types that can be reused. To use a supertype within another supertype, simply use the original type you prefixed with $ or _$.

import 'package:supertypes/supertypes.dart';

part 'person.supertypes.dart';

typedef Person = ({
  String firstName,
  String lastName,
  int age,
});

@superType
typedef $CreatePerson = Required<Person>;

@superType
typedef $UpdatePerson = Partial<Person>;

@superType
typedef $PersonOperation = ({
  $CreatePerson create,
  $UpdatePerson update,
});

// This generates:

/// Generate for [$CreatePerson]
typedef CreatePerson = ({
  int age,
  String firstName,
  String lastName,
});

/// Generate for [$UpdatePerson]
typedef UpdatePerson = ({
  int? age,
  String? firstName,
  String? lastName,
});

/// Generate for [$PersonOperation]
typedef PersonOperation = ({
  ({
    int age,
    String firstName,
    String lastName,
  }) create,
  ({
    int? age,
    String? firstName,
    String? lastName,
  }) update,
});

Features: #

  • More modifier types
    • Partial - Make all fields nullable
    • Required - Make all fields required
    • Omit - Omit fields from a type
    • Pick - Pick fields from a type
    • Merge - Merge two types
    • WithRequired - Make certain fields required
    • WithPartial - Make certain fields optional
    • Awaited - Unwrap a Future type recursively
  • JSON serialization and deserialization for records
    • Generate fromJson top level function
    • Generate toJson extension method on the record
  • Support for using classes in the supertype records by extracting their fields