Skip to content

Conversation

@keeganpotgieter
Copy link

@keeganpotgieter keeganpotgieter commented May 20, 2025

Description

The defineString function does not narrow the type when adding input.select.options. This means we have to typecast in certain situations, even though we know what the types should be.

Code sample

const environmentParam = params.defineString("ENVIRONMENT", { default: "production", // This will now error out if it does not match one of the options input: { select: { options: [ { label: "Production", value: "production" }, { label: "Staging", value: "staging" }, { label: "Development", value: "development" }, ], }, }, }); const environment = environmentParam.value(); // ^? "production" | "staging" | "development"

Limitations

  • If defaultValue is provided with no options the value is still wide, rather than loose autocomplete
@keeganpotgieter keeganpotgieter force-pushed the refactor/make-defineString-type-safe branch from c4788a9 to 5e88ed1 Compare June 17, 2025 01:13
runtimeValue(): string {
return process.env[this.name] || "";
runtimeValue(): T {
return process.env[this.name] as T;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when it's undefined? doesn't this cast undefined as T

* Declare a string parameter.
*
* @param name The name of the environment variable to use to load the parameter.
* @param options Configuration options for the parameter.
Copy link
Contributor

@cabljac cabljac Oct 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the JSDoc needs updating?

Maybe something like this?

 @returns A parameter whose `.value()` return type is narrowed based on the options provided. 
value: T;
}

/**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a comment here with a brief explanation of why NoInfer is needed here for future maintainers?

@cabljac
Copy link
Contributor

cabljac commented Oct 29, 2025

/gemini

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a great improvement by making defineString generic, which enhances type-safety when using select options. The use of NoInfer<T> is a good way to control type inference for the default value. However, I've found a critical issue in the implementation of StringParam.runtimeValue that could lead to runtime errors. My review includes a suggestion to fix this. Once that's addressed, this will be a solid refactoring.

Comment on lines +535 to 537
runtimeValue(): T {
return process.env[this.name] as T;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The implementation of runtimeValue has a potential issue. If process.env[this.name] is not set, it will be undefined, and undefined as T will return undefined. However, the return type T is constrained to string, so this violates the type contract and can lead to runtime errors when the value is used.

The previous implementation had a fallback to an empty string: process.env[this.name] || "". This behavior should be preserved to ensure a string is always returned, consistent with other parameter types that have zero-value fallbacks.

While returning "" might not be strictly correct if T is a union of literals that doesn't include "", it's a safer default than undefined and maintains consistency with the previous behavior.

Suggested change
runtimeValue(): T {
return process.env[this.name] as T;
}
runtimeValue(): T {
return (process.env[this.name] || "") as T;
}
@gemini-code-assist
Copy link
Contributor

It looks like you tried to invoke a command, but you didn't specify which one. Please use /gemini <command> or @gemini-code-assist <command> to specify a command. For a list of available commands, you can use /gemini help.

Copy link
Contributor

@cabljac cabljac left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! Thanks for the contribution!

I think we'd probably want to add some test cases here -

  1. Test type narrowing works using select options - perhaps just type assertions?
  2. Test backward compatibility without select options
  3. Test runtime behavior when env var contains an invalid value?
  4. Test edge case when env var is undefined (see comment on the relevant code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants