> Markdown version of [Type Nullability](https://vaadin.com/docs/v24/hilla/reference/type-nullability). Section index: [llms.txt](https://vaadin.com/docs/v24/hilla/llms.txt)

# Type Nullability

Types that are set as *non\_nullable* are in essence required. Whereas types that are set as *nullable* are not required and thereby optional. By default, types are mapped and generated using the Java rules:

- Any primitive type, such as `int`, is non-nullable.

- Any reference type, such as `String` or `Integer`, is nullable.

- A collection accepts `null`, unless the collection item type is primitive.

- A map accepts `null`, unless the collection item type is primitive.

Any of these nullable types can be made non-nullable by applying a `@NonNull` annotation. Vaadin recommends using `@org.jspecify.annotations.NonNull`. The full list of supported annotations is as follows:

- `org.jspecify.annotations.NonNull` (Recommended)

- `org.springframework.lang.NonNull`

- `jakarta.annotation.Nonnull`

- `com.vaadin.hilla.Nonnull` (Deprecated)

## <a id="endpoint-functions"></a>Endpoint Functions

For an endpoint function, nullable elements are as follows: **Function Parameter Type** or **Function Return Type**.

For Function Parameter Types, arguments cannot be omitted, even when the parameter types are nullable. To receive a `null` parameter value in Java, send an `undefined` argument in the endpoint function call.

Original Java endpoint class

```java
import org.jspecify.annotations.NonNull;

@Endpoint
class PersonEndpoint {
    // Person must have at least the first and last name.
    public void setFullName(@NonNull String firstName, @NonNull String lastName, String middleName) {
        // omitted code
    }

    // Full name must exist.
    @NonNull
    public String getFullName() {
        // omitted code
    }

    // Person should have no connections with other people. If they have,
    // the connection cannot be null.
    public Map<String, @NonNull String> getConnections() {
        // omitted code
    }
}
```

Generated TypeScript endpoint functions

```typescript
export async function setName(
  firstName: string,
  lastName: string,
  middleName: string | undefined
) {
  return client.call('PersonEndpoint', 'setFullName', {firstName, lastName, middleName});
}

export async function getFullName(): Promise<string> {
  return client.call('PersonEndpoint', 'getFullName');
}

export async function getConnections(): Promise<Record<string, string> | undefined> {
  return client.call('PersonEndpoint', 'getConnections');
}
```

## <a id="data-class-properties"></a>Data Class Properties

Properties of data classes are nullable. Unlike the function parameters, all nullable properties can be omitted.

Original properties in Java data class

```java
public class MyBean {
    private long id;
    @NonNull
    private String value;
    private String description;
    private Map<String, String> map;
    @NonNull
    private List<String> list;
}
```

Generated properties in TypeScript data interface

```typescript
export default interface MyBean {
  id: number;
  value: string;
  description?: string;
  map?: Record<string, string | undefined>;
  list: Array<string | undefined>;
}
```

## <a id="collection-item-types"></a>Collection Item Types

The collection item type is nullable.

Original properties in Java data class

```java
public class MyBean {
    private List<String> list;
    private List<@NonNull String> nonNullableList;
    private Map<String, String> map;
    private Map<String, @NonNull String> nonNullableMap;
}
```

Generated properties in TypeScript data interface

```typescript
export default interface MyBean {
  list?: Array<string | undefined>;
  nonNullableList?: Array<string>;
  map?: Record<string, string | undefined>;
  nonNullableMap?: Record<string, string>;
}
```

## <a id="nonnullapi"></a>`@NonNullApi`

Along with `@NonNull` annotations, you could also use package-level `@NonNullApi` annotations. It would make all the nullable types in a package non-nullable by default. All nested types — `List` and `Map` items, etc. — are also affected.

By default, the following annotation is supported: `org.springframework.lang.NonNullApi`.

To make any type nullable, you must add a `@Nullable` annotation to it. Vaadin recommends using `@org.jspecify.annotations.Nullable`. The full list of supported annotations is as follows:

- `org.jspecify.annotations.Nullable` (Recommended)

- `jakarta.annotation.Nullable`

- `org.springframework.lang.Nullable`

- `com.vaadin.hilla.Nullable` (Deprecated)

`package-info.java`

```java
@NonNullApi
package com.example.application;
```

`MyBean.java`

```java
public class MyBean {
    public List<String> list;
    public Map<String, Integer> map;
    @Nullable
    public String nullable;
}
```

`MyBean.ts`

```typescript
export default interface MyBean {
  list: Array<string>;
  map: Record<string, number>;
  nullable?: string;
}
```

## <a id="kotlin-native-nullability-support"></a>Kotlin Native Nullability Support (since V24.8)

Starting with Vaadin 24.8, Hilla supports Kotlin’s native type system regarding nullability. When using Kotlin, the language’s built-in nullability markers (`?`) are automatically recognized and properly mapped to TypeScript types, eliminating the need for additional annotations.

Key characteristics of this feature:

- **Language-based nullability**: For calculating the type nullability of Kotlin source code, only the language’s typing is taken into account (e.g., `String` vs `String?`).

- **No annotation enforcement**: Java nullability annotations like `@NonNullApi`, `@NonNull`, and `@Nullable` are disregarded when processing Kotlin sources.

- **Kotlin-specific processing**: The feature exclusively affects classes from Kotlin codebases; Java entities and endpoints maintain their existing annotation-based nullability handling.

- **Mixed inheritance support**: When a Kotlin endpoint extends Java-based classes (like `CrudRepositoryService`), inherited methods retain Java processing while new Kotlin implementations use language-based nullability detection.

- **Default activation**: The underlying parser plugin activates automatically and can be disabled by excluding the `hilla-parser-jvm-plugin-nonnull-kotlin` dependency.

Kotlin data class with native nullability

```kotlin
data class Person(
    val id: Long,                    // Non-nullable by default
    val firstName: String,           // Non-nullable
    val lastName: String,            // Non-nullable
    val middleName: String?,         // Nullable
    val connections: Map<String, String>?  // Nullable map
)
```

Generated TypeScript interface

```typescript
export default interface Person {
  id: number;
  firstName: string;
  lastName: string;
  middleName?: string;
  connections?: Record<string, string>;
}
```

### <a id="maven-configuration-requirement"></a>Maven Configuration Requirement

For Kotlin’s native nullability to work correctly with Hilla, the Kotlin compiler must preserve Java method parameter names. This requires the `-java-parameters` compiler flag.

In Maven projects, you need to explicitly configure the `kotlin-maven-plugin`:

pom.xml

```xml
<plugin>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-maven-plugin</artifactId>
    <configuration>
        <args>
            <arg>-java-parameters</arg>
        </args>
    </configuration>
</plugin>
```

> **Note:** Gradle projects automatically include this configuration through the Kotlin Gradle plugin, so no additional setup is required.

### <a id="kotlin-nullability-priority"></a>Kotlin Nullability Priority

When using Kotlin, the language’s native nullability takes precedence over Java annotations. Kotlin’s type system (`String` vs `String?`) determines the nullability of generated TypeScript types, and Java annotations like `@NonNull` or `@Nullable` are ignored in Kotlin code.

Kotlin endpoint with native nullability

```kotlin
@Endpoint
class PersonEndpoint {
    // firstName and lastName are non-nullable by default in Kotlin
    fun setFullName(firstName: String, lastName: String, middleName: String?) {
        // omitted code
    }

    // Return type is non-nullable
    fun getFullName(): String {
        // omitted code
    }

    // Map is nullable, but values are non-nullable
    fun getConnections(): Map<String, String>? {
        // omitted code
    }
}
```

Generated TypeScript endpoint functions

```typescript
export async function setFullName(
  firstName: string,
  lastName: string,
  middleName: string | undefined
) { /* omitted code */ }

export async function getFullName(): Promise<string> { /* omitted code */ }

export async function getConnections(): Promise<Record<string, string> | undefined> { /* omitted code */ }
```
