From 5cc9596eeb470bda89892bdebc981355a224d56b Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 2 Jul 2026 20:31:56 +0200 Subject: [PATCH 1/2] Add more language around tools with a runtime vs no-runtime --- src/pages/learn/robust-applications.mdx | 40 ++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/pages/learn/robust-applications.mdx b/src/pages/learn/robust-applications.mdx index 1e9db98c8e..9f512bb91b 100644 --- a/src/pages/learn/robust-applications.mdx +++ b/src/pages/learn/robust-applications.mdx @@ -37,7 +37,45 @@ switch (status) { } ``` -In typed languages, if your codegen tool marks enums as exhaustive, configure it to generate a catch-all variant (often called `UNKNOWN` or `%future added value`) so the compiler enforces that you handle it. +In typed languages, some tools can generate a catch-all enum value. Those tools parse the value at runtime and map it to that catch-all value: + +```kotlin +enum class Status { + ACTIVE, + INACTIVE, + __UNKNOWN +} + +// Kotlin: exhaustive switch with catch-all value +when (status) { + ACTIVE -> showActive() + INACTIVE -> showInactive() + __UNKNOWN -> showUnknown(status) +} +``` + +Some languages use the language builtin parser (JavaScript's `JSON.parse`) and mapping to a catch-all value is not an option. + +In those cases, some tools generate a specific `%future added value` value to force the developer to handle unkonwn values. That special value is a hint that a `default:` branch is required but should not be used. Relay has a [linter rule](https://relay.dev/docs/getting-started/lint-rules/#relayno-future-added-value) to check for this. + +```ts +enum Status { + ACTIVE = "ACTIVE", + INACTIVE = "INACTIVE", + '%future added value' = "%future added value" +} + +switch (status) { + case Status.ACTIVE: + return showActive(); + case Status.Active: + return showInactive(); + // unknown value + default: + return showUnknown(status); + // note there is no need to handle '%future added value' +} +``` ## Plan for unknown union and interface subtypes From 944cf890267e6e724580614a44ebb6e498417540 Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Fri, 3 Jul 2026 10:37:20 +0200 Subject: [PATCH 2/2] wordsmithing --- src/pages/learn/robust-applications.mdx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/pages/learn/robust-applications.mdx b/src/pages/learn/robust-applications.mdx index 9f512bb91b..c249cd4fb1 100644 --- a/src/pages/learn/robust-applications.mdx +++ b/src/pages/learn/robust-applications.mdx @@ -37,7 +37,7 @@ switch (status) { } ``` -In typed languages, some tools can generate a catch-all enum value. Those tools parse the value at runtime and map it to that catch-all value: +In typed languages, some tools can generate a catch-all enum value, often called `__UNKNOWN`. Those tools parse the value at runtime and map it to that catch-all value: ```kotlin enum class Status { @@ -50,19 +50,20 @@ enum class Status { when (status) { ACTIVE -> showActive() INACTIVE -> showInactive() + // other values, such as "PENDING", are mapped to __UNKNOWN __UNKNOWN -> showUnknown(status) } ``` -Some languages use the language builtin parser (JavaScript's `JSON.parse`) and mapping to a catch-all value is not an option. +In other languages, the builtin parser is used and mapping to a catch-all value is not an option (for example, when using JavaScript's `JSON.parse`). -In those cases, some tools generate a specific `%future added value` value to force the developer to handle unkonwn values. That special value is a hint that a `default:` branch is required but should not be used. Relay has a [linter rule](https://relay.dev/docs/getting-started/lint-rules/#relayno-future-added-value) to check for this. +In those cases, codegen can generate a specific `%do not use this value, add a default case instead` value to force the developer to add a `default:` branch. That special value must be handled in a `default:` branch and not by its name, because it's a synthetic name. Relay has a [linter rule](https://relay.dev/docs/getting-started/lint-rules/#relayno-future-added-value) to check for this. ```ts enum Status { ACTIVE = "ACTIVE", INACTIVE = "INACTIVE", - '%future added value' = "%future added value" + '%do not use this value, add a default case instead' = "%do not use this value, add a default case instead" } switch (status) { @@ -73,7 +74,7 @@ switch (status) { // unknown value default: return showUnknown(status); - // note there is no need to handle '%future added value' + // note '%do not use this value, add a default case instead' must not be used here } ```