# Basic Types

> Understanding Taxi's built-in primitive types and special types that form the foundation of the type system

Source: https://taxilang.org/docs/language/basic-types

## Type Hierarchy

> **Note: Type System Foundation**
>
> Every type in Taxi inherits from `Any`, which in turn inherits from `Nothing`. This creates a complete type hierarchy that enables type-safe operations across the system.

## Core Primitive Types

### Logical
| Type    | Description                                          |
|---------|------------------------------------------------------|
| Boolean | Represents a value which is either `true` or `false` |

### Numeric
| Type    | Description                                                                      |
|---------|----------------------------------------------------------------------------------|
| Int     | A signed integer - a whole number (positive or negative), with no decimal places |
| Long    | A signed long - a whole number (positive or negative), with no decimal places    |
| Decimal | A signed decimal number - a whole number with decimal places                     |
| Double  | A double-precision 64-bit IEEE 754 floating point number                         |

### Text
| Type   | Description                |
|--------|----------------------------|
| String | A collection of characters |

### Date and Time Types

> **Note: Time Handling**
>
> Taxi provides several types for handling dates and times. When dealing with points in time, prefer `Instant` as it includes timezone information.

| Type     | Description                        | Default Format                 | Example                   |
|----------|------------------------------------|--------------------------------|---------------------------|
| Date     | A date, without time or timezone   | `yyyy-MM-dd`                   | `2024-03-15`              |
| Time     | Time only, excluding the date part | `HH:mm:ss`                     | `14:30:00`                |
| DateTime | A date and time, without timezone  | `yyyy-MM-dd'T'HH:mm:ss.SSS`    | `2024-03-15T14:30:00.000` |
| Instant  | A point in time with timezone      | `yyyy-MM-dd'T'HH:mm:ss[.SSS]X` | `2024-03-15T14:30:00Z`    |

Format Symbols:

| Symbol | Meaning              | Example     |
|--------|----------------------|-------------|
| y      | Year                 | 2024        |
| M      | Month                | 07 or July  |
| d      | Day                  | 25          |
| H      | Hour (0-23)          | 13          |
| m      | Minute               | 30          |
| s      | Second               | 45          |
| S      | Millisecond          | 678         |
| X      | Timezone (accepts Z) | Z or +01:00 |
| Z      | RFC 822 timezone     | +0100       |

Example Usage:
```taxi
// Define types with specific formats
@Format("dd/MM/yyyy")
type BirthDate inherits Date

@Format("yyyy-MM-dd HH:mm")
type AppointmentTime inherits DateTime

@Format("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
type EventTimestamp inherits Instant
```

## Collection Types

### Arrays
Arrays can be declared using either bracket notation or generic syntax:

```taxi
model Person {
  // These declarations are equivalent
  friends : Person[]
  alsoFriends : Array<Person>
}
```

Array values can be read using a get function:

