Skip to content

IsLiteral: Adding strictStringCheck option #1153

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

benzaria
Copy link
Contributor

@benzaria benzaria commented May 25, 2025

Adding strictStringCheck option to control the behavior of IsStringLiteral against wide patterns such as template literals (on${string}), or utility type results like Uppercase<string>.

  • When strictStringCheck is true, types like on${string} or Uppercase<string> are considered not string literals.
  • When set to false, those cases are considered valid literals.

Fixes #1145

… of `IsStringLiteral` returning boolean for unions and `IsNumericLiteral` returning false for numeric unions

 - Matching `IsStringLiteral` behavior to the other is*Literals when dealing with unions of different types return boolean, now return false.
 - Fixing `IsNumericLiteral` return false for numeric union like (1 | 1n), now return true.
 - Adding `strict` option to control the behavior of `IsStringLiteral` against infinite signature types.
@benzaria
Copy link
Contributor Author

@som-sm I think this is a great way to solve the problem we encounter in ExtractLiterals, i hope u review this ASAP so i can rebase the ExtractLiterals branch

@som-sm
Copy link
Collaborator

som-sm commented May 26, 2025

@benzaria Please keep the PRs focused on a single change. In this case, just add the strict option to IsLiteral and IsStringLiteral, and avoid fixing any unrelated types in the same PR.

Keeping changes atomic makes it much easier to review and provide feedback. Hope that makes sense!

@benzaria
Copy link
Contributor Author

@som-sm I want to point out that the issue u mentioned in the review for the boolean behavior getting removed from IsStringLiteral, actually its coming from LiteralCheck type which does not distribute the type giving.
But in the other hand _IsStringLiteral helper type does distribute, So this inconsistency in behavior is not reliable, at least for the moment because all other types return false on union.
So I can remove IsTrue from this line to bring the boolean back but that will only be in Strict true (uses _IsStringLiteral), if it's not the LiteralCheck type will return false anyway.

My thought on this is keeping the false for now until finding a solution for LiteralCheck type! Maybe @sindresorhus can give an opinion on this as well ?

@benzaria benzaria requested a review from som-sm May 27, 2025 13:48
@som-sm
Copy link
Collaborator

som-sm commented May 27, 2025

So I can remove IsTrue from this line to bring the boolean back but that will only be in Strict true (uses _IsStringLiteral), if it's not the LiteralCheck type will return false anyway.

Ideally, all the logic should remain in _IsStringLiteral, and it should receive the ResolvedOptions from IsStringLiteral.

export type IsStringLiteral<S, Options extends IsStringLiteralOptions = {}> =
	ApplyDefaultOptions<IsStringLiteralOptions, DefaultIsStringLiteralOptions, Options> extends infer ResolvedOptions extends Required<IsStringLiteralOptions>
		? IfNotAnyOrNever<S,
			_IsStringLiteral<CollapseLiterals<S extends TagContainer<any> ? UnwrapTagged<S> : S>, ResolvedOptions>,
			false, false>
		: never;

type _IsStringLiteral<S, Options extends Required<IsStringLiteralOptions>> =
	// If `T` is an infinite string type (e.g., `on${string}`), `Record<T, never>` produces an index signature,
	// and since `{}` extends index signatures, the result becomes `false`.
	S extends string
		? Options['strict'] extends false
			? LiteralChecks<S, string>
			: {} extends Record<S, never>
				? false
				: true
		: false;

And, this would ensure you get boolean even after using LiteralChecks, because the type would’ve been distributed before reaching LiteralChecks. So, the only change required here was to pass ResolvedOptions from IsStringLiteral to _IsStringLiteral, and then call LiteralChecks when strict is false.

@benzaria
Copy link
Contributor Author

@som-sm i did ur suggestion. and also later on we can modify LiteralChecks to do the distribution for other types.

@benzaria benzaria force-pushed the is-literal branch 2 times, most recently from 5b79acc to 663365c Compare June 1, 2025 14:34
@benzaria
Copy link
Contributor Author

benzaria commented Jun 1, 2025

@som-sm Hi, I hope u review the latest changes, and maybe take a look at #1158 as well.

Copy link
Collaborator

@som-sm som-sm left a comment

Choose a reason for hiding this comment

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

@benzaria Please address this #1153 (comment), this PR still seems to have changes that are not related to adding strict option to IsLiteral and IsStringLiteral.

@benzaria benzaria marked this pull request as draft June 5, 2025 00:31
@benzaria
Copy link
Contributor Author

benzaria commented Jun 5, 2025

This PRs will be ready to review after Merge of ExtendsStrict branch

@benzaria benzaria marked this pull request as ready for review June 6, 2025 14:03
@benzaria benzaria requested a review from som-sm June 6, 2025 14:03
@benzaria
Copy link
Contributor Author

benzaria commented Jun 6, 2025

@benzaria Please address this #1153 (comment), this PR still seems to have changes that are not related to adding strict option to IsLiteral and IsStringLiteral.

@som-sm Are u referring to the changes on IsNumericLiteral ?

Copy link
Collaborator

@som-sm som-sm left a comment

Choose a reason for hiding this comment

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

Please move Fixing unpredictable behaviors part in a separate PR.

Comment on lines 86 to 87
By default, it's Strict only matching finite literals, See {@link IsLiteralOptions.strict strict} option to change this behaviour.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Run it through AI please.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Check the new doc ! and tell me if it any good


@default true
*/
strict?: boolean;
Copy link
Collaborator

Choose a reason for hiding this comment

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

strict name is ambiguous here, since IsLiteral doesn't involve just strings.


I was thinking of referencing the IsStringLiteralOptions in IsLiteralOptions, so we'd not have to repeat ourself.

type IsStringLiteralOptions = {
	strict?: boolean;
};

/**
Supports all {@link IsStringLiteralOptions} options.
*/
type IsLiteralOptions = IsStringLiteralOptions;

In future if we add options to any other Is*Literal type, we can simply & it here and add a link, like:

/**
Supports all {@link IsStringLiteralOptions} and {@link IsNumberLiteralOptions} options.
*/
type IsLiteralOptions = IsStringLiteralOptions & IsNumberLiteralOptions;

Only downside is that we would have ensure there's no conflicting names. @sindresorhus WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That sound amazing actualy. great thinking !

Copy link
Owner

Choose a reason for hiding this comment

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

👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I Did it, and test it with and other options and worked flawlessly

Copy link
Contributor Author

@benzaria benzaria Jun 21, 2025

Choose a reason for hiding this comment

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

@sindresorhus @som-sm Hey guys what should we keep the current strictStringCheck options name as is or change it?
here are some suggestion I'm thinking of:

  • strictStringLiteral
  • strictLiteralCheck

@benzaria benzaria requested a review from som-sm June 20, 2025 13:36
@benzaria benzaria changed the title IsLiteral: Adding strict options and Fixing unpredictable behaviors… IsLiteral: Adding strictStringCheck option Jun 20, 2025
@benzaria benzaria requested a review from sindresorhus June 22, 2025 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants