The Type System
Understanding PolicyFlow's powerful type system
PolicyFlow features a strong, static type system that provides safety, clarity, and powerful IDE support. This guide covers all the types available in PolicyFlow and how to use them effectively.
Built-in Types Availability
All built-in types and libraries in PolicyFlow are globally available without requiring imports. This includes:
- All primitive types (
String
,Number
,Decimal
,Boolean
) - All temporal types (
DateTime
,Date
,Time
,Duration
) - All specialized types (
Email
,URL
,IPAddress
,UUID
) - All built-in libraries (
Math
,Crypto
,Runtime
,Relationships
)
Primitive Types
Basic Types
Type | Description | Example |
---|---|---|
String | Unicode text | "Hello, World!" |
Number | 64-bit signed integer | 42 , -100 , 1_000_000 |
Decimal | High-precision decimal (floating-point) | 99.99 , 0.0001 |
Boolean | True or false | true , false |
Number vs Decimal
Number
: Use for counting, IDs, array indices, and any whole number values
age: Number // 25
count: Number // 1000
priority: Number // 1, 2, 3
Decimal
: Use for monetary values, percentages, measurements, and any fractional values
price: Decimal // 99.99
taxRate: Decimal // 0.08
percentage: Decimal // 0.15
Temporal Types
Type | Description | Example |
---|---|---|
DateTime | ISO 8601 timestamp with timezone | DateTime.Parse("2025-06-12T12:00:00Z") |
Date | Date without time | Date.Today() |
Time | Time without date | Time.Parse("14:30:00") |
Duration | Time span | Duration.FromUnit(TimeUnit.Hours, 2) |
Specialized String Types
These types provide built-in validation and specialized methods:
Type | Description | Example |
---|---|---|
Email | RFC-compliant email address | "user@example.com" |
URL | Valid URL | "https://example.com/path" |
IPAddress | IPv4 or IPv6 address | "192.168.1.1" , "::1" |
UUID | Universally unique identifier | "550e8400-e29b-41d4-a716-446655440000" |
Composite Types
Arrays
Ordered collections of elements of the same type:
schema ExampleSchema {
type User {
roles: String[] // Array of strings
loginTimes: DateTime[] // Array of timestamps
scores: Number[] // Array of numbers
}
}
Maps
Key-value dictionaries with typed keys and values:
schema ExampleSchema {
type User {
attributes: Map<String, Any> // Flexible attributes
permissions: Map<String, Boolean> // Permission flags
metadata: Map<String, String> // String key-value pairs
}
}
Optional Types
Any type can be made nullable with the ?
modifier:
schema ExampleSchema {
type User {
email: Email // Required
phoneNumber: String? // Optional (can be null)
manager: User? // Optional reference
}
}
Object Types
The Object Type Concept
The Object
type serves as the conceptual parent for all complex types. It's an abstract concept and cannot be used directly as a concrete type.
schema InvalidExample {
type User {
// ERROR: Cannot use 'Object' as a concrete type
data: Object // ❌ This will not compile
}
}
Instead, define specific types:
schema ValidExample {
type UserData {
name: String
age: Number
}
type User {
data: UserData // ✅ Use a defined type
}
}
Type Inheritance
Types can inherit from other types to build complex, reusable data models:
schema BaseSchema {
// Base type with common fields
type Entity {
id: UUID
createdAt: DateTime
updatedAt: DateTime
}
// Another base type
type Auditable : Entity {
createdBy: UUID
lastModifiedBy: UUID
}
}
import * as Base from "@/schemas/base.pfs";
schema UserSchema {
// Inherits id, createdAt, updatedAt from Entity
User type CorporateUser : Base.Entity {
email: Email
department: String
}
// Inherits from Auditable (which inherits from Entity)
Resource type SensitiveDocument : Base.Auditable {
content: String
classification: String
}
}
Type Categories
Regular Types
Standard data structures that define the shape of data:
schema DataSchema {
// Regular types - not directly usable in policies
type Address {
street: String
city: String
country: String
}
type PhoneNumber {
countryCode: String
number: String
}
}
Designated Types
Types marked with User
, Resource
, Context
, or Relationship
can be used in policy schemas. The correct syntax is:
schema PolicySchema {
// Correct syntax: designation keyword before 'type'
User type Employee {
id: UUID
email: Email
roles: String[]
}
// Can be used in 'Resource from' in policies
Resource type Document {
id: UUID
ownerId: UUID
content: String
}
// Can be used in 'Context from' in policies
Context type WebRequest {
ipAddress: IPAddress
userAgent: String
}
// Can be used to define relationship schemas with attributes
Relationship type TeamMembership {
userId: UUID
teamId: UUID
role: String
permissions: String[]
since: DateTime
active: Boolean = true
}
}
The four designated type categories are:
User
: Represents the principal (person, service, or entity) performing an actionResource
: Represents the object being accessed or operated onContext
: Represents the environmental conditions of the request (IP, time, etc.)Relationship
: Defines the schema for relationship attributes in the relationship graph
Working with Any Type
The Any
type provides flexibility for dynamic data, but requires special handling:
// Schema with dynamic attributes
schema FlexibleSchema {
User type FlexibleUser {
id: UUID
attributes: Map<String, Any> // Can hold any value type
}
}
// Using Any type safely in policies
policy FlexiblePolicy {
rule CheckDynamicAttribute {
when {
// Use Runtime library for safe access
if (Runtime.HasProperty(user.attributes, "department")) {
const dept = Runtime.GetProperty(user.attributes, "department");
return dept == "Engineering";
}
return false;
}
then ALLOW
}
}
Type Constraints
Range Constraints
Numeric types can have range constraints that are validated when values are assigned:
schema ConstrainedSchema {
type User {
age: Number range(0..120)
score: Decimal range(0.0..100.0)
priority: Number range(1..5)
}
}
Example usage in policies:
policy AgeRestrictedAccess {
rules {
rule AdultContent {
when user.age >= 18 // age is guaranteed to be 0-120
then ALLOW
}
rule PriorityAccess {
when user.priority >= 3 // priority is guaranteed to be 1-5
then ALLOW
}
}
}
String Pattern Constraints
String types can be constrained with regular expressions using the pattern()
constraint:
schema PatternSchema {
type User {
username: String pattern("^[a-zA-Z0-9_]+$")
phoneNumber: String pattern("^\\+?[1-9]\\d{1,14}$")
employeeId: String pattern("^EMP-\\d{6}$")
}
}
Example usage:
policy ValidatedAccess {
rules {
rule EmployeeOnly {
// employeeId is guaranteed to match EMP-XXXXXX format
when user.employeeId.StartsWith("EMP-")
then ALLOW
}
}
}
Default Values
Types can have default values that are evaluated at object instantiation time:
schema DefaultSchema {
type User {
isActive: Boolean = true
createdAt: DateTime = DateTime.Now() // Set when object is created
role: String = "user"
tags: String[] = []
score: Decimal = 0.0
}
}
Important: Default values are evaluated when the object is created, not when the schema is defined:
// If two users are created at different times:
// User A created at 2025-06-12 10:00:00
// User B created at 2025-06-12 10:00:05
// They will have different createdAt values
policy CheckNewUsers {
rules {
rule NewUserRestrictions {
when {
// Each user's createdAt reflects when they were instantiated
const accountAge = (DateTime.Now() - user.createdAt).TotalHours();
return accountAge < 24; // Less than 24 hours old
}
then DENY
reason: "New accounts have restricted access for 24 hours"
}
}
}
Another example showing different default timestamps:
policy AuditDefaultValues {
rules {
rule LogCreationTime {
when resource.createdAt != null
then ALLOW
}
}
}
Type Methods
Every type instance has access to specific methods. Here are some key examples:
Universal Method
All object instances have:
.ToString()
- Returns a string representation
String Methods
.Length()
,.Lower()
,.Upper()
,.Trim()
.Contains()
,.StartsWith()
,.EndsWith()
.Matches()
,.Split()
Collection Methods
.Count()
,.IsEmpty()
.Contains()
,.Distinct()
.Any()
,.All()
,.Filter()
,.Map()
Specialized Type Methods
- Email:
.Domain()
,.LocalPart()
- URL:
.Host()
,.Path()
,.GetQueryParam()
- IPAddress:
.IsIPv4()
,.IsIPv6()
,.Matches(cidr)
For a complete reference of all type methods, see the Type Methods Reference.