Taxi's stdlib has a number of functions for filtering and reading arrays, such as:
 * [`getAtIndex()`](https://taxilang.org/docs/language/stdlib#get-at-index)
 * [`first()`](https://taxilang.org/docs/language/stdlib#first)
 * [`last()`](https://taxilang.org/docs/language/stdlib#last)

### Maps
Maps are key-value collections:

```taxi
type Inventory inherits Map<ProductId, Quantity>
```

> **Warning: Using Collections**
>
> 
>   - Use arrays for ordered collections of the same type
>   - Only use maps when you need dynamic key-value relationships
>   - Consider creating a proper model instead of a map if the structure is known
> 

## Union and Intersection types
Taxi has partial (but growing) support for union and intersection types.

Theses are most commonly found in queries, specifically relating to joining streams.

### Merging properties
For both Union Types and Intersection Types inherit all the properties of their
underlying types:

```taxi
model Person { 
  name : Name
}
model LivingThing {
   age : Age
}
type PersonOrAge = Person | Age
type PersonWithAge = Person & Age
```

In both scenarios, the resulting type contains two fields: 

 - `name : Name`
 - `age : Age`

#### Dealing with name conflicts
If both types declare a type with the same name, this results in a conflict. Conflicts are resolved as follows:

1. If both types have the same name and the same type, the resulting type will contain only a single field.
 
```taxi
model Person { 
  name : Name
}
model LivingThing {
   name : Name
}
type PersonWithAge = Person & Age // contains only name : Name
```

2. If both models have fields with the same name, but different types, the declaring model's name is prepended:

```taxi
model Tweet {
   id : TweetId inherits Int
   user : UserId
}
model User {
   id : UserId inherits Int
}

// Contains fields Tweet_id and User_id
type TweetAndUser = Tweet | User
```

3. If both models have fields with the same name, and both models also have the same name, then the declaring models fully qualified name is prepended:

```taxi
namespace foo {
  model Tweet {
     id : TweetId inherits Int
     user : UserId
  }
}

namespace bar {
  model Tweet {
     id : UserId inherits Int
  }
}
// Contains fields foo_Tweet_id and bar_Tweet_id
type TweetAndUser = Tweet | User
```

> **Note**
>
> Unlike in other languages, fields names are a lot less important in Taxi. Most mapping,
> orchestration, and transformation functions use Types, rather than field names.

### Union types
A `union type` is declared using the `|` operator, and broadly represents "This *or* That"

```taxi
// Subscribe to feeds of both Foo and Bar, and emit
// when either emit a message.
stream { Foo | Bar }
```

### Intersection types
Intersection types are declared using the `&` operator, and broadly represent "This *and* That"

```taxi
// Subscribe to feeds of both Foo and Bar, and emit
// only after both have emitted a message
stream { Foo & Bar }
```

A stream of an intersection type is generally stateful, so requires
some form of State Store provided by the TaxiQL engine. Read more about this
in State Stores.

### Limitations
Currently, Union and Intersection types are only supported as 
discovery types in a stream - as shown above.

Over time, support will expand to cover scenarios like:
 * Support for declaring top-level union / intersection types:

```taxi
// This is currently legal, but not fully supported by query engines
type Baz = Foo & Bar
type BreadRoll = Foo | Bar
```

 * Fields with Union Types (similar to OpenAPI's `anyof`)
 * `find { }` queries, where the behaviour is similar to `stream {}` queries:
   * `find { A | B }` - Load A and B, return if either produce a result
   * `find { A & B }` - Load A and B, return only if both produce a result

## Special Types

### Nothing
`Nothing` is a special type indicating that a function or expression never returns normally, typically due to throwing an exception.

As the bottom type in the type hierarchy, `Nothing` is a subtype of all other types.
This allows `Nothing` expressions to be assigned to any type, useful for handling exceptional cases.

Characteristics:
- **No Instances:** Represents the absence of a value
- **Bottom Type:** Subtype of all types, enabling flexible type assignments
- **Control Flow:** Used for functions that do not complete normally

### Any
`Any` is the root type in the Taxi type system, representing the most general type. All other types inherit from `Any`, making it the universal supertype.

Characteristics:
- **Universal Supertype:** All types inherit from `Any`
- **Extensibility:** Provides a common base for defining more specific semantic subtypes

> **Warning: Using Any**
>
> While `Any` is available, prefer defining specific semantic types that convey meaning. `Any` should primarily serve as the foundation for more specific types.

### Void
Represents the absence of a return value in operations that don't return anything.

## Type Nullability

By default, all types are non-nullable. Use the `?` operator to make a type nullable:

```taxi
model Person {
    // Required fields
    id : PersonId
    name : PersonName
    
    // Optional fields
    nickname : Nickname?
    middleName : MiddleName?
}
```

> **Note: Nullability Enforcement**
>
> Taxi defines nullability in the schema, but enforcement is handled by the implementing systems and tools.

## Enums

Enums in Taxi allow you to define a fixed set of values. Values can be explicitly provided or inferred from the enum name.

```taxi
// Basic enum - values are same as names
enum BookCategory {
    FICTION,
    NON_FICTION
}

// Enum with explicit values
enum Country {
    NEW_ZEALAND("NZ"),
    AUSTRALIA("AUS"),
    UNITED_KINGDOM("UK")
}
```

### Value Types and Inference

Taxi automatically infers the enum's base type based on its values:

```taxi
// Inferred as Int
enum Numbers {
   One(1),
   Two(2)
}

// Inferred as String when mixing types
enum Mixed {
   One("One"),
   Two(2)    // Will be converted to string
}

// Boolean values
enum Selected {
   Yes(true),
   No(false)
}

// String booleans as members
enum Flags {
   `true`,    // Note the backticks
   `false`
}
```

> **Note: Type Inference**
>
> When values are mixed (e.g., strings and numbers), Taxi defaults to treating all values as strings.

### Generic Enums with Object Bodies

Enums can hold structured data using generic type parameters:

```taxi
model ErrorDetails {
   code : ErrorCode inherits Int
   message : ErrorMessage inherits String
}

enum Errors<ErrorDetails> {
   BadRequest({ code : 400, message : 'Bad Request' }),
   Unauthorized({ code : 401, message : 'Unauthorized' })
}
```

You can access properties of enum values:

```taxi
model Response {
  // Access a property of an enum value
  errorCode: ErrorCode by Errors.BadRequest.code
  
  // Access nested properties
  errorMessage: ErrorMessage by Errors.BadRequest.message
}
```

> **Warning: Generic Constraints**
>
> - Enums support at most one type argument
> - Object values must match the specified type exactly
> - All required fields must be provided

### Lenient Matching

#### Basic Lenient Matching

```taxi
lenient enum Country {
   NZ("New Zealand"),
   AUS("Australia")
}
```

This allows:
- Case-insensitive name matching: `"nz"` matches `Country.NZ`
- Case-insensitive value matching: `"new zealand"` matches `Country.NZ`

#### Special Characters in Lenient Matching

```taxi
lenient enum DayCountConvention {
   ACT_360("ACT/360")
}
```

This matches:
- `"Act/360"`
- `"ACT/360"`
- `"act/360"`

### Default Values

Enums can specify a default value for unmatched inputs:

```taxi
enum Country {
   NZ("New Zealand"),
   AUS("Australia"),
   default UNKNOWN("Unknown")
}
```

Default values work with both names and values:
- `"UK"` resolves to `Country.UNKNOWN`
- Can be combined with lenient matching
- Only one default value is allowed per enum

### Synonyms

#### Basic Synonyms

```taxi
enum English {
   One,
   Two
}

enum French {
   Un synonym of English.One,
   Deux synonym of English.Two
}
```

#### Multiple Synonyms

```taxi
enum English { One }
enum French { Un }
enum Australian {
   One synonym of [English.One, French.Un]
}
```

> **Note: Synonym Characteristics**
>
> - Relationships are bidirectional
> - Synonyms are transitive across multiple enums
> - Can use fully qualified names
> - Can reference synonyms before their target is declared

### Array Support

Enums can be used in array literals:

```taxi
enum Country {
   NZ("NZD"),
   AU("AUD")
}

// Arrays can contain enum values
given { countries: Country[] = ["NZD", "AUD"] }

// Arrays can use enum names
given { countries: Country[] = ["NZ", "AU"] }

// Arrays can use enum references
given { countries: Country[] = [Country.NZ, Country.AU] }
```

### Enums - common Patterns

#### 1. Status Enums

```taxi
enum Status<StatusDetails> {
   ACTIVE({ code: "A", description: "Active" }),
   INACTIVE({ code: "I", description: "Inactive" }),
   default UNKNOWN({ code: "U", description: "Unknown" })
}
```

#### 2. Code Mappings

```taxi
enum ExternalMapping {
   INTERNAL_A("EXT_1") synonym of Internal.A,
   INTERNAL_B("EXT_2") synonym of Internal.B
}
```

#### 3. Validation Results

```taxi
model ValidationDetails {
   severity: Severity inherits String
   code: Code inherits Int
   message: Message inherits String
}

enum ValidationResult<ValidationDetails> {
   OK({ severity: "INFO", code: 0, message: "Valid" }),
   ERROR({ severity: "ERROR", code: 1, message: "Invalid" })
}
```